Kontext
Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet. Hittade du ett fel? Rapportera problem →
Din context innehåller data som alla dina tRPC-procedurer kommer att ha åtkomst till, och är en perfekt plats för att lägga saker som autentiseringsinformation.
Att sätta upp contexten görs i två steg: först definierar du typen under initialiseringen och sedan skapar du runtime-contexten för varje förfrågan.
Definiera context-typen
När du initierar tRPC med initTRPC bör du kedja .context<TContext>() till initTRPC-byggarfunktionen innan du anropar .create(). Typen TContext kan antingen härledas från en funktions returtyp eller definieras explicit.
Detta säkerställer att din context är korrekt typad i dina procedurer och middleware.
tsimport {initTRPC } from '@trpc/server';import type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';export constcreateContext = async (opts :CreateHTTPContextOptions ) => {// Example: extract a session token from the request headersconsttoken =opts .req .headers ['authorization'];return {token ,};};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 {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';export constcreateContext = async (opts :CreateHTTPContextOptions ) => {// Example: extract a session token from the request headersconsttoken =opts .req .headers ['authorization'];return {token ,};};export typeContext =Awaited <ReturnType <typeofcreateContext >>;constt =initTRPC .context <Context >().create ();t .procedure .use ((opts ) => {opts .ctx ;returnopts .next ();});
Skapa context
Funktionen createContext() måste skickas till handlern som monterar din appRouter. Handlern kan använda HTTP eller ett server-side call.
createContext() anropas en gång per förfrågan, så alla procedurer inom en enskild batched request delar samma context.
ts// 1. HTTP requestimport {createHTTPHandler } from '@trpc/server/adapters/standalone';import {createContext } from './context';import {appRouter } from './router';consthandler =createHTTPHandler ({router :appRouter ,createContext ,});
ts// 1. HTTP requestimport {createHTTPHandler } from '@trpc/server/adapters/standalone';import {createContext } from './context';import {appRouter } from './router';consthandler =createHTTPHandler ({router :appRouter ,createContext ,});
ts// 2. Server-side callimport {createContext } from './context';import {createCaller } from './router';constcaller =createCaller (awaitcreateContext ());
ts// 2. Server-side callimport {createContext } from './context';import {createCaller } from './router';constcaller =createCaller (awaitcreateContext ());
ts// 3. Server-side helpers (Next.js-specific, see /docs/client/nextjs/pages-router/server-side-helpers)import {createServerSideHelpers } from '@trpc/react-query/server';import {createContext } from './context';import {appRouter } from './router';consthelpers =createServerSideHelpers ({router :appRouter ,ctx : awaitcreateContext (),});
ts// 3. Server-side helpers (Next.js-specific, see /docs/client/nextjs/pages-router/server-side-helpers)import {createServerSideHelpers } from '@trpc/react-query/server';import {createContext } from './context';import {appRouter } from './router';consthelpers =createServerSideHelpers ({router :appRouter ,ctx : awaitcreateContext (),});
Exempelkod
ts// -------------------------------------------------// @filename: context.ts// -------------------------------------------------import type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';/*** Creates context for an incoming request* @see https://trpc.io/docs/v11/context*/export async functioncreateContext (opts :CreateHTTPContextOptions ) {consttoken =opts .req .headers ['authorization'];// In a real app, you would verify the token and look up the userconstuser =token ? {return {user ,};}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 .user ?.throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {// Infers the `user` as non-nullableuser :opts .ctx .user ,},});});
ts// -------------------------------------------------// @filename: context.ts// -------------------------------------------------import type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';/*** Creates context for an incoming request* @see https://trpc.io/docs/v11/context*/export async functioncreateContext (opts :CreateHTTPContextOptions ) {consttoken =opts .req .headers ['authorization'];// In a real app, you would verify the token and look up the userconstuser =token ? {return {user ,};}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 .user ?.throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {// Infers the `user` as non-nullableuser :opts .ctx .user ,},});});
Inner och outer context
I vissa scenarion kan det vara meningsfullt att dela upp din context i "inner"- och "outer"-funktioner.
Inner context är där du definierar context som inte beror på förfrågan, t.ex. din databasanslutning. Du kan använda den här funktionen för integrationstester eller server-side calls, där du inte har ett request-objekt. Allt som definieras här kommer alltid att vara tillgängligt i dina procedurer.
createContextInnerAtt lägga en databas-klient som prisma i createContextInner är bekvämt och vanligt, men stora genererade klienter (som Prisma) kan öka typkontrollens overhead eftersom de blir en del av din context-typ över procedurer.
Om denna overhead blir märkbar, är ett alternativ att hålla contexten mindre och importera klienten direkt vid anropsställen där den behövs.
Outer context är där du definierar context som beror på förfrågan, t.ex. för användarsession. Allt som definieras här är endast tillgängligt för procedurer som anropas via HTTP.
Exempel på inner & outer context
tsimport type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';import {getSessionFromCookie , typeSession } from './auth';import {db } from './db';/*** Defines your inner context shape.* Add fields here that the inner context brings.*/interfaceCreateInnerContextOptions {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 `req`/`res`* - server-side calls where we don't have `req`/`res`** @see https://trpc.io/docs/v11/context#inner-and-outer-context*/export async functioncreateContextInner (opts ?:CreateInnerContextOptions ) {return {db ,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 functioncreateContext (opts :CreateHTTPContextOptions ) {constsession =getSessionFromCookie (opts .req );constcontextInner = awaitcreateContextInner ({session });return {...contextInner ,req :opts .req ,res :opts .res ,};}export typeContext =Awaited <ReturnType <typeofcreateContextInner >>;// The usage in your router is the same as the example above.
tsimport type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';import {getSessionFromCookie , typeSession } from './auth';import {db } from './db';/*** Defines your inner context shape.* Add fields here that the inner context brings.*/interfaceCreateInnerContextOptions {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 `req`/`res`* - server-side calls where we don't have `req`/`res`** @see https://trpc.io/docs/v11/context#inner-and-outer-context*/export async functioncreateContextInner (opts ?:CreateInnerContextOptions ) {return {db ,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 functioncreateContext (opts :CreateHTTPContextOptions ) {constsession =getSessionFromCookie (opts .req );constcontextInner = awaitcreateContextInner ({session });return {...contextInner ,req :opts .req ,res :opts .res ,};}export typeContext =Awaited <ReturnType <typeofcreateContextInner >>;// The usage in your router is the same as the example above.
Det är viktigt att härleda din Context från inner-contexten, eftersom endast det som definieras där verkligen alltid är tillgängligt i dina procedurer.
Om du inte vill kontrollera om req eller res är undefined i dina procedurer hela tiden, kan du bygga en liten återanvändbar procedur för det:
tsexport constapiProcedure =publicProcedure .use ((opts ) => {if (!opts .ctx .req || !opts .ctx .res ) {throw newError ('You are missing `req` or `res` in your call.');}returnopts .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 constapiProcedure =publicProcedure .use ((opts ) => {if (!opts .ctx .req || !opts .ctx .res ) {throw newError ('You are missing `req` or `res` in your call.');}returnopts .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 ,},});});