Usage with the Fetch API
tRPC includes an adapter for the native Fetch API out of the box. This adapter lets you convert your tRPC router into a Request
handler that returns Response
objects.
Example apps
Description | Links |
---|---|
Cloudflare Workers example | Source |
Deno Deploy example | Source |
Next.js Edge Runtime example | Source |
Vercel Edge Runtime example | Source |
Common setup
Install dependencies
You can skip this step if you use Deno Deploy.
- npm
- yarn
- pnpm
sh
npm install @trpc/server zod
sh
npm install @trpc/server zod
sh
yarn add @trpc/server zod
sh
yarn add @trpc/server zod
sh
pnpm add @trpc/server zod
sh
pnpm add @trpc/server zod
Zod isn't a required dependency, but it's used in the sample router below.
Create the router
First of all you need a router to handle your queries, mutations and subscriptions.
A sample router is given below, save it in a file named router.ts
.
router.ts
router.tsts
import { initTRPC } from '@trpc/server';import { z } from 'zod';import { Context } from './context';type User = {id: string;name: string;bio?: string;};const users: Record<string, User> = {};export const t = initTRPC.context<Context>().create();export const appRouter = t.router({getUserById: t.procedure.input(z.string()).query(({ input }) => {return users[input]; // input type is string}),createUser: t.procedure// validate input with Zod.input(z.object({name: z.string().min(3),bio: z.string().max(142).optional(),}),).mutation(({ input }) => {const id = Date.now().toString();const user: User = { id, ...input };users[user.id] = user;return user;}),});// export type definition of APIexport type AppRouter = typeof appRouter;
router.tsts
import { initTRPC } from '@trpc/server';import { z } from 'zod';import { Context } from './context';type User = {id: string;name: string;bio?: string;};const users: Record<string, User> = {};export const t = initTRPC.context<Context>().create();export const appRouter = t.router({getUserById: t.procedure.input(z.string()).query(({ input }) => {return users[input]; // input type is string}),createUser: t.procedure// validate input with Zod.input(z.object({name: z.string().min(3),bio: z.string().max(142).optional(),}),).mutation(({ input }) => {const id = Date.now().toString();const user: User = { id, ...input };users[user.id] = user;return user;}),});// export type definition of APIexport type AppRouter = typeof appRouter;
If your router file starts getting too big, split your router into several subrouters each implemented in its own file. Then merge them into a single root appRouter
.
Create the context
Then you need a context that will be created for each request.
A sample context is given below, save it in a file named context.ts
:
context.ts
context.tsts
import { inferAsyncReturnType } from '@trpc/server';import { FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';export function createContext({ req }: FetchCreateContextFnOptions) {const user = { name: req.headers.get('username') ?? 'anonymous' };return { req, user };}export type Context = inferAsyncReturnType<typeof createContext>;
context.tsts
import { inferAsyncReturnType } from '@trpc/server';import { FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';export function createContext({ req }: FetchCreateContextFnOptions) {const user = { name: req.headers.get('username') ?? 'anonymous' };return { req, user };}export type Context = inferAsyncReturnType<typeof createContext>;
Runtimes-specific setup
Cloudflare Worker
Install dependencies
- npm
- yarn
- pnpm
sh
npm install -g wrangler
sh
npm install -g wrangler
sh
yarn global add wrangler
sh
yarn global add wrangler
sh
pnpm add -g wrangler
sh
pnpm add -g wrangler
Create Cloudflare Worker
server.tsts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';import { createContext } from './context';import { appRouter } from './router';export default {async fetch(request: Request): Promise<Response> {return fetchRequestHandler({endpoint: '/trpc',req: request,router: appRouter,createContext,});},};
server.tsts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';import { createContext } from './context';import { appRouter } from './router';export default {async fetch(request: Request): Promise<Response> {return fetchRequestHandler({endpoint: '/trpc',req: request,router: appRouter,createContext,});},};
Run wrangler dev server.ts
and your endpoints will be available via HTTP!
Endpoint | HTTP URI |
---|---|
getUser | GET http://localhost:8787/trpc/getUserById?input=INPUT where INPUT is a URI-encoded JSON string. |
createUser | POST http://localhost:8787/trpc/createUser with req.body of type User |
Deno Deploy
Install dependencies
- macOS / Linux
- Windows
sh
curl -fsSL https://deno.land/x/install/install.sh | sh
sh
curl -fsSL https://deno.land/x/install/install.sh | sh
sh
irm https://deno.land/install.ps1 | iex
sh
irm https://deno.land/install.ps1 | iex
Update the imports in router.ts
router.tsts
import { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
router.tsts
import { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
Update the imports in context.ts
context.tsts
import { inferAsyncReturnType } from 'npm:@trpc/server';import { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
context.tsts
import { inferAsyncReturnType } from 'npm:@trpc/server';import { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
Create Deno Deploy Function
server.tsts
import { serve } from 'https://deno.land/std@0.140.0/http/server.ts';import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';import { createContext } from './context.ts';import { appRouter } from './router.ts';function handler(request) {return fetchRequestHandler({endpoint: '/trpc',req: request,router: appRouter,createContext,});}serve(handler);
server.tsts
import { serve } from 'https://deno.land/std@0.140.0/http/server.ts';import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';import { createContext } from './context.ts';import { appRouter } from './router.ts';function handler(request) {return fetchRequestHandler({endpoint: '/trpc',req: request,router: appRouter,createContext,});}serve(handler);
Run deno run --allow-net=:8000 --allow-env ./server.ts
and your endpoints will be available via HTTP!
Endpoint | HTTP URI |
---|---|
getUser | GET http://localhost:8000/trpc/getUserById?input=INPUT where INPUT is a URI-encoded JSON string. |
createUser | POST http://localhost:8000/trpc/createUser with req.body of type User |
Next.js Edge Runtime
See a full example here.
Vercel Edge Runtime
Install dependencies
- npm
- yarn
- pnpm
sh
npm install -g edge-runtime
sh
npm install -g edge-runtime
sh
yarn global add edge-runtime
sh
yarn global add edge-runtime
sh
pnpm add -g edge-runtime
sh
pnpm add -g edge-runtime
Create Edge Runtime Function
server.tsts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';import { createContext } from './context';import { appRouter } from './router';addEventListener('fetch', (event) => {return event.respondWith(fetchRequestHandler({endpoint: '/trpc',req: event.request,router: appRouter,createContext,}),);});
server.tsts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';import { createContext } from './context';import { appRouter } from './router';addEventListener('fetch', (event) => {return event.respondWith(fetchRequestHandler({endpoint: '/trpc',req: event.request,router: appRouter,createContext,}),);});
Run edge-runtime --listen server.ts --port 3000
and your endpoints will be available via HTTP!
Endpoint | HTTP URI |
---|---|
getUser | GET http://localhost:3000/trpc/getUserById?input=INPUT where INPUT is a URI-encoded JSON string. |
createUser | POST http://localhost:3000/trpc/createUser with req.body of type User |