Client

Overview

Create an AccorudoClient and make typed HTTP requests.

accorudo/client turns a route registry into a typed HTTP client backed by fetch (and XMLHttpRequest for upload progress). Unlike accorudo/core, it ships runtime code.

Creating a client

AccorudoClient is generic over your route registry (see Registering routes):

import { AccorudoClient } from "accorudo/client";
import type { MainRoutes } from "./api";

export const mainApi = new AccorudoClient<MainRoutes>("https://api.example.com");

req

Makes a request and resolves with the parsed JSON response. TypeScript infers pathParams, query, body, headers, and the response type from the (method, path) pair:

const widgets = await mainApi.req("get", "/widget", {
  query: { limit: 20, offset: 0 },
});

const widget = await mainApi.req("get", "/widget/:id", {
  pathParams: { id: "abc-123" },
});

await mainApi.req("post", "/widget", {
  body: { name: "My widget", enabled: true },
});

config also accepts keepalive and signal (AbortSignal).

withProgress

Same signature as req, plus an onProgress callback (0–1) reporting upload progress. Built on XMLHttpRequest since fetch cannot report upload progress. Prefer it for file uploads — see Multipart routes:

await mainApi.withProgress(
  "post",
  "/widget/:id/media",
  {
    pathParams: { id: widgetId },
    body: { file },
    type: RouteType.Multipart,
  },
  (progress) => {
    uploadProgress.value = progress;
  }
);

Error handling

Both methods reject with ApiError (an Error subclass carrying status) on a non-2xx response, or on an aborted withProgress call (status: 0):

import { isApiError } from "accorudo/client";

try {
  await mainApi.req("get", "/widget/:id", { pathParams: { id } });
} catch (error) {
  if (isApiError(error) && error.status === 404) {
    // handle not found
  }
  throw error;
}

Next steps

Both req and withProgress run any registered interceptors before issuing the request — useful for logging, or built-ins like rate limiting.

Copyright © 2026