# Routers

> Plain objects of procedures, merged with mergeRouters and split with lazy().

A router is a plain object. Keys are path segments: `{ post: { byId } }` serves `post.byId`, at `/trpc/post.byId` over HTTP.

```ts
export const appRouter = {
  health: t.procedure.query(() => 'ok'),
  post: { byId, create },
};
```

Merge routers with `mergeRouters(a, b)`. A key in both is a type error and a runtime error; object spread would silently keep the last.

With thousands of procedures, split the router into packages and type apps from the server's built declarations: see [Large routers](https://v12.trpc.io/v12/docs/server/large-routers.md).

## Lazy subtrees

`lazy()` loads a subtree on its first call, for large routers that should start fast (serverless cold starts). The client types it as if it were inline.

```ts
import { lazy } from '@trpc12/trpc/server';

export const appRouter = {
  post: postRouter,
  admin: lazy(() => import('./admin.ts')),
};
```

`t.lazy(load, { meta })` also takes router-level meta, as `t.router()` does.

`load` may return the router or a module whose `default` export is the router. Each subtree loads once.

- The RPC handlers, `createRouterClient` and `localLink` load it on the first call under it.
- Tools that need the whole router ask for it loaded: `await loadRouter(appRouter)`.
