Lifecycle Hooks

Three hooks let you intercept the request lifecycle: before the fetch, after a successful response, and on failure.

Overview

ts
const rpc = createRpcClient({
  baseUrl: '/api',
  onRequest:  (ctx) => { /* before fetch */ },
  onResponse: (ctx) => { /* after success */ },
  onError:    (ctx) => { /* on failure */ },
});

// Execution order:
// 1. onRequest  — mutate headers, log
// 2. fetch      — actual HTTP call
// 3. onResponse — if success
//    onError    — if failure (may repeat if retry is configured)

onRequest

Fires before the fetch. You can mutate ctx.headers to add or override headers dynamically. Runs again on every retry attempt.

ts
const rpc = createRpcClient({
  baseUrl: '/api',
  onRequest: (ctx) => {
    console.log(`→ ${ctx.method} ${ctx.procedure}`);
    // Mutate headers before the request is sent
    ctx.headers['X-Request-Id'] = crypto.randomUUID();
  },
});
ts
interface RequestContext {
  procedure: string;               // e.g. "get_user"
  method: 'GET' | 'POST';         // GET for queries, POST for mutations
  url: string;                     // full request URL
  headers: Record<string, string>; // mutable — add/modify headers here
  input: unknown;                  // the serialized input
}

onResponse

Fires after a successful response. Use it for logging, metrics, or cache warming.

ts
const rpc = createRpcClient({
  baseUrl: '/api',
  onResponse: (ctx) => {
    console.log(`← ${ctx.procedure} ${ctx.response.status} (${ctx.duration}ms)`);
  },
});
ts
interface ResponseContext {
  procedure: string;
  method: 'GET' | 'POST';
  url: string;
  response: Response;   // the raw fetch Response
  data: unknown;        // parsed response body
  duration: number;     // request duration in ms
}

onError

Fires on failure. Check ctx.willRetry to know if the client will retry, or use ctx.attempt to track retry progress.

ts
const rpc = createRpcClient({
  baseUrl: '/api',
  onError: (ctx) => {
    console.error(`✗ ${ctx.procedure} attempt ${ctx.attempt}`, ctx.error);
    if (ctx.willRetry) {
      console.log('Retrying...');
    } else {
      reportToSentry(ctx.error);
    }
  },
});
ts
interface ErrorContext {
  procedure: string;
  method: 'GET' | 'POST';
  url: string;
  error: unknown;       // the thrown error
  attempt: number;      // current attempt (1 = first try)
  willRetry: boolean;   // true if retry policy will retry
}

Try it

Calls an endpoint and logs every lifecycle hook as it fires.

visit GitHub to learn more about metaxy