Contexte
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Votre contexte contient des données accessibles par toutes vos procédures tRPC. C'est l'endroit idéal pour placer des éléments comme les connexions à la base de données ou les informations d'authentification.
La configuration du contexte se fait en 2 étapes : définir le type lors de l'initialisation, puis créer le contexte d'exécution pour chaque requête.
Définition du type de contexte
Lors de l'initialisation de tRPC avec initTRPC, vous devez chaîner .context<TContext>() à la fonction de construction initTRPC avant d'appeler .create(). Le type TContext peut être inféré à partir du type de retour d'une fonction ou être explicitement défini.
Cela garantira que votre contexte soit correctement typé dans vos procédures et 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 ();});
Création du contexte
La fonction createContext() doit être passée au gestionnaire qui monte votre appRouter, que ce soit via HTTP, un appel côté serveur ou nos assistants côté serveur.
createContext() est appelée pour chaque invocation de tRPC, donc les requêtes groupées partageront un contexte.
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(),});
Exemple de code
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 ,},});});
Contexte interne et externe
Dans certains scénarios, il peut être judicieux de scinder votre contexte en fonctions "interne" et "externe".
Le contexte interne définit les éléments indépendants de la requête, comme votre connexion à la base de données. Vous pouvez l'utiliser pour les tests d'intégration ou les assistants côté serveur, où vous n'avez pas d'objet requête. Tout ce qui est défini ici sera toujours disponible dans vos procédures.
Le contexte externe définit les éléments dépendants de la requête, comme la session utilisateur. Ce qui est défini ici n'est disponible que pour les procédures appelées via HTTP.
Exemple de contexte interne et externe
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.
Il est crucial d'inférer votre Context à partir du contexte interne, car seul ce qui y est défini est véritablement toujours disponible dans vos procédures.
Si vous souhaitez éviter de vérifier constamment si req ou res sont undefined dans vos procédures, vous pouvez créer une petite procédure réutilisable :
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,},});});
Limitation de la taille des lots
Vous pouvez utiliser le contexte pour limiter le nombre de requêtes pouvant être groupées ensemble.
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 {};}
Ce contexte générera une erreur TOO_MANY_REQUESTS si un client tente de regrouper plus de 10 requêtes. Vous pouvez ajuster la constante MAX_BATCH_SIZE selon vos besoins.