Skip to content
D
Documentation

Shared clients and URL defaults

how-to
2 min readUpdated

Share clients and URL defaults

Use a reusable ky instance when several requests share an API origin, path prefix, headers, or request options, then override them per request or replace them for a derived client.

When to use it

Create one client for a service boundary, then use that client for requests to the service. ky.create() gives the instance a new set of defaults. ky.extend() creates a child instance that inherits the parent's defaults and changes selected values.

Create a client

Install Ky first:

bash
npm install ky

The exported KyInstance type describes the reusable client. Create the client at a module boundary that your request code can import:

ts
import ky, {type KyInstance} from 'ky';

export const api: KyInstance = ky.create({
	baseUrl: 'https://api.example.com/',
	prefix: 'v1/',
	headers: {
		accept: 'application/json',
		'x-client': 'dashboard',
	},
	retry: 2,
	timeout: 10_000,
});

The client resolves a relative input such as users against the base URL after applying the prefix, so this example targets the service's v1/users path. The default headers and retry and timeout settings apply to requests made through api.

Override defaults for one request

Pass request options to the instance method. A request-level baseUrl, prefix, header, or retry value takes precedence for that request; wrap headers with replaceOption when that request must replace the shared headers:

ts
import {replaceOption} from 'ky';
import {api} from './api.js';

interface User {
	id: number;
	name: string;
}

async function loadUsers(): Promise<void> {
	const users = await api.get<User[]>('users', {
		headers: replaceOption({
			accept: 'application/json',
			'x-request-id': 'dashboard-users',
		}),
		retry: 0,
	}).json();

	const healthApi = api.extend({
		headers: replaceOption({
			'x-client': 'health-check',
		}),
	});

	const health = await healthApi.get('health', {
		baseUrl: 'https://status.example.com/',
		prefix: '',
	}).text();

console.log(users, health);
}

void loadUsers();

api.get() returns a ResponsePromise, so .json<User[]>() reads and types the response body without first awaiting a separate response. The second call uses its request-level URL settings instead of the shared service path and returns the body as text.

For the URL input and resolution rules for URL, Request, baseUrl, prefix, and searchParams, see Instances and defaults. This page adds the request-level override pattern: keep shared defaults for ordinary calls, then supply different URL settings for one request.

Derive a specialized client

Use extend() when a group of requests needs a variation of the shared client. Normal extension deep-merges options: headers merge, search parameters accumulate, and hooks append. Use replaceOption when the child must replace an inherited merged value rather than combine with it:

ts
import {replaceOption, type KyInstance} from 'ky';
import {api} from './api.js';

export const usersApi: KyInstance = api.extend({
	prefix: 'users/',
	headers: replaceOption({
		accept: 'application/json',
		'x-client': 'users-service',
	}),
});

async function loadUser(): Promise<void> {
	const response = await usersApi.get('42').text();
	console.log(response);
}

void loadUser();

The child keeps the parent's baseUrl and other defaults, uses users/ as its prefix, and replaces the inherited header collection with the two headers shown. The original api remains unchanged.

These settings are fields of Options. The options that matter here are:

OptionTypeDefaultWhat it does
baseUrlstring | URL—Resolves relative inputs against a base URL. An absolute input bypasses it.
prefixstring | URL—Prepends a string or URL to a string input before URL resolution.
headersKyHeadersInit—Supplies HTTP headers; child instances merge them unless you use replaceOption.
retryRetryOptions | number—Controls retry behavior; a number supplies the retry limit.
timeoutnumber | false—Sets the per-attempt timeout in milliseconds.
jsonunknown—Sends a JSON body instead of constructing body yourself.

Pitfalls

  • Include a trailing slash in a path-bearing baseUrl, such as /api/, when page-relative inputs should extend that path rather than replace its last segment.
  • prefix only affects string inputs. A URL or Request input does not receive it; a Request also ignores baseUrl.
  • extend() does not replace merged values by default. Wrap a header, hook, search-parameter, context, or other merged option with replaceOption when the child must replace it.
  • ky for the complete instance API and URL options.
  • ky-options for the full Options type.

Was this page helpful?