Skip to main content

Overview

The BaseCallApiConfig interface defines the configuration options for creating a new CallApi client using createFetchClient(). This configuration applies to all requests made by the client and can be either a static object or a dynamic function.

Type Definition

type BaseCallApiConfig<
  TBaseCallApiContext extends CallApiContext = DefaultCallApiContext,
  TBaseData = DefaultDataType,
  TBaseErrorData = DefaultDataType,
  TBaseResultMode extends ResultModeType = ResultModeType,
  TBaseThrowOnError extends ThrowOnErrorBoolean = DefaultThrowOnError,
  TBaseResponseType extends ResponseTypeType = ResponseTypeType,
  TBaseSchemaAndConfig extends BaseCallApiSchemaAndConfig = BaseCallApiSchemaAndConfig,
  TBasePluginArray extends CallApiPlugin[] = DefaultPluginArray,
> =
  | (CallApiRequestOptions & BaseCallApiExtraOptions)
  | ((context: InstanceContext) => CallApiRequestOptions & BaseCallApiExtraOptions)

Configuration Options

The base configuration combines request options and extra options, and can be provided in two ways:

Static Configuration

Provide a configuration object directly:
import { createFetchClient } from 'callapi';

const api = createFetchClient({
  baseURL: 'https://api.example.com',
  headers: {
    'Authorization': 'Bearer token',
  },
  timeout: 5000,
  throwOnError: true,
});

Dynamic Configuration

Provide a function that returns configuration based on context:
import { createFetchClient } from 'callapi';

const api = createFetchClient((ctx) => ({
  baseURL: 'https://api.example.com',
  headers: {
    'Content-Type': 'application/json',
    // Access instance-level headers from context
    ...ctx.request.headers,
  },
  // Conditional configuration based on request
  timeout: ctx.initURL.includes('/slow/') ? 30000 : 5000,
}));

Instance Context

When using dynamic configuration, the function receives an InstanceContext object:
context.initURL
string
required
The initial URL passed to the callApi instance
context.options
CallApiExtraOptions
required
The extra options passed to the callApi instance
context.request
CallApiRequestOptions
required
The request options passed to the callApi instance

Base-Specific Options

In addition to standard request and extra options, the base configuration includes:
plugins
CallApiPlugin[]
Array of base CallApi plugins to extend library functionality. Base plugins are applied to all instances created from this configuration.
const api = createFetchClient({
  baseURL: 'https://api.example.com',
  plugins: [loggerPlugin({ enabled: true })],
});
schema
BaseCallApiSchemaAndConfig
Base validation schemas for the client configuration. Defines validation rules that apply to all instances created from this base configuration.
import { z } from 'zod';

const api = createFetchClient({
  baseURL: 'https://api.example.com',
  schema: {
    routes: {
      '/users': {
        response: z.object({
          id: z.string(),
          name: z.string(),
        }),
      },
    },
  },
});
skipAutoMergeFor
'all' | 'options' | 'request'
Controls which configuration parts skip automatic merging between base and instance configs.
  • “all”: Disables automatic merging for both request options and extra options
  • “options”: Disables automatic merging of extra options only (hooks, plugins, etc.)
  • “request”: Disables automatic merging of request options only (headers, body, etc.)
// Skip all automatic merging - full manual control
const client = createFetchClient((ctx) => ({
  skipAutoMergeFor: 'all',
  baseURL: ctx.options.baseURL, // Keep base URL
  timeout: 5000, // Override timeout
  headers: {
    ...ctx.request.headers, // Merge headers manually
    'X-Custom': 'value',
  },
}));

// Skip options merging - manual plugin/hook control
const client = createFetchClient((ctx) => ({
  skipAutoMergeFor: 'options',
  plugins: [
    ...ctx.options.plugins?.filter(p => p.name !== 'unwanted') || [],
    customPlugin,
  ],
  method: 'POST', // Request options still auto-merge
}));

Examples

Basic API Client

const api = createFetchClient({
  baseURL: 'https://api.example.com',
  headers: {
    'Content-Type': 'application/json',
  },
  timeout: 10000,
});

// All requests inherit base configuration
const users = await api('/users');
const posts = await api('/posts');

Authenticated API Client

const api = createFetchClient({
  baseURL: 'https://api.example.com',
  auth: 'Bearer your-token',
  throwOnError: true,
});

try {
  const data = await api('/protected/resource');
  console.log('Success:', data);
} catch (error) {
  console.error('Request failed:', error);
}

Dynamic Configuration with Context

const api = createFetchClient((ctx) => {
  // Access the incoming request details
  const isUpload = ctx.request.body instanceof FormData;
  
  return {
    baseURL: 'https://api.example.com',
    timeout: isUpload ? 60000 : 5000, // Longer timeout for uploads
    headers: {
      // Add custom header based on URL
      'X-Request-Type': ctx.initURL.includes('/admin/') ? 'admin' : 'user',
    },
  };
});

With Plugins and Schemas

import { z } from 'zod';

const api = createFetchClient({
  baseURL: 'https://api.example.com',
  plugins: [
    loggerPlugin(),
    retryPlugin({ maxRetries: 3 }),
  ],
  schema: {
    routes: {
      '/users': {
        response: z.array(z.object({
          id: z.string(),
          name: z.string(),
          email: z.string().email(),
        })),
      },
    },
  },
  onResponseError: ({ response, error }) => {
    console.error(`Request failed: ${response.status}`, error);
  },
});

See Also

Build docs developers (and LLMs) love