RizTech Academy logo
RizTech Academy
Capstone: A Typed Library and ClientLesson 1 of 520 min

The brief: a typed weather API client

You have learnt the whole type system — primitives to conditional types, narrowing to generics, async to validation. This capstone is where it stops being a list of features and becomes a way of building. Over five lessons you will build one real thing, end to end, applying the course's judgement at every step: a typed weather client that fetches live weather for any location and turns an untrusted JSON response into a value the rest of your program can trust completely.

What we are building

A small library — no framework, no UI, just typed TypeScript — with one job: given a latitude and longitude, return the current weather as a clean, fully-typed value, and handle every way that can go wrong without ever crashing. Concretely, this function:

getWeather(19.07, 72.87, fetch)   // Mumbai
// → { ok: true, value: { temperatureC: 27.1, windSpeedKmh: 0.4, condition: "cloudy" } }

or, when something fails:

// → { ok: false, error: { kind: "http", status: 503 } }

It is deliberately small enough to build properly in five lessons, and deliberately real: it calls a live public API, deals with genuine untrusted data, and models the failures that actually happen. When you finish, you will have written the kind of typed boundary that sits at the edge of every professional codebase.

The API: Open-Meteo (free, no key)

We use Open-Meteo — a free weather API that needs no sign-up and no API key, so you can run every line of this capstone yourself. The endpoint we call:

https://api.open-meteo.com/v1/forecast?latitude=19.07&longitude=72.87&current=temperature_2m,wind_speed_10m,weather_code

A real response looks like this (trimmed to what we use):

{
  "current": {
    "time": "2026-09-27T21:15",
    "temperature_2m": 27.1,
    "wind_speed_10m": 0.4,
    "weather_code": 1
  }
}

Three fields matter to us: temperature_2m (°C), wind_speed_10m (km/h), and weather_code — a WMO numeric code where 0 is clear, 61 is rain, 95 is thunderstorm, and so on. Part of our job is turning that opaque number into a meaningful condition. Try the URL in your browser now; seeing the real shape is the first step.

The requirements — the standard the capstone is built to

This is not "make it work". It is "build it the way the best-practices and real-projects modules said to". The requirements encode that:

  1. The public value is fully typed and trustworthy. Callers get a Weather value with a numeric temperature, a numeric wind speed, and a condition drawn from a small closed set — never a raw weather_code, never any, never a possibly-missing field.
  2. The boundary is validated, not asserted. The JSON from Open-Meteo is untrusted (the typing-apis lesson): it arrives as unknown and is checked before we believe it — no as ApiResponse.
  3. Every failure is modelled, never thrown into the void. Network failure, an error HTTP status, and a malformed body are three distinct, typed outcomes the caller can handle — using the Result pattern, so a failure is a value, not an exception that a caller might forget to catch.
  4. Illegal states are unrepresentable. You cannot construct a Weather with a condition outside the known set, and you cannot read value on a failed result — the types forbid both.
  5. It is testable without the network. fetch is injected, so the whole client can be exercised against fake responses — success, each failure — with no live call. This is dependency injection from the design-patterns module, earning its place.

Every one of these is a decision the course argued for. The capstone is where you see them combine into something that could not have been built as safely in plain JavaScript.

How the five lessons go

We build in the order a careful engineer actually works — types first, then implementation, then hardening, then tests:

  • Design (next lesson): model the domain in types before writing any logic — the Weather value, the Condition set, the Result type, the typed WeatherError. Make the illegal states impossible on paper first.
  • Build: implement the client — the request, validating the untrusted response into our domain model, mapping the WMO code to a condition. Get the happy path working against the real API.
  • Hardening: handle every failure exhaustively, render each outcome, and use the compiler's exhaustiveness check so a new error kind cannot be silently ignored.
  • Testing: exercise the whole thing against injected fake responses — every success and failure path — plus type-level tests that assert the types themselves are what we intend.

Design first is not ceremony. In a typed language, the types are the design — get them right and the implementation often writes itself, because the compiler rejects everything that does not fit. That is the lesson the whole capstone is meant to land.

Before you start

Set up a scratch project you will build across all five lessons:

mkdir weather-client && cd weather-client
npm init -y
npm install --save-dev typescript tsx
npx tsc --init --strict

Set "target": "ES2022" and "lib": ["ES2022", "DOM"] in the generated tsconfig.json (we use fetch and promises — the tsconfig lesson explains why DOM and a modern target are needed). You will run files with npx tsx <file>.ts and type-check with npx tsc --noEmit, exactly as throughout the course. Confirm the setup works with a one-line console.log("ready") before moving on.

Check your work

What we are building. A typed weather client: latitude/longitude in, a fully-typed Weather value out, every failure handled — a real typed boundary, no framework.

The API. Open-Meteo — free, no key — returning current.temperature_2m, wind_speed_10m, and a WMO weather_code we map to a condition.

The five requirements. Fully-typed trustworthy value; validated (not asserted) boundary; every failure modelled as a typed Result; illegal states unrepresentable; testable via injected fetch.

Why design-first. In a typed language the types are the design — getting them right makes the implementation fall out and the compiler reject what does not fit.

The build order. Design → build → hardening → testing.

Practice

  1. Open the Open-Meteo URL in your browser for your own city's coordinates and read the real current object. Note which fields we use and which we ignore.
  2. Look up three WMO weather codes (try 0, 61, 95) and decide what condition each should map to.
  3. Set up the weather-client project above and confirm npx tsx runs a one-liner.
  4. Before reading the next lesson, sketch on paper the types you think this needs: what does Weather look like? What are the possible failures? You will compare your sketch to the designed version next.
  5. Write down, for each of the five requirements, which course module it came from — a quick map of what the capstone is exercising.

Official documentation

Next: designing the domain in types, before a line of logic.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship