Skip to content
D
Documentation

Resolve request URLs

how-to
3 min readUpdated

Use ky to resolve endpoint paths against an API base, prepend a path when leading slashes must append, and merge query parameters into the outgoing URL. Ky builds on Fetch: you pass a string, URL, or Request and receive a response.

The samples run in a browser or Node.js with native Fetch support. They use absolute API addresses so URL construction also works on the server. Replace https://api.example.com with your API's origin; inspect the outgoing URLs in the console rather than relying on the service to echo them.

1. Resolve paths with baseUrl

Use baseUrl for standard URL resolution. Give a base path a trailing slash and pass a page-relative input such as 'users' to extend that path. Create an instance with the base as a default, then make requests through it:

ts
import ky from 'ky';

const api = ky.create({
	baseUrl: 'https://api.example.com/v1/',
	hooks: {
		beforeRequest: [({request}) => {
			console.log('Outgoing URL:', request.url);
		}],
	},
});

async function main() {
	await api.get('users');
	await api.get('/users');
}

main().catch(console.error);

The first call requests /v1/users; the second requests /users at the same origin. Each awaited call returns a response. The hook logs the constructed request URL before Ky sends it.

The input's leading slash means origin-root, not “append to the base.” A base without a trailing slash also changes how page-relative inputs resolve:

baseUrlInputDestination
https://api.example.com/v1/usershttps://api.example.com/v1/users
https://api.example.com/v1//usershttps://api.example.com/users
https://api.example.com/v1usershttps://api.example.com/users

An absolute input bypasses baseUrl. For example, with no prefix, 'https://other.example.com/users' stays at that address.

2. Append leading-slash inputs with prefix

Prefer baseUrl in most cases because it follows web standards. Use prefix when inputs such as /users must append to an API path instead of discarding it.

Ky joins prefix and the string input first, trimming trailing slashes from the prefix and leading slashes from the input at the join boundary. It then resolves the joined result against baseUrl, if present.

ts
import ky from 'ky';

const api = ky.create({
	baseUrl: 'https://api.example.com/',
	prefix: 'v1',
	hooks: {
		beforeRequest: [({request}) => {
			console.log('Outgoing URL:', request.url);
		}],
	},
});

async function main() {
	await api.get('users');
	await api.get('/users');
}

main().catch(console.error);

Both calls request https://api.example.com/v1/users. A trailing slash on prefix is optional. Unlike baseUrl, prefix does string joining even when the string input contains an absolute URL; do not use it as an absolute-URL fallback.

3. Merge query parameters, including on a Request

Pass searchParams to add query parameters without building a query string yourself. Existing input parameters remain, and new values append rather than replace existing values with the same name. Use an object value of undefined to delete a parameter.

A Request input ignores baseUrl and prefix, but searchParams still applies:

ts
import ky from 'ky';

const request = new Request(
	'https://api.example.com/users?role=reader&debug=true',
);

async function main() {
	await ky.get(request, {
		baseUrl: 'https://other.example.com/',
		prefix: 'v1',
		searchParams: {
			page: 2,
			active: true,
			debug: undefined,
		},
		hooks: {
			beforeRequest: [({request: outgoingRequest}) => {
				console.log('Outgoing URL:', outgoingRequest.url);
			}],
		},
	});
}

main().catch(console.error);

The outgoing URL stays at https://api.example.com/users. It retains role=reader, adds page=2 and active=true, and removes debug. The request does not move to other.example.com or acquire a /v1/ path.

You can also pass a query string, a URLSearchParams instance, or an array of key-value pairs. For example, repeated keys represent multiple values; adding another role does not erase the input's role=reader.

Options that matter

These fields belong to Options.

OptionTypeDefaultWhat it does
baseUrlstring | URLundefinedResolves relative string inputs after prefix joining; absolute inputs bypass it.
prefixstring | URL''Prepends to string inputs, normalizing slashes at the join boundary.
searchParamsQuery string, object, key-value pairs, or URLSearchParams''Merges query parameters into the input URL; object values of undefined delete keys.

Server-side and input pitfalls

  • Server-side requests require an absolute URL. Use an absolute baseUrl, as above, rather than a browser-relative base such as /api/. In browsers, a relative base resolves against the environment's base URL, such as document.baseURI.
  • To extend a base path, keep its trailing slash and omit the input's leading slash. Use prefix instead when leading-slash inputs must append.
  • prefix only affects string inputs. Construct a URL or Request with its intended destination before passing it to Ky; neither receives prefix joining, and a Request also ignores baseUrl.

Was this page helpful?

Resolve request URLs — ky · GPT-6.1 Sol