Skip to content

Response Transformation

HTTP Client Toolkit provides a multi-stage response pipeline. After the raw Response is received, it passes through an optional response interceptor, then the body is parsed and flows through transform then validate stages. This lets you inspect raw responses, normalize API data, and enforce response shapes before they reach your application code.

After a successful HTTP response, data flows through:

  1. responseInterceptor — Inspect or modify the raw Response object (headers, status, body stream). See the Interceptors guide
  2. Parse — Response body is read and parsed (JSON or text)
  3. responseTransformer — Normalize parsed response data (e.g. convert keys to camelCase)
  4. responseHandler — Validate or unwrap the transformed data

All stages are optional. If none are provided, the raw parsed JSON response is returned.

Use responseTransformer to normalize API data before it reaches your application:

import camelcaseKeys from 'camelcase-keys';
const client = new HttpClient(stores, {
responseTransformer: (data) => camelcaseKeys(data as Record<string, unknown>, { deep: true }),
});
// API returns: { "user_name": "Alice", "created_at": "2024-01-01" }
// You receive: { userName: "Alice", createdAt: "2024-01-01" }

Use responseHandler to validate the shape of the response, unwrap nested data, or throw on unexpected formats:

const client = new HttpClient(stores, {
responseHandler: (data) => {
if (!data || typeof data !== 'object') {
throw new Error('Unexpected response shape');
}
return data;
},
});
import camelcaseKeys from 'camelcase-keys';
import { z } from 'zod';
const client = new HttpClient(stores, {
// First: normalize keys
responseTransformer: (data) =>
camelcaseKeys(data as Record<string, unknown>, { deep: true }),
// Then: validate shape
responseHandler: (data) => {
const schema = z.object({
userName: z.string(),
createdAt: z.string(),
});
return schema.parse(data);
},
});

The response pipeline runs before the result is cached. This means cached entries already contain the transformed and validated data — subsequent cache hits return the same processed result without re-running the pipeline.

Use errorHandler to convert HTTP errors (non-2xx responses) into domain-specific types. Note that errorHandler is only called for HTTP errors — errors thrown by responseTransformer or responseHandler (e.g. validation failures) are wrapped in HttpClientError directly.

const client = new HttpClient(stores, {
responseTransformer: (data) => transform(data),
responseHandler: (data) => validate(data),
errorHandler: (context) => {
// Only called for non-2xx HTTP responses
return new ApiError(context.message, context.response.status);
},
});