Build / Web apps
Weather app for your phone
An installable web app with current conditions, active alerts and the next 48 hours for wherever the phone is.
TypeScriptPWAEdge function

What it does
The phone sends its location to your own endpoint. The endpoint asks the API for current conditions and a handful of forecast hours at once, and returns one JSON blob. The page draws it. Your key never touches the phone. Add it to the home screen and it behaves like an app.
- In the page, call navigator.geolocation.getCurrentPosition, then fetch your endpoint with lat and lon.
- Show the severe alerts as they come. Do not reword them. This is not an official NWS product.
API calls
Set your key once: export TFX_KEY=tfx_live_.... Free previews need no key.
Right now: conditions plus severe alerts
curl "https://data.truefixr.com/v1/data?dataset=weather&lat=30.27&lon=-97.74"
A forecast hour, 0 to 48 ahead
curl "https://data.truefixr.com/v1/data?dataset=weather&lat=30.27&lon=-97.74&hours_ahead=24"
Code (TypeScript)
Cut down on purpose. Print the real response keys on your first call before you map fields.
interface Env { TFX_KEY?: string }
const API = "https://data.truefixr.com/v1/data";
const HOURS = [0, 6, 12, 24, 48];
async function hour(lat: string, lon: string, h: number, key?: string) {
const u = new URL(API);
u.searchParams.set("dataset", "weather");
u.searchParams.set("lat", lat);
u.searchParams.set("lon", lon);
u.searchParams.set("hours_ahead", String(h));
if (key) u.searchParams.set("details", "true");
const r = await fetch(u, { headers: key ? { Authorization: `Bearer ${key}` } : {} });
if (!r.ok) throw new Error(`weather ${h}h: HTTP ${r.status}`);
return r.json();
}
export default {
async fetch(req: Request, env: Env): Promise<Response> {
const q = new URL(req.url).searchParams;
const lat = q.get("lat");
const lon = q.get("lon");
if (!lat || !lon) return new Response("lat and lon required", { status: 400 });
const rows = await Promise.all(HOURS.map((h) => hour(lat, lon, h, env.TFX_KEY)));
const body = { now: rows[0], alerts: rows[0].alerts ?? [], hourly: rows };
return new Response(JSON.stringify(body), {
headers: { "content-type": "application/json", "cache-control": "private, max-age=300" },
});
},
};