Files

120 lines
3.2 KiB
Markdown

# trpc-toolbox
This package provides type utilities to work with trpc.
Specifically, it provides a way to describe your trpc setup in an
interface definition, then use it in the server implementation
and when creating a client.
## Defining a trpc router
Create your interface package:
```ts
import type { defineTrpcRouter } from 'trpc-toolbox';
// Define rpc setup
type RpcSetup = {
ctx: { userName: string };
meta: { hasAuth: boolean };
};
// Define router setup
type RouterProcedures = {
greeter: {
greet: {
type: 'query';
input: { times: number };
output: { greeting: string };
};
};
};
export type TRouter = defineTrpcRouter<RouterProcedures, RpcSetup>;
```
## Implementing the router
In your router implementation, import your router settings and type.
```ts
import { inferRouterContext, inferRouterMeta, initTRPC } from '@trpc/server';
import { z } from 'zod';
import { TRouter } from 'my-router-interface';
const t = initTRPC.context<inferRouterContext<TRouter>>().meta<inferRouterMeta<TRouter>>().create();
export const router = t.router({
greeter: t.router({
greet: t.procedure
.input(z.object({ times: z.number() }))
.output(z.object({ greeting: z.string() }))
.query(({ input, ctx }) => {
return { greeting: `Hello ${ctx.userName} ${input.times} times!` };
}),
}),
}) satisfies TRouter; // Don't forget the `satisfies` for type safety!
```
## Creating a client
To create a typed client all you need is the router type:
```ts
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import { TRouter } from 'my-router-interface';
const client = createTRPCProxyClient<TRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000/trpc',
}),
],
});
const myGreeting /* { greeting: string } */ = await client.greeting.greet({
times: 93939939393932438032480285094573954809537,
});
```
## Usage with Zod (or other validators)
Router interfaces may be created with Zod validators as inputs or outputs.
This may be useful in a case where you'd like tooling to auto-generate documentation
based on Zod objects, which it may not be able to do from types in the type
system.
To use this technique, define your input and output formats, then
use the `typeof` each in the router definition:
```ts
export const incrementInput = z.object({ times: z.number() });
export const incrementOutput = z.object({ countString: z.string() });
export type TZodRouter = defineTrpcRouter<{
increment: {
type: 'mutation';
input: typeof input;
output: typeof output;
};
}>;
```
Your implementation can then import the zod validators and use them
in the router:
```ts
import { incrementInput, incrementOutput, TZodRouter } from 'zod-interface';
const tZodRpc = initTRPC.context<inferRouterContext<TZodRouter>>().meta<inferRouterMeta<TZodRouter>>().create();
const zodRouter = tZodRpc.router({
increment: tZodRpc.procedure
.input(incrementInput)
.output(incrementOutput)
.mutation(({ input }) => {
count *= input.times;
return { countString: `Count is ${count}` };
}),
}) satisfies TZodRouter;
```