Quickstart
We highly encourage you to check out the Example Apps to get a feel of tRPC and getting up & running as seamless as possible.
Installation
⚠️ Requirements: tRPC requires TypeScript > 4.1 as it relies on Template Literal Types.
For implementing tRPC endpoints and routers. Install in your server codebase.
npm install @trpc/server@next
For making typesafe API calls from your client. Install in your client codebase (@trpc/server
is a peer dependency of @trpc/client
).
npm install @trpc/client@next @trpc/server@next
For generating a powerful set of React hooks for querying your tRPC API. Powered by @tanstack/react-query.
npm install @trpc/react-query@next @tanstack/react-query
For a set of utilies for integrating tRPC with Next.js.
npm install @trpc/next@next
Installation Snippets
npm
bash
npm install @trpc/server@next @trpc/client@next @trpc/react-query@next @trpc/next@next @tanstack/react-query
bash
npm install @trpc/server@next @trpc/client@next @trpc/react-query@next @trpc/next@next @tanstack/react-query
yarn
bash
yarn add @trpc/server@next @trpc/client@next @trpc/react-query@next @trpc/next@next @tanstack/react-query
bash
yarn add @trpc/server@next @trpc/client@next @trpc/react-query@next @trpc/next@next @tanstack/react-query
pnpm
bash
pnpm add @trpc/server@next @trpc/client@next @trpc/react-query@next @trpc/next@next @tanstack/react-query
bash
pnpm add @trpc/server@next @trpc/client@next @trpc/react-query@next @trpc/next@next @tanstack/react-query
Defining a router
Let's walk through the steps of building a typesafe API with tRPC. To start, this API will only contain two endpoints:
ts
userById(id: string) => { id: string; name: string; }userCreate(data: {name:string}) => { id: string; name: string; }
ts
userById(id: string) => { id: string; name: string; }userCreate(data: {name:string}) => { id: string; name: string; }
Create a router instance
First we define a router somewhere in our server codebase:
server.tsts
import {initTRPC } from '@trpc/server';constt =initTRPC .create ();constappRouter =t .router ({});// only export *type signature* of router!// to avoid accidentally importing your API// into client-side codeexport typeAppRouter = typeofappRouter ;
server.tsts
import {initTRPC } from '@trpc/server';constt =initTRPC .create ();constappRouter =t .router ({});// only export *type signature* of router!// to avoid accidentally importing your API// into client-side codeexport typeAppRouter = typeofappRouter ;
Add a query procedure
Use t.procedure.query()
to add a query procedure/endpoint to the router. Methods:
input
: Optional. This should be a function that validates/casts the input of this procedure and either returns a strongly typed value (if valid) or throws an error (if invalid). Alternatively you can pass a Zod, Superstruct or Yup schema.query
: This is the actual implementation of the procedure (a "resolver"). It's a function with a singlereq
argument. The validated input is passed intoreq.input
and the context is inreq.ctx
(more about context later!)
The following would create a query procedure called userById
that takes a single string argument and returns a user object:
server.tsts
// @filename: server.tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .create ();interfaceUser {id : string;name : string;}constuserList :User [] = [{id : '1',name : 'KATT',},];constappRouter =t .router ({userById :t .procedure .input ((val : unknown) => {if (typeofval === 'string') returnval ;throw newError (`Invalid input: ${typeofval }`);}).query ((req ) => {const {input } =req ;constuser =userList .find ((u ) =>u .id ===input );returnuser ;}),});export typeAppRouter = typeofappRouter ;
server.tsts
// @filename: server.tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .create ();interfaceUser {id : string;name : string;}constuserList :User [] = [{id : '1',name : 'KATT',},];constappRouter =t .router ({userById :t .procedure .input ((val : unknown) => {if (typeofval === 'string') returnval ;throw newError (`Invalid input: ${typeofval }`);}).query ((req ) => {const {input } =req ;constuser =userList .find ((u ) =>u .id ===input );returnuser ;}),});export typeAppRouter = typeofappRouter ;
Add a mutation procedure
Similarly to GraphQL, tRPC makes a distinction between query and mutation procedures. Let's add a userCreate
mutation:
ts
userCreate(payload: {name: string}) => {id: string; name: string};
ts
userCreate(payload: {name: string}) => {id: string; name: string};
server.tsts
// @filename: server.tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .create ();interfaceUser {id : string;name : string;}constuserList :User [] = [{id : '1',name : 'KATT',},];constappRouter =t .router ({userById :t .procedure .input ((val : unknown) => {if (typeofval === 'string') returnval ;throw newError (`Invalid input: ${typeofval }`);}).query ((req ) => {constinput =req .input ;constuser =userList .find ((it ) =>it .id ===input );returnuser ;}),userCreate :t .procedure .input (z .object ({name :z .string () })).mutation ((req ) => {constid = `${Math .random ()}`;constuser :User = {id ,name :req .input .name ,};userList .push (user );returnuser ;}),});export typeAppRouter = typeofappRouter ;
server.tsts
// @filename: server.tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .create ();interfaceUser {id : string;name : string;}constuserList :User [] = [{id : '1',name : 'KATT',},];constappRouter =t .router ({userById :t .procedure .input ((val : unknown) => {if (typeofval === 'string') returnval ;throw newError (`Invalid input: ${typeofval }`);}).query ((req ) => {constinput =req .input ;constuser =userList .find ((it ) =>it .id ===input );returnuser ;}),userCreate :t .procedure .input (z .object ({name :z .string () })).mutation ((req ) => {constid = `${Math .random ()}`;constuser :User = {id ,name :req .input .name ,};userList .push (user );returnuser ;}),});export typeAppRouter = typeofappRouter ;
Using your new backend on the client
Setup the tRPC Client
client.tsts
// @filename: client.tsimport {createTRPCProxyClient ,httpBatchLink } from '@trpc/client';import type {AppRouter } from './server';consttrpc =createTRPCProxyClient <AppRouter >({links : [httpBatchLink ({url : 'http://localhost:3000/trpc',}),],});
client.tsts
// @filename: client.tsimport {createTRPCProxyClient ,httpBatchLink } from '@trpc/client';import type {AppRouter } from './server';consttrpc =createTRPCProxyClient <AppRouter >({links : [httpBatchLink ({url : 'http://localhost:3000/trpc',}),],});
Querying & mutating
client.tsts
// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'sachinraja' });
client.tsts
// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'sachinraja' });
Full autocompletion
client.tsts
// Full autocompletion on your routestrpc .u ;
client.tsts
// Full autocompletion on your routestrpc .u ;
Next steps
tRPC includes more sophisticated client-side tooling designed for React projects generally and Next.js specifically. Read the appropriate guide next: