Adaptador para Fetch y Edge Runtimes
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Puedes crear un servidor tRPC en cualquier entorno edge que siga las especificaciones de WinterCG, en concreto la Minimum Common Web Platform API.
Algunos de estos entornos incluyen, entre otros:
-
Cloudflare Workers
-
Deno Deploy
-
Vercel Edge Runtime (y Next.js Edge Runtime)
Esto también facilita la integración en frameworks que utilizan las APIs web para representar solicitudes y respuestas, como:
-
Astro (modo SSR)
-
Remix
-
SolidStart
Aplicaciones de ejemplo
| Description | Links |
|---|---|
| Cloudflare Workers example | Source |
| Deno Deploy example | Source |
| Next.js Edge Runtime example | Source |
| Vercel Edge Runtime example | Source |
Cómo usar el servidor tRPC con un entorno edge
tRPC proporciona un adaptador fetch que utiliza las APIs nativas Request y Response como entrada y salida. El código específico de tRPC es idéntico en todos los entornos; la única diferencia radica en cómo se devuelve la respuesta.
tRPC incluye de serie un adaptador para la Fetch API nativa. Este adaptador te permite convertir tu enrutador tRPC en un manejador de Request que devuelve objetos Response.
APIs web requeridas
El servidor tRPC utiliza las siguientes APIs de Fetch:
-
Request,Response -
fetch -
Headers -
URL
Si tu entorno es compatible con estas APIs, puedes usar el servidor tRPC.
Dato curioso: ¡eso también significa que puedes usar un servidor tRPC en tu navegador!
Configuración común
Instalar dependencias
Puedes omitir este paso si usas Deno Deploy.
- npm
- yarn
- pnpm
- bun
- deno
npm install @trpc/server @trpc/client zod
yarn add @trpc/server @trpc/client zod
pnpm add @trpc/server @trpc/client zod
bun add @trpc/server @trpc/client zod
deno add npm:@trpc/server npm:@trpc/client npm:zod
Zod no es una dependencia obligatoria, pero se utiliza en el enrutador de ejemplo a continuación.
Crear el enrutador
En primer lugar, necesitas un enrutador para manejar tus consultas, mutaciones y suscripciones.
A continuación se muestra un enrutador de ejemplo; guárdalo en un archivo llamado router.ts.
router.ts
router.tstsimport { 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((opts) => {return users[opts.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((opts) => {const id = Date.now().toString();const user: User = { id, ...opts.input };users[user.id] = user;return user;}),});// export type definition of APIexport type AppRouter = typeof appRouter;
router.tstsimport { 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((opts) => {return users[opts.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((opts) => {const id = Date.now().toString();const user: User = { id, ...opts.input };users[user.id] = user;return user;}),});// export type definition of APIexport type AppRouter = typeof appRouter;
Si tu archivo de enrutador comienza a ser demasiado grande, divide el enrutador en varios subenrutadores implementados cada uno en su propio archivo. Luego fusiónalos en un único enrutador raíz appRouter.
Crear el contexto
Luego necesitas un contexto que se creará para cada solicitud.
A continuación se muestra un contexto de ejemplo; guárdalo en un archivo llamado context.ts:
context.ts
context.tstsimport { FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';export function createContext({req,resHeaders,}: FetchCreateContextFnOptions) {const user = { name: req.headers.get('username') ?? 'anonymous' };return { req, resHeaders, user };}export type Context = Awaited<ReturnType<typeof createContext>>;
context.tstsimport { FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';export function createContext({req,resHeaders,}: FetchCreateContextFnOptions) {const user = { name: req.headers.get('username') ?? 'anonymous' };return { req, resHeaders, user };}export type Context = Awaited<ReturnType<typeof createContext>>;
Configuración específica por entorno
Astro
src/pages/trpc/[trpc].tstsimport { fetchRequestHandler } from '@trpc/server/adapters/fetch';import type { APIRoute } from 'astro';import { createContext } from '../../server/context';import { appRouter } from '../../server/router';export const ALL: APIRoute = (opts) => {return fetchRequestHandler({endpoint: '/trpc',req: opts.request,router: appRouter,createContext,});};
src/pages/trpc/[trpc].tstsimport { fetchRequestHandler } from '@trpc/server/adapters/fetch';import type { APIRoute } from 'astro';import { createContext } from '../../server/context';import { appRouter } from '../../server/router';export const ALL: APIRoute = (opts) => {return fetchRequestHandler({endpoint: '/trpc',req: opts.request,router: appRouter,createContext,});};
Cloudflare Worker
Necesitas la CLI de Wrangler para ejecutar Cloudflare Workers.
Crear Cloudflare Worker
server.tstsimport { 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.tstsimport { 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,});},};
Ejecuta wrangler dev server.ts y tus endpoints estarán disponibles mediante 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 Oak
Esto asume que tienes Deno instalado y configurado. Consulta su guía de inicio para más información.
Actualizar las importaciones en router.ts
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
Actualizar las importaciones en context.ts
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
Usar fetchRequestHandler con Oak en app.ts
app.tstsimport { Application, Router } from 'https://deno.land/x/oak/mod.ts';import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';import { createContext } from './context.ts';import { appRouter } from './router.ts';const app = new Application();const router = new Router();router.all('/trpc/(.*)', async (ctx) => {const res = await fetchRequestHandler({endpoint: '/trpc',req: new Request(ctx.request.url, {headers: ctx.request.headers,body:ctx.request.method !== 'GET' && ctx.request.method !== 'HEAD'? ctx.request.body({ type: 'stream' }).value: void 0,method: ctx.request.method,}),router: appRouter,createContext,});ctx.response.status = res.status;ctx.response.headers = res.headers;ctx.response.body = res.body;});app.use(router.routes());app.use(router.allowedMethods());await app.listen({ port: 3000 });
app.tstsimport { Application, Router } from 'https://deno.land/x/oak/mod.ts';import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';import { createContext } from './context.ts';import { appRouter } from './router.ts';const app = new Application();const router = new Router();router.all('/trpc/(.*)', async (ctx) => {const res = await fetchRequestHandler({endpoint: '/trpc',req: new Request(ctx.request.url, {headers: ctx.request.headers,body:ctx.request.method !== 'GET' && ctx.request.method !== 'HEAD'? ctx.request.body({ type: 'stream' }).value: void 0,method: ctx.request.method,}),router: appRouter,createContext,});ctx.response.status = res.status;ctx.response.headers = res.headers;ctx.response.body = res.body;});app.use(router.routes());app.use(router.allowedMethods());await app.listen({ port: 3000 });
Deno Deploy
Esto asume que tienes Deno instalado y configurado. Consulta su guía de inicio para más información.
Mira nuestra aplicación de ejemplo para Deno Deploy como referencia funcional.
Actualizar las importaciones en router.ts
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
Actualizar las importaciones en context.ts
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
Crear función de Deno Deploy
server.tstsimport { 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,});}Deno.serve(handler);
server.tstsimport { 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,});}Deno.serve(handler);
Ejecuta deno run --allow-net=:8000 --allow-env ./server.ts y tus endpoints estarán disponibles vía 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
Ver ejemplo completo aquí.
Remix
app/routes/trpc.$trpc.tstsimport type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node';import { fetchRequestHandler } from '@trpc/server/adapters/fetch';import { createContext } from '~/server/context';import { appRouter } from '~/server/router';export const loader = async (args: LoaderFunctionArgs) => {return handleRequest(args);};export const action = async (args: ActionFunctionArgs) => {return handleRequest(args);};function handleRequest(args: LoaderFunctionArgs | ActionFunctionArgs) {return fetchRequestHandler({endpoint: '/trpc',req: args.request,router: appRouter,createContext,});}
app/routes/trpc.$trpc.tstsimport type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node';import { fetchRequestHandler } from '@trpc/server/adapters/fetch';import { createContext } from '~/server/context';import { appRouter } from '~/server/router';export const loader = async (args: LoaderFunctionArgs) => {return handleRequest(args);};export const action = async (args: ActionFunctionArgs) => {return handleRequest(args);};function handleRequest(args: LoaderFunctionArgs | ActionFunctionArgs) {return fetchRequestHandler({endpoint: '/trpc',req: args.request,router: appRouter,createContext,});}
SolidStart
src/routes/api/trpc/[trpc].tstsimport { fetchRequestHandler } from '@trpc/server/adapters/fetch';import type { APIEvent } from 'solid-start';import { createContext } from '../../server/context';import { appRouter } from '../../server/router';const handler = (event: APIEvent) =>fetchRequestHandler({endpoint: '/api/trpc',req: event.request,router: appRouter,createContext,});export { handler as GET, handler as POST };
src/routes/api/trpc/[trpc].tstsimport { fetchRequestHandler } from '@trpc/server/adapters/fetch';import type { APIEvent } from 'solid-start';import { createContext } from '../../server/context';import { appRouter } from '../../server/router';const handler = (event: APIEvent) =>fetchRequestHandler({endpoint: '/api/trpc',req: event.request,router: appRouter,createContext,});export { handler as GET, handler as POST };
Vercel Edge Runtime
Consulta la documentación oficial de Vercel Edge Runtime para más información.
Mira nuestra aplicación de ejemplo para Vercel Edge Runtime como referencia funcional.
Instalar dependencias
- npm
- yarn
- pnpm
- bun
shnpm install -g edge-runtime
shnpm install -g edge-runtime
shyarn global add edge-runtime
shyarn global add edge-runtime
shpnpm add -g edge-runtime
shpnpm add -g edge-runtime
shbun add -g edge-runtime
shbun add -g edge-runtime
Crear función de Edge Runtime
server.tstsimport { 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.tstsimport { 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,}),);});
Ejecuta edge-runtime --listen server.ts --port 3000 y tus endpoints estarán disponibles vía 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 |