Documentation

API Client

Configure every createMicroApi option, including headers, authHeader, fetcher, and onError.

createMicroApi

API client options

Every API starts with a client created by createMicroApi.

createMicroApi defines settings shared by every resource created from that client.

Use it to configure things like the base URL, global headers, auth headers, a custom fetcher, and global error handling.

If something should be shared by many requests, it usually belongs on the client. If it only applies to one endpoint, it usually belongs on that endpoint.

name
query key namespace.
baseUrl
root URL for endpoint paths.
headers
headers sent with every request.
tokenProvider
optional token and refresh manager.
authHeader
turns an access token into request headers.
fetcher
custom fetch implementation.
onError
observes request failures.
extend
creates another client from this one.
TypeScript
1import { createMicroApi } from "micro-rq";2 3export const api = createMicroApi({4  name: "main",5  baseUrl: "https://api.example.com",6  headers: () => ({7    "x-client": "web",8  }),9  tokenProvider,10  authHeader: (token) => ({11    Authorization: `Bearer ${token}`,12  }),13  fetcher: fetch,14  onError: (error, context) => {15    console.error(context.method, context.url, error);16  },17});

name and baseUrl

name is only used inside generated query keys. It is not sent over the network.

baseUrl is joined with each endpoint path before the request is sent.

Use different name values when your app talks to multiple backends so their query keys stay separate.

Same-origin API
use baseUrl: "/api".
External API
use baseUrl: "https://api.example.com".
Multiple APIs
use different name values.
Generated key
name is the first item in every query key.
TypeScript
1const mainApi = createMicroApi({2  name: "main",3  baseUrl: "https://api.example.com",4});5 6const billingApi = createMicroApi({7  name: "billing",8  baseUrl: "https://billing.example.com",9});10 11// mainApi users key starts with ["main", ...]12// billingApi invoices key starts with ["billing", ...]

headers

headers adds global headers to every request made by this client.

It accepts either a normal HeadersInit value or a function. The function can be async.

Request headers can come from three layers: client headers, endpoint headers, and authHeader.

Use client headers for values shared by many requests. Use endpoint headers when the value depends on endpoint variables.

Static headers
good for app version, client name, locale, or tenant.
Async headers
good when values must be read at request time.
Merge order
client headers, endpoint headers, then authHeader.
Override rule
later headers replace earlier ones.
TypeScript
1const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  headers: {5    "x-client": "dashboard",6    "x-app-version": "1.4.0",7  },8});
TypeScript
1const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  headers: async () => {5    const locale = localStorage.getItem("locale") ?? "en";6 7    return {8      "accept-language": locale,9    };10  },11});
TypeScript
1const users = api.resource("users", {2  detail: api.get<User, { id: string; traceId: string }>(3    ({ id }) => `/users/${id}`,4    {5      headers: ({ traceId }) => ({6        "x-trace-id": traceId,7      }),8    },9  ),10});

authHeader

authHeader receives the current access token and returns the headers added to authenticated requests.

Each endpoint can define authMode, which controls whether auth is used for that endpoint. It also controls whether authHeader can run.

authHeader only runs when a token exists and the endpoint authMode is not none.

Most APIs use a Bearer token, but you can return any auth header shape you need.

Key points

  • When authMode is "none", token lookup and authHeader are skipped.
  • When authMode is "required", a missing token throws MicroAuthRequiredError before fetch runs.
TypeScript
1const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  tokenProvider,5  authHeader: (token) => ({6    Authorization: `Bearer ${token}`,7  }),8});
TypeScript
1const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  tokenProvider,5  authHeader: (token) => ({6    "x-access-token": token,7  }),8});

Info

TokenProvider

We will cover tokenProvider next in the Authentication page.

fetcher

fetcher replaces the fetch implementation used by micro-rq.

By default, the package uses globalThis.fetch.

Provide fetcher when you need a mock fetch in tests, when your environment has no global fetch, or when you want to wrap fetch with custom behavior.

If only some endpoints need a custom fetcher, create another client with api.extend() instead of repeating the whole config.

Tests
pass a mock fetcher.
Node environments
pass a fetch implementation when globalThis.fetch is unavailable.
Instrumentation
wrap fetch to measure request duration.
Session cookies
use a custom fetcher with credentials: "include".

Key points

  • Do not use fetcher for endpoint-specific headers or bodies.
TypeScript
1const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  fetcher: async (input, init) => {5    const startedAt = performance.now();6 7    try {8      return await fetch(input, init);9    } finally {10      console.log("request took", performance.now() - startedAt);11    }12  },13});
TypeScript
1const mockFetcher: typeof fetch = async () =>2  new Response(JSON.stringify({ id: "user-1", name: "Jane" }), {3    status: 200,4    headers: {5      "content-type": "application/json",6    },7  });8 9const testApi = createMicroApi({10  name: "test",11  baseUrl: "https://example.test",12  fetcher: mockFetcher,13});
TypeScript
1const sessionApi = createMicroApi({2  name: "session",3  baseUrl: "/api",4  fetcher: (input, init) =>5    fetch(input, {6      ...init,7      credentials: "include",8    }),9});
TypeScript
1const uploadApi = api.extend({2  name: "uploads",3  fetcher: createUploadFetcher("avatar"),4});

extend()

extend() creates another API client from the current one.

Use it when most settings stay the same, but a few values need to change.

This is especially useful for upload clients that share the same auth and base URL, but use a different fetcher.

Inherited
any config you do not override.
Overridden
values passed to extend() replace the base config.
Recommended
change name if the new client should have a separate query-key namespace.
Common use
api.extend({ name: "uploads", fetcher: uploadFetcher }).
TypeScript
1export const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  tokenProvider,5  authHeader: (token) => ({6    Authorization: `Bearer ${token}`,7  }),8  onError,9});10 11export const uploadApi = api.extend({12  name: "uploads",13  fetcher: createUploadFetcher("avatar"),14});

onError

onError is a global error observer for this API client.

It is called for failed HTTP responses, missing required auth, refresh failures, network failures, and response parsing failures.

It does not replace the original error. The package calls onError and then still throws the original error to TanStack Query or your direct caller.

Logging
send error and context to your logger.
Telemetry
record context.method, context.url, and context.authMode.
Auth cleanup
detect auth errors and clear app state if needed.
UI handling
keep user-facing messages in TanStack Query onError or component state.
TypeScript
1const api = createMicroApi({2  name: "main",3  baseUrl: "/api",4  onError: (error, context) => {5    reportApiError({6      error,7      method: context.method,8      path: context.path,9      url: context.url,10      authMode: context.authMode,11    });12  },13});
TypeScript
1const userQuery = useQuery({2  ...users.detail.toQuery(userId),3  onError: (error) => {4    // Component-level UI behavior still belongs here.5    toast.error("Could not load user");6  },7});