Skip to content
rest-rpc
Esc
navigateopen⌘Jpreview
On this page

Fetch Client

Create a typed client from the contract.

initClient() creates a typed fetch client from a contract.

import { initClient } from "@rest-rpc/core";
import { api } from "./contract";

const client = initClient(api, {
	origin: "https://api.example.com",
});

The client has the same route tree shape as the contract.

const todo = await client.todos.get.fetch({ id: "todo_1" });

fetch()

Routes with one successful response expose fetch().

const todo = await client.todos.get.fetch({
	id: "todo_1",
});

fetch() returns the successful response body directly.

It throws when the response is undeclared or is not a declared success response.

Use it when non-success responses should use the error path.

fetchResponse()

Every HTTP route exposes fetchResponse().

const response = await client.todos.get.fetchResponse({
	id: "todo_1",
});

fetchResponse() returns declared and undeclared responses as values.

if (response.declared && response.status === 200) {
	return response.body;
}

if (response.declared && response.status === 404) {
	return undefined;
}

throw new Error(`Unexpected response: ${response.status}`);

Use it when HTTP status is part of normal control flow.

Client Options

const client = initClient(api, {
	origin: "https://api.example.com",
	getGlobalHeaders: () => ({
		authorization: `Bearer ${readToken()}`,
	}),
	timeoutMs: 10_000,
	fetchOptions: {
		credentials: "include",
	},
});

Options include:

  • origin
  • fetch
  • fetchOptions
  • getGlobalHeaders
  • timeoutMs
  • unknownRequestKeys
  • validateResponses

origin must be an absolute URL origin without a path, search, or hash. Put API path prefixes in route paths so the fetch client, server adapters, OpenAPI, and cache tags share the same route identity.

Use pathPrefix when routes share a prefix.

const api = router(
	{
		todos: router(
			{
				get: route({
					method: "GET",
					path: "/todos/:id",
					// ...
				}),
			},
			{ pathPrefix: "/v1" },
		),
	},
	{ pathPrefix: "/api" },
);

The client still receives only the origin.

const client = initClient(api, {
	origin: "https://api.example.com",
});

The normalized route path includes the stacked prefixes: api.todos.get.path === "/api/v1/todos/:id".

Custom Fetch

If you need to wrap the fetch client with custom logic, pass a fetch function.

const client = initClient(api, {
	origin: "https://api.example.com",
	fetch: async (url, init) => {
		const startedAt = performance.now();

		try {
			return await fetch(url, init);
		} finally {
			console.log("API request took", performance.now() - startedAt);
		}
	},
});

Per-Call Fetch Options

Pass fetch options as the second argument.

const todo = await client.todos.get.fetch(
	{
		id: "todo_1",
		"x-request-id": "req_1",
	},
	{
		cache: "no-store",
	},
);

Unknown Request Keys

By default, unknown request keys are rejected.

const client = initClient(api, {
	origin: "https://api.example.com",
	unknownRequestKeys: "throw",
});

Use "strip" when extra keys should be ignored before building the HTTP request.

const client = initClient(api, {
	origin: "https://api.example.com",
	unknownRequestKeys: "strip",
});

Response Validation on the Client

It is possible to enable server output validation on the client by enabling validateResponses. For understanding the implications of this option, please read HTTP and Schema I/O gotchas first.

const client = initClient(api, {
	origin: "https://api.example.com",
	validateResponses: true,
});

This option also validates incoming WebSocket server messages on the client.

WebSocket Routes

WebSocket routes expose openConnection() instead of fetch() and fetchResponse().

const socket = client.todos.watch.openConnection({
	id: "todo_1",
});

See WebSockets for details.

Was this page helpful?