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:
bashnpm install ky
The exported KyInstance type describes the reusable client. Create the client at a module boundary that your request code can import:
tsimport 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:
tsimport {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:
tsimport {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:
| Option | Type | Default | What it does |
|---|---|---|---|
baseUrl | string | URL | — | Resolves relative inputs against a base URL. An absolute input bypasses it. |
prefix | string | URL | — | Prepends a string or URL to a string input before URL resolution. |
headers | KyHeadersInit | — | Supplies HTTP headers; child instances merge them unless you use replaceOption. |
retry | RetryOptions | number | — | Controls retry behavior; a number supplies the retry limit. |
timeout | number | false | — | Sets the per-attempt timeout in milliseconds. |
json | unknown | — | 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. prefixonly affects string inputs. AURLorRequestinput does not receive it; aRequestalso ignoresbaseUrl.extend()does not replace merged values by default. Wrap a header, hook, search-parameter, context, or other merged option withreplaceOptionwhen the child must replace it.
Related
kyfor the complete instance API and URL options.ky-optionsfor the fullOptionstype.
Was this page helpful?