---
title: Contract Route Builder
description: 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](/docs/route-builder).
The difference is that outputs are declared with schemas rather than inferred
from handlers.

## Declare Routes

```ts contract.ts
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](/docs/http-behavior/schemas).

## Implement Routes

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

```ts server.ts
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()`:

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

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

See the [Contract-first Quickstart](/docs/contract-first-quickstart) for a
complete example and [Fetch Client](/docs/client/fetch-client) for client options.
