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:
originfetchfetchOptionsgetGlobalHeaderstimeoutMsunknownRequestKeysvalidateResponses
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.