Typed Fetch Client With Endpoints

Build a small HTTP client whose methods are typed against endpoint definitions, so request bodies and response shapes are checked at the call site.

TypescriptData Fetching

Code

snippet
export interface Endpoints {
  "GET /users/:id": { params: { id: string }; response: { id: string; name: string } };
  "POST /users": { body: { name: string }; response: { id: string } };
}

export function createClient(baseUrl: string) {
  async function call<
    K extends keyof Endpoints,
    E extends Endpoints[K],
  >(key: K, arg: E extends { body: infer B } ? B : E extends { params: infer P } ? P : never): Promise<E extends { response: infer R } ? R : never> {
    const [method, path] = (key as string).split(" ");
    const url = baseUrl + path.replace(/:id/, (arg as { id?: string }).id ?? "");
    const response = await fetch(url, {
      method,
      body: method === "GET" ? undefined : JSON.stringify(arg),
    });
    return (await response.json()) as never;
  }
  return { call };
}