Skip to content

TypeScript, React and the web

Calling models from TypeScript and getting the tokens into a browser: streaming, cancellation, sanitising, auth, rate limits and the runtime limits you will hit.

Almost every tutorial about calling a language model is written in Python, and almost every product that puts one in front of a user is written in TypeScript. The gap between those two facts is where this cluster lives. The model call itself is the easy part — one fetch, one JSON body. Everything expensive is on either side of it: keeping the key out of the browser bundle, getting a stream through a serverless function that wants to buffer it, rendering half-finished markdown without opening an injection hole, cancelling a request the user has already navigated away from, and deciding whose balance a call spends before the model ever sees it.

These pages are built from web platform primitives first — ReadableStream, AbortController, TextDecoder, Worker — because those do not change, and the framework conveniences wrapped around them do. Where a page depends on a Next.js route segment option, a Vercel plan ceiling or a Cloudflare limit, it says which version or plan the behaviour belongs to and where to check the current value, rather than printing a number that quietly goes wrong.

Every page ends with code you can paste into a project. The security pages — sanitising model output, rate limiting, and attaching a payer to a request — show the failure before the fix, because a fix you cannot see the point of is a fix that gets deleted in the next refactor.

Your First LLM Call in TypeScript

A typed model call in Node with plain fetch, an API key that never reaches the browser, and the four errors you will hit first.

9 min read

Streaming Tokens Into a React UI

Reading a ReadableStream into React state without re-rendering the whole tree on every token, with the SSE parsing that trips people up.

11 min read

A Next.js Route Handler That Calls a Model

A streaming App Router route handler, the Edge and Node runtime trade-off stated precisely, and the buffering that silently kills streaming.

11 min read

Server Actions and AI: What Fits and What Doesn't

Where a Server Action is the right home for a model call, and the exact property that forces streaming back onto a route handler.

10 min read

Cancelling an In-Flight Request From the Browser

AbortController from the button click to the upstream socket, and an honest account of which tokens you still pay for after cancelling.

10 min read

Rendering Model Markdown Without an XSS Hole

The exploit that lands when you render model markdown as HTML, why streaming makes it worse, and a sanitiser configuration that holds.

12 min read

A Chat UI That Survives 500 Messages

Windowing, sticky-bottom scroll that does not fight the user, and resuming a stream after the connection drops mid-answer.

13 min read

Storing Chat History in Postgres

A schema where messages form a tree rather than a list, edits keep their history, and every generation carries the tokens it cost.

12 min read

Zod Schemas as Output Contracts

One schema that constrains the model, validates what comes back, drives the repair prompt, and types the component that renders it.

12 min read

Calling Models From Cloudflare Workers

A streaming Worker that proxies a model, and the two limits that catch people out: subrequests, and CPU time, which is not wall-clock time.

11 min read

Deploying an AI App Without Timing Out

Why a function that works locally times out in production, what streaming does and does not fix, and the point where work belongs on a queue.

12 min read

Node Streams, Web Streams and SSE in One Model

Three streaming APIs that look interchangeable, what each is actually for, and the conversions between them that work in Node 18 and later.

12 min read

A Typed Client for Any Model Provider

Discriminated unions for messages, tool calls and errors, with an exhaustiveness check that fails the build when a new case appears.

13 min read

Sending an Image to a Model From the Browser

Resizing in the browser before upload, a signed upload that does not proxy bytes through your server, and the tile arithmetic that caps the bill.

12 min read

Running a Model in the Browser With WebGPU

What actually fits in a tab, derived from weights size and the adapter's own reported limits, with a capability probe you run on your own machine.

12 min read

Optimistic UI for AI Features

Which parts of an AI interaction can be shown before the model answers, how to roll back without losing the user's input, and what not to fake.

10 min read

Rate Limiting an AI Endpoint You Expose Publicly

Why request counts are the wrong unit for an AI endpoint, and a Redis limiter that counts cost, per user and per IP, with the atomicity that makes it hold.

13 min read

Auth for an AI Feature: Who Pays for This Call?

Resolving every request to a payer before the model sees it, and holding the money before spending it so concurrency cannot overdraw an account.

13 min read

Web Workers for Parsing and Embedding

Moving parsing, chunking, tokenising and embedding off the main thread, with transferable buffers so the handoff itself is not the bottleneck.

11 min read

Shipping an AI Feature in React Native

Streaming when fetch has no response body, what the OS does to your request when the app backgrounds, and the store rules that block AI apps at review.

12 min read

Other topics