Browser Support
Using typed-fetch in browser and mixed client/server applications
Browser Support
typed-fetch works seamlessly in modern browsers. When running in a browser, observations are automatically sent to the local typed-fetch watch server — no extra configuration needed.
Browser Requirements
- Modern browser with native
fetchsupport - Works in all modern browsers (Chrome, Firefox, Safari, Edge)
- Node.js 18+ for server-side usage
Basic Browser Usage
import { typedFetch } from '@phumudzo/typed-fetch';
const result = await typedFetch(
'https://api.example.com/user/123',
{ method: 'GET' },
{ endpointKey: 'GET /user/:id' }
);
if (result.status === 200) {
console.log(result.data);
}No special browser setup needed — typedFetch works the same way in Node.js and the browser.
How Observations Work in the Browser
When observerMode is "auto" (the default), typed-fetch detects its runtime environment:
- Node.js → writes observations directly to the registry file
- Browser → POSTs observations to the local HTTP observer server at
http://localhost:{observerPort}/__typed-fetch/observe
The observer server is started automatically by typed-fetch watch. From the browser's perspective, calling typedFetch() is all that's needed — observation and type generation happen in the background.
Getting Started with a Browser App
# 1. Initialize (auto-detects generatedPath from your tsconfig)
npx typed-fetch init
# 2. Start the watch server
npx typed-fetch watchWatching .typed-fetch/registry.json
Observer listening on http://localhost:7779
Press Ctrl+C to stop.# 3. Run your app in another terminal
npm run devMake API calls in the browser. Types are regenerated automatically after each call.
Works with Any Framework
Because the observation transport is plain HTTP POST, it works with any dev setup — no plugins or framework-specific configuration required.
| Framework | Setup |
|---|---|
| Vite (React, Vue, Svelte) | typed-fetch init + typed-fetch watch |
| Create React App | typed-fetch init + typed-fetch watch |
| Next.js | typed-fetch init + typed-fetch watch |
| Plain HTML / no bundler | typed-fetch init + typed-fetch watch |
Mixed Client/Server Applications
Both browser and Node.js calls flow through the same typed-fetch watch process:
- Node.js calls → write directly to the registry file → watcher detects change → types regenerate
- Browser calls → POST to observer server → observer writes to registry → watcher detects change → types regenerate
The registry merges shapes from both sources automatically, so your generated types reflect all observed responses regardless of where they originated.
Typical Workflow
┌─────────────────────────────────────────────────────────┐
│ Terminal 1: typed-fetch watch │
│ $ npx typed-fetch watch │
│ Watching .typed-fetch/registry.json │
│ Observer listening on http://localhost:7779 │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Terminal 2: Development server │
│ $ npm run dev │
│ Browser + server both making API calls... │
└─────────────────────────────────────────────────────────┘
↓ types regenerate automatically ↓
┌─────────────────────────────────────────────────────────┐
│ src/generated/typed-fetch.d.ts updated │
└─────────────────────────────────────────────────────────┘React Integration Example
import { typedFetch } from '@phumudzo/typed-fetch';
import { useEffect, useState } from 'react';
export function UserProfile({ userId }: { userId: number }) {
const [user, setUser] = useState<{ id: number; name: string; email: string } | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchUser = async () => {
const result = await typedFetch(
`/api/user/${userId}`,
{ method: 'GET' },
{ endpointKey: 'GET /user/:id' }
);
if (result.status === 200) {
setUser(result.data); // typed once types are generated
} else if (result.status === 404) {
setError('User not found');
} else {
setError(`Error: ${result.status}`);
}
setLoading(false);
};
fetchUser();
}, [userId]);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!user) return null;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}Custom Observer Port
If port 7779 conflicts with another service, change it in typed-fetch.config.json:
{
"observerPort": 8888
}The watch server and browser runtime both read this value, so changing it in one place is all that's needed.
CORS
The observer server runs on a different port to your dev server. typed-fetch handles this automatically — the watch server sets Access-Control-Allow-Origin: * on all responses so browser POSTs are never blocked by CORS.
Disabling Observation in Browser
To prevent observation entirely (e.g., in production):
{
"observerMode": "none"
}With this setting:
typedFetchstill works normally- No observations are recorded or sent
- Already-generated types continue to work
Next: API Reference
Learn about the TypeScript Types and Result Object.