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

Contract Route Builder

Declare your routes separately from their handlers.

Import route from @rest-rpc/core to declare a route independently of its server implementation. The contract can be shared directly with a Fetch client and implemented with any server adapter.

The declaration methods are the same as the Route Builder. The difference is that outputs are declared with schemas rather than inferred from handlers.

Declare Routes

import { route } from "@rest-rpc/core";
import { z } from "zod";

const todoSchema = z.object({ id: z.string(), title: z.string() });

const create = route.input(z.object({ title: z.string() })).output(todoSchema);

const get = route
	.get("/todos/:id")
	.params(z.object({ id: z.string() }))
	.response(200, todoSchema)
	.response(404, z.object({ code: z.literal("TODO_NOT_FOUND") }));

export const api = { todos: { create, get } };

Use .output() for a plain result or .response() for status responses. Streams use .streamOutput() or .streamResponse(). These schemas define handler and client types and provide runtime validation.

Implement Routes

Each server adapter exports implement(). Pass a route or route tree to get builders with typed .handler() methods:

import { implement } from "@rest-rpc/express";
import { api } from "./contract";
import { createTodo, findTodo } from "./todos";

const builders = implement(api);

const create = builders.todos.create.handler(({ input: { title } }) =>
	createTodo(title),
);

const get = builders.todos.get.handler(({ params }) => {
	const todo = findTodo(params.id);
	return todo
		? { status: 200, body: todo }
		: { status: 404, body: { code: "TODO_NOT_FOUND" } };
});

export const routes = { todos: { create, get } };

The implementation must match the contract’s inputs and declared outputs. Register the completed routes with your adapter as usual.

Use in a Client

Pass the shared contract directly to initClient():

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

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

See the Contract-first Quickstart for a complete example and Fetch Client for client options.

Was this page helpful?