# Getting started

> A router, a handler that serves it, and a client that calls it with the router's types.

A router is a plain object of procedures. A handler serves it; a client calls it with the router's type.

```package-install
@trpc12/trpc effect
```

`effect` is a peer dependency, so your app and `@trpc12/trpc` share one copy; code that uses Promises never imports it ([Packages](https://v12.trpc.io/v12/docs/packages.md)).

`.input()` takes any Standard Schema (zod, Valibot, ArkType, …) or an Effect Schema ([Input and output](https://v12.trpc.io/v12/docs/server/procedures.md#input-and-output)). This page uses zod:

```package-install
zod
```

## Define a router

```ts title="server.ts"
import { createFetchHandler, error, initTrpc } from '@trpc12/trpc/server';
import { z } from 'zod';

const t = initTrpc<{ ctx: { user: string | null } }>();

export const appRouter = {
  post: {
    byId: t.procedure
      .input(z.object({ id: z.string() }))
      .query(
        (opts) => db.post.find(opts.input.id) ?? error({ code: 'NOT_FOUND' }),
      ),
    create: t.procedure
      .input(z.object({ title: z.string().min(1) }))
      .mutation((opts) =>
        db.post.create({ ...opts.input, author: opts.ctx.user }),
      ),
  },
};
export type AppRouter = typeof appRouter;

export const handler = createFetchHandler({
  router: appRouter,
  createContext: (opts) => ({
    user: opts.request.headers['authorization'] ?? null,
  }),
});
```

`request` is Effect's `HttpServerRequest`: `headers` is a record of lowercase names, and on fetch runtimes `request.source` is the web `Request`.

## Serve it

`handler.fetch(request)` is a web-standard handler: see [Adapters](https://v12.trpc.io/v12/docs/adapters/overview.md) to serve it. On Node:

```ts title="main.ts"
import { createServer } from 'node:http';
import { NodeHttpServer, NodeRuntime } from '@effect/platform-node';
import { Layer } from 'effect';
import { serve } from '@trpc12/trpc/http';

serve(handler).pipe(
  Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 })),
  Layer.launch,
  NodeRuntime.runMain,
);
```

## Call it

The client imports only the type:

```ts title="client.ts"
import {
  createTrpcClient,
  httpLink,
  matchError,
  routerType,
  safe,
} from '@trpc12/trpc/client';
import type { AppRouter } from './server.ts';

const client = createTrpcClient({
  router: routerType<AppRouter>(),
  links: [httpLink({ url: 'http://localhost:3000/trpc' })],
});

const post = await client.post.byId.query({ id: '1' });
// const post: {
//     id: string;
//     title: string;
//     author: string | null;
//     createdAt: string;
// }
const [created, err] = await safe(client.post.create.mutate({ title: '' }));
if (err) {
  matchError(err, {
    BAD_REQUEST: (e) => e.data.issues,
    // (property) SchemaInputErrorData<ZodObject<{ title: ZodString; }, $strip>>.issues: readonly ({
    //     readonly code: "invalid_type";
    //     readonly expected: $ZodInvalidTypeExpected;
    //     readonly path: readonly [] | readonly ["title"];
    //     readonly message: string;
    // } | {
    //     readonly code: "too_big";
    //     readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    //     readonly maximum: string | number;
    //     readonly inclusive?: boolean | undefined;
    //     readonly exact?: boolean | undefined;
    //     readonly path: readonly [] | readonly ["title"];
    //     readonly message: string;
    // } | {
    //     readonly code: "too_small";
    //     readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
    //     ... 4 more ...;
    //     readonly message: string;
    // } | ... 8 more ... | {
    //     ...;
    // })[]
    builtin: (e) => e.message,
  });
} else {
  created.id;
}
```

`routerType<AppRouter>()` types the client from the router type alone, and the client also types what its links read (see [The client](https://v12.trpc.io/v12/docs/client/overview.md)).

`safe()` resolves to `[data, error]` instead of throwing. Once `error` is handled, `data` is the output. `matchError()` takes a handler for each of the procedure's errors and one for the built-in ones (network failures, aborts, crashes), and leaving one out is a type error ([Errors without try/catch](https://v12.trpc.io/v12/docs/client/errors.md)).

## Naming

Names case acronyms as words: `TrpcError`, `initTrpc`, `createTrpcClient`, `OpenApi…`, `Json…`.

## Next steps

- [Procedures](https://v12.trpc.io/v12/docs/server/procedures.md): input, output and the three procedure types.
- [Errors](https://v12.trpc.io/v12/docs/server/errors.md): typed errors without declaring them.
- [Middleware](https://v12.trpc.io/v12/docs/server/middleware.md): context, base procedures and reusable middleware.
- [Migrating from v11](https://v12.trpc.io/v12/docs/migrations/from-v11.md): serve old clients while you move.
