Config

The generated client accepts a config object with options for headers, retry, timeout, lifecycle hooks, and more.

Basic

ts
import { createRpcClient } from './rpc-client';

const rpc = createRpcClient({
  baseUrl: '/api',
});

Full options

ts
const rpc = createRpcClient({
  baseUrl: '/api',

  // Custom fetch (SSR, testing)
  fetch: customFetch,

  // Static or async headers
  headers: { Authorization: 'Bearer token' },
  // or: headers: async () => ({ Authorization: await getToken() }),

  // Lifecycle hooks
  onRequest: (ctx) => {
    console.log(`→ ${ctx.procedure}`);
  },
  onResponse: (ctx) => {
    console.log(`← ${ctx.procedure} (${ctx.duration}ms)`);
  },
  onError: (ctx) => {
    if (ctx.willRetry) console.log(`Retrying ${ctx.procedure}...`);
  },

  // Retry policy
  retry: {
    attempts: 3,
    delay: (n) => Math.min(1000 * 2 ** n, 10000),
    retryOn: [408, 429, 500, 502, 503, 504],
  },

  // Timeout (ms)
  timeout: 10000,

  // Request deduplication
  dedupe: true,

  // Cancellation
  signal: controller.signal,

  // Custom serialization
  serialize: JSON.stringify,
  deserialize: JSON.parse,
});

Lifecycle hook types

onRequest fires before the fetch — you can mutate ctx.headers. onResponse fires after a successful response. onError fires on failure — check ctx.willRetry to know if the client will retry.

ts
interface RequestContext {
  procedure: string;
  method: 'GET' | 'POST';
  url: string;
  headers: Record<string, string>; // mutable
  input: unknown;
}

interface ResponseContext {
  procedure: string;
  method: 'GET' | 'POST';
  url: string;
  response: Response;
  data: unknown;
  duration: number;
}

interface ErrorContext {
  procedure: string;
  method: 'GET' | 'POST';
  url: string;
  error: unknown;
  attempt: number;
  willRetry: boolean;
}

visit GitHub to learn more about metaxy