Skip to main content
The Fetch client is built on the native Fetch API and works in all modern browsers and Node.js environments. It’s the default client for OpenAPI TypeScript with zero dependencies.

Installation

The Fetch client is included in the main @hey-api/openapi-ts package. To use it standalone:
npm install @hey-api/client-fetch

Basic Usage

1

Create a client instance

import { createClient } from '@hey-api/client-fetch';

const client = createClient({
  baseUrl: 'https://api.example.com',
});
2

Make API calls

// GET request
const { data, error } = await client.get({
  url: '/users/{id}',
  path: { id: 123 },
});

// POST request
const { data, error } = await client.post({
  url: '/users',
  body: {
    name: 'John Doe',
    email: '[email protected]',
  },
});
3

Handle responses

if (error) {
  console.error('Error:', error);
} else {
  console.log('User:', data);
}

Configuration

The Fetch client supports comprehensive configuration options:

Client Options

import { createClient } from '@hey-api/client-fetch';

const client = createClient({
  // Base URL for all requests
  baseUrl: 'https://api.example.com',

  // Default headers
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'your-api-key',
  },

  // Custom fetch implementation
  fetch: customFetch,

  // Response parsing format
  parseAs: 'json', // 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' | 'stream' | 'auto'

  // Response style
  responseStyle: 'fields', // 'fields' | 'data'

  // Error handling
  throwOnError: false,

  // Authentication
  auth: async (auth) => getToken(),

  // Body serialization
  bodySerializer: JSON.stringify,

  // Query serialization
  querySerializer: (query) => new URLSearchParams(query).toString(),

  // Request validator
  requestValidator: async (data) => validateRequest(data),

  // Response transformer
  responseTransformer: async (data) => transformResponse(data),

  // Response validator
  responseValidator: async (data) => validateResponse(data),

  // Standard fetch options
  credentials: 'include',
  mode: 'cors',
  cache: 'default',
  redirect: 'follow',
  referrer: 'client',
  referrerPolicy: 'no-referrer',
  integrity: '',
  keepalive: false,
  signal: abortController.signal,
});

Runtime Configuration

Update client configuration after creation:
// Get current config
const config = client.getConfig();

// Update config
client.setConfig({
  headers: {
    'Authorization': 'Bearer new-token',
  },
});

HTTP Methods

The client provides methods for all HTTP verbs:
const { data } = await client.get({
  url: '/users',
  query: {
    page: 1,
    limit: 10,
  },
});

Authentication

The Fetch client supports multiple authentication schemes:

Bearer Token

const client = createClient({
  baseUrl: 'https://api.example.com',
  auth: 'your-token', // Simple token
});

// Or with a function
const client = createClient({
  baseUrl: 'https://api.example.com',
  auth: async (auth) => {
    if (auth.scheme === 'bearer') {
      return await getAccessToken();
    }
  },
});

Basic Authentication

const client = createClient({
  baseUrl: 'https://api.example.com',
  auth: async (auth) => {
    if (auth.scheme === 'basic') {
      // Return base64 encoded credentials
      return btoa('username:password');
    }
  },
});

API Key

const client = createClient({
  baseUrl: 'https://api.example.com',
  auth: async (auth) => {
    if (auth.type === 'apiKey') {
      return 'your-api-key';
    }
  },
});

// Per-request authentication
const { data } = await client.get({
  url: '/users',
  security: [
    {
      type: 'apiKey',
      in: 'header',
      name: 'X-API-Key',
    },
  ],
});

Interceptors

Add request, response, and error interceptors:

Request Interceptor

client.interceptors.request.use((request, options) => {
  // Modify request before sending
  console.log('Sending request to:', request.url);
  
  // Add custom header
  request.headers.set('X-Request-ID', generateRequestId());
  
  return request;
});

Response Interceptor

client.interceptors.response.use((response, request, options) => {
  // Process response
  console.log('Received response:', response.status);
  
  // Log response time
  const responseTime = response.headers.get('X-Response-Time');
  console.log('Response time:', responseTime);
  
  return response;
});

Error Interceptor

client.interceptors.error.use((error, response, request, options) => {
  // Handle errors
  console.error('Request failed:', error);
  
  // Transform error
  if (response?.status === 401) {
    return { message: 'Unauthorized', code: 'AUTH_ERROR' };
  }
  
  return error;
});

Response Parsing

Control how responses are parsed:

Auto (Default)

const { data } = await client.get({
  url: '/users',
  parseAs: 'auto', // Infers from Content-Type header
});

JSON

const { data } = await client.get({
  url: '/users',
  parseAs: 'json',
});

Text

const { data } = await client.get({
  url: '/document',
  parseAs: 'text',
});

Blob

const { data } = await client.get({
  url: '/image.png',
  parseAs: 'blob',
});

Stream

const { data } = await client.get({
  url: '/large-file',
  parseAs: 'stream',
});

// data is ReadableStream
const reader = data.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log('Chunk:', value);
}

Server-Sent Events

Stream real-time data with Server-Sent Events:
const stream = await client.sse.get({
  url: '/events',
  onSseEvent: (event) => {
    console.log('Event:', event.data);
  },
  onSseError: (error) => {
    console.error('Stream error:', error);
  },
  sseMaxRetryAttempts: 3,
  sseMaxRetryDelay: 5000,
  sseDefaultRetryDelay: 1000,
});

// Close the stream
stream.close();

Body Serialization

Customize how request bodies are serialized:

JSON (Default)

import { jsonBodySerializer } from '@hey-api/client-fetch';

const client = createClient({
  bodySerializer: jsonBodySerializer.bodySerializer,
});

FormData

import { formDataBodySerializer } from '@hey-api/client-fetch';

const client = createClient({
  bodySerializer: formDataBodySerializer.bodySerializer,
});

const { data } = await client.post({
  url: '/upload',
  body: {
    file: fileBlob,
    name: 'document.pdf',
  },
});

URL Encoded

import { urlSearchParamsBodySerializer } from '@hey-api/client-fetch';

const client = createClient({
  bodySerializer: urlSearchParamsBodySerializer.bodySerializer,
});

Query Serialization

Customize query parameter serialization:
const client = createClient({
  querySerializer: {
    // Array serialization
    array: {
      style: 'form', // 'form' | 'spaceDelimited' | 'pipeDelimited'
      explode: true,
    },
    // Object serialization
    object: {
      style: 'deepObject', // 'form' | 'deepObject'
      explode: true,
    },
    // Allow reserved characters
    allowReserved: false,
  },
});

Advanced Examples

Custom Fetch Implementation

import { createClient } from '@hey-api/client-fetch';
import nodeFetch from 'node-fetch';

const client = createClient({
  baseUrl: 'https://api.example.com',
  fetch: nodeFetch as any,
});

Request Cancellation

const controller = new AbortController();

const promise = client.get({
  url: '/users',
  signal: controller.signal,
});

// Cancel the request
controller.abort();

Response Validation

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
});

const client = createClient({
  baseUrl: 'https://api.example.com',
  responseValidator: async (data) => {
    UserSchema.parse(data);
  },
});

Response Transformation

const client = createClient({
  baseUrl: 'https://api.example.com',
  responseTransformer: async (data) => {
    // Transform ISO date strings to Date objects
    if (data.createdAt) {
      data.createdAt = new Date(data.createdAt);
    }
    return data;
  },
});

TypeScript Types

Import client types for advanced usage:
import type {
  Client,
  Config,
  RequestOptions,
  RequestResult,
  ResponseStyle,
} from '@hey-api/client-fetch';

// Custom client wrapper
function createApiClient(config: Config): Client {
  return createClient({
    ...config,
    headers: {
      'X-Custom-Header': 'value',
      ...config.headers,
    },
  });
}

Next Steps

Axios Client

Advanced features with Axios

Authentication

Set up API authentication

Interceptors

Add request/response interceptors

Error Handling

Handle errors effectively

Build docs developers (and LLMs) love