---
title: Router
sidebar:
  icon: git-branch
description: Group procedures into a router, nest them, check their inputs, and call them in the same process.
---

`tacho()` creates a procedure builder. Call it as `rpc({ ... })` to make a router.

```ts twoslash
import { tacho } from "tacho";
import { z } from "zod";

const rpc = tacho();

export const app = rpc({
  ping: rpc.run(() => "pong" as const),
  user: {
    get: rpc
      .input(z.object({ id: z.string() }))
      .output(z.object({ id: z.string(), name: z.string() }))
      .run(({ input }) => ({ id: input.id, name: "Ada" })),
  },
});
```

- **`.input(schema)`** - Standard Schema. Failed input is `INVALID_PARAMS`.
- **`.output(schema)`** - Failed output is `INTERNAL_ERROR`. Streams check each `yield`.
- **`.run(fn)`** - Terminal. `fn` gets `{ input, ctx }`. Return a value, a `Promise`, or an `async function*`.

No `.input()` → params are untyped / unused. Nested paths are `user.get`.
