Contexto
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Tu contexto contiene datos a los que todos tus procedimientos tRPC tendrán acceso, siendo un lugar ideal para conexiones a bases de datos o información de autenticación.
Configurar el contexto se realiza en 2 pasos: definir el tipo durante la inicialización y luego crear el contexto en tiempo de ejecución para cada solicitud.
Definir el tipo de contexto
Al inicializar tRPC con initTRPC, debes encadenar .context<TContext>() a la función constructora initTRPC antes de llamar a .create(). El tipo TContext puede inferirse del tipo de retorno de una función o definirse explícitamente.
Esto garantizará que tu contexto tenga tipado correcto en procedimientos y middlewares.
tsimport {initTRPC } from '@trpc/server';import type {CreateNextContextOptions } from '@trpc/server/adapters/next';import {getSession } from 'next-auth/react';export constcreateContext = async (opts :CreateNextContextOptions ) => {constsession = awaitgetSession ({req :opts .req });return {session ,};};export typeContext =Awaited <ReturnType <typeofcreateContext >>;constt =initTRPC .context <Context >().create ();t .procedure .use ((opts ) => {opts .ctx ;returnopts .next ();});
tsimport {initTRPC } from '@trpc/server';import type {CreateNextContextOptions } from '@trpc/server/adapters/next';import {getSession } from 'next-auth/react';export constcreateContext = async (opts :CreateNextContextOptions ) => {constsession = awaitgetSession ({req :opts .req });return {session ,};};export typeContext =Awaited <ReturnType <typeofcreateContext >>;constt =initTRPC .context <Context >().create ();t .procedure .use ((opts ) => {opts .ctx ;returnopts .next ();});
Crear el contexto
La función createContext() debe pasarse al manejador que monta tu appRouter, ya sea mediante HTTP, una llamada de servidor o nuestros servicios de servidor.
createContext() se llama en cada invocación de tRPC, por lo que las solicitudes agrupadas compartirán un contexto.
ts// 1. HTTP requestimport { createHTTPHandler } from '@trpc/server/adapters/standalone';import { createContext } from './context';import { appRouter } from './router';const handler = createHTTPHandler({router: appRouter,createContext,});
ts// 1. HTTP requestimport { createHTTPHandler } from '@trpc/server/adapters/standalone';import { createContext } from './context';import { appRouter } from './router';const handler = createHTTPHandler({router: appRouter,createContext,});
ts// 2. Server-side callimport { createContext } from './context';import { createCaller } from './router';const caller = createCaller(await createContext());
ts// 2. Server-side callimport { createContext } from './context';import { createCaller } from './router';const caller = createCaller(await createContext());
ts// 3. servers-side helpersimport { createServerSideHelpers } from '@trpc/react-query/server';import { createContext } from './context';import { appRouter } from './router';const helpers = createServerSideHelpers({router: appRouter,ctx: await createContext(),});
ts// 3. servers-side helpersimport { createServerSideHelpers } from '@trpc/react-query/server';import { createContext } from './context';import { appRouter } from './router';const helpers = createServerSideHelpers({router: appRouter,ctx: await createContext(),});
Código de ejemplo
tsx// -------------------------------------------------// @filename: context.ts// -------------------------------------------------import type {CreateNextContextOptions } from '@trpc/server/adapters/next';import {getSession } from 'next-auth/react';/*** Creates context for an incoming request* @see https://trpc.io/docs/v11/context*/export async functioncreateContext (opts :CreateNextContextOptions ) {constsession = awaitgetSession ({req :opts .req });return {session ,};}export typeContext =Awaited <ReturnType <typeofcreateContext >>;// -------------------------------------------------// @filename: trpc.ts// -------------------------------------------------import {initTRPC ,TRPCError } from '@trpc/server';import {Context } from './context';constt =initTRPC .context <Context >().create ();export constrouter =t .router ;/*** Unprotected procedure*/export constpublicProcedure =t .procedure ;/*** Protected procedure*/export constprotectedProcedure =t .procedure .use (functionisAuthed (opts ) {if (!opts .ctx .session ?.user ?.throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {// Infers the `session` as non-nullablesession :opts .ctx .session ,},});});
tsx// -------------------------------------------------// @filename: context.ts// -------------------------------------------------import type {CreateNextContextOptions } from '@trpc/server/adapters/next';import {getSession } from 'next-auth/react';/*** Creates context for an incoming request* @see https://trpc.io/docs/v11/context*/export async functioncreateContext (opts :CreateNextContextOptions ) {constsession = awaitgetSession ({req :opts .req });return {session ,};}export typeContext =Awaited <ReturnType <typeofcreateContext >>;// -------------------------------------------------// @filename: trpc.ts// -------------------------------------------------import {initTRPC ,TRPCError } from '@trpc/server';import {Context } from './context';constt =initTRPC .context <Context >().create ();export constrouter =t .router ;/*** Unprotected procedure*/export constpublicProcedure =t .procedure ;/*** Protected procedure*/export constprotectedProcedure =t .procedure .use (functionisAuthed (opts ) {if (!opts .ctx .session ?.user ?.throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {// Infers the `session` as non-nullablesession :opts .ctx .session ,},});});
Contexto interno y externo
En algunos escenarios puede ser útil dividir tu contexto en funciones "internas" y "externas".
Contexto interno: Aquí defines elementos que no dependen de la solicitud, como conexiones a bases de datos. Puedes usar esta función para pruebas de integración o servicios de servidor donde no tienes un objeto de solicitud. Todo lo definido aquí estará siempre disponible en tus procedimientos.
Contexto externo: Aquí defines elementos que dependen de la solicitud, como la sesión del usuario. Lo definido aquí solo está disponible para procedimientos llamados mediante HTTP.
Ejemplo de contexto interno y externo
tsimport type { CreateNextContextOptions } from '@trpc/server/adapters/next';import { getSessionFromCookie, type Session } from './auth';/*** Defines your inner context shape.* Add fields here that the inner context brings.*/interface CreateInnerContextOptions extends Partial<CreateNextContextOptions> {session: Session | null;}/*** Inner context. Will always be available in your procedures, in contrast to the outer context.** Also useful for:* - testing, so you don't have to mock Next.js' `req`/`res`* - tRPC's `createServerSideHelpers` where we don't have `req`/`res`** @see https://trpc.io/docs/v11/context#inner-and-outer-context*/export async function createContextInner(opts?: CreateInnerContextOptions) {return {prisma,session: opts.session,};}/*** Outer context. Used in the routers and will e.g. bring `req` & `res` to the context as "not `undefined`".** @see https://trpc.io/docs/v11/context#inner-and-outer-context*/export async function createContext(opts: CreateNextContextOptions) {const session = getSessionFromCookie(opts.req);const contextInner = await createContextInner({ session });return {...contextInner,req: opts.req,res: opts.res,};}export type Context = Awaited<ReturnType<typeof createContextInner>>;// The usage in your router is the same as the example above.
tsimport type { CreateNextContextOptions } from '@trpc/server/adapters/next';import { getSessionFromCookie, type Session } from './auth';/*** Defines your inner context shape.* Add fields here that the inner context brings.*/interface CreateInnerContextOptions extends Partial<CreateNextContextOptions> {session: Session | null;}/*** Inner context. Will always be available in your procedures, in contrast to the outer context.** Also useful for:* - testing, so you don't have to mock Next.js' `req`/`res`* - tRPC's `createServerSideHelpers` where we don't have `req`/`res`** @see https://trpc.io/docs/v11/context#inner-and-outer-context*/export async function createContextInner(opts?: CreateInnerContextOptions) {return {prisma,session: opts.session,};}/*** Outer context. Used in the routers and will e.g. bring `req` & `res` to the context as "not `undefined`".** @see https://trpc.io/docs/v11/context#inner-and-outer-context*/export async function createContext(opts: CreateNextContextOptions) {const session = getSessionFromCookie(opts.req);const contextInner = await createContextInner({ session });return {...contextInner,req: opts.req,res: opts.res,};}export type Context = Awaited<ReturnType<typeof createContextInner>>;// The usage in your router is the same as the example above.
Es crucial inferir tu Context desde el contexto interno, pues solo lo definido allí está realmente disponible siempre en tus procedimientos.
Si prefieres no verificar constantemente si req o res son undefined en tus procedimientos, puedes crear un pequeño procedimiento reutilizable:
tsexport const apiProcedure = publicProcedure.use((opts) => {if (!opts.ctx.req || !opts.ctx.res) {throw new Error('You are missing `req` or `res` in your call.');}return opts.next({ctx: {// We overwrite the context with the truthy `req` & `res`, which will also overwrite the types used in your procedure.req: opts.ctx.req,res: opts.ctx.res,},});});
tsexport const apiProcedure = publicProcedure.use((opts) => {if (!opts.ctx.req || !opts.ctx.res) {throw new Error('You are missing `req` or `res` in your call.');}return opts.next({ctx: {// We overwrite the context with the truthy `req` & `res`, which will also overwrite the types used in your procedure.req: opts.ctx.req,res: opts.ctx.res,},});});
Limitar el tamaño de lotes
Puedes usar el contexto para limitar la cantidad de solicitudes que pueden agruparse.
tsimport {TRPCError } from '@trpc/server';import type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';constMAX_BATCH_SIZE = 10;// Create a context that checks batch sizeexport async functioncreateContext (opts :CreateHTTPContextOptions ) {if (opts .info .calls .length >MAX_BATCH_SIZE ) {throw newTRPCError ({code : 'TOO_MANY_REQUESTS',message : `Batch size limit of ${MAX_BATCH_SIZE } exceeded`,});}return {};}
tsimport {TRPCError } from '@trpc/server';import type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';constMAX_BATCH_SIZE = 10;// Create a context that checks batch sizeexport async functioncreateContext (opts :CreateHTTPContextOptions ) {if (opts .info .calls .length >MAX_BATCH_SIZE ) {throw newTRPCError ({code : 'TOO_MANY_REQUESTS',message : `Batch size limit of ${MAX_BATCH_SIZE } exceeded`,});}return {};}
Este contexto lanzará un error TOO_MANY_REQUESTS si un cliente intenta agrupar más de 10 solicitudes. Puedes ajustar la constante MAX_BATCH_SIZE según tus necesidades.