---
title: Web
description: Use rest-rpc with Fetch runtimes
---

## Install

```sh
pnpm add @rest-rpc/web
```

## Usage

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

type Context = {
	env: Env;
	ctx: ExecutionContext;
};

const web = initWeb<Context>();

const routes = web
	.router(api)
	.middleware(({ runtime }) => ({
		db: runtime.env.DB,
	}))
	.handlers({
		todos: {
			list({ context }) {
				return listTodos(context.db);
			},
			get({ id, context }) {
				return getTodo(context.db, id);
			},
			create({ title, context }) {
				return createTodo(context.db, { title });
			},
		},
	});

const handleRequest = web.createRouteHandler(routes);

export default {
	fetch(request, env, ctx) {
		return handleRequest(request, { env, ctx });
	},
};
```

## Framework Context

```ts
type MiddlewareInput<Runtime> = {
	request: Request;
	route: HttpRouteDeclaration;
	runtime: Runtime;
};
```

The runtime context passed to `handleRequest` is available to middleware. Route
handlers receive the context returned by middleware with `request` added.

## Options

```ts
type CreateWebHandlerOptions = {
	errorHandlers?: ServerErrorHandlers<Record<never, never>>;
	parseBody?: WebRouteParseBody;
};
```

### Error Handlers

Request validation errors use `onRequestValidationError`. Response contract
validation errors use `onResponseValidationError` and default to a generic 500
response. Other unhandled route errors use `onUnhandledError`, or are re-thrown
when that hook is omitted or returns `undefined`.

```ts
const handleRequest = web.createRouteHandler(routes, {
	errorHandlers: {
		onRequestValidationError: ({ issues }) => ({
			status: 422,
			body: { code: "VALIDATION_ERROR", issues },
		}),
		onResponseValidationError: () => ({
			status: 500,
			body: { code: "INVALID_RESPONSE" },
		}),
		onUnhandledError: () => ({
			status: 500,
			body: { code: "INTERNAL_SERVER_ERROR" },
		}),
	},
});
```

### Body Parsing

```ts
const handleUpload = web.createRouteHandler(routes, {
	parseBody: ({ request }) => request.formData(),
});
```
