1import{t as n}from"./jsx-runtime-D_bcST6I.js";var s=n(),a={title:"Structuring the GraphQL Request Context",date:"2026-06-09",published:!0,tags:["graphql","node.js","typescript","javascript"],summary:"My approach to shaping an Apollo server's GraphQL context around three fields: a resolved current user, a shared data layer, and a fresh set of DataLoaders.",image:"/blog/structuring-the-graphql-request-context.jpg",thumbnail:"/blog/structuring-the-graphql-request-context.thumb.jpg"};function i(r){const e={a:"a",blockquote:"blockquote",button:"button",code:"code",div:"div",em:"em",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",span:"span",strong:"strong",ul:"ul",...r.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(e.p,{children:["There's a lot written about modeling a GraphQL schema, but less about the other half of standing up a server: ",(0,s.jsx)(e.strong,{children:"what I put into the per-request context every resolver receives."})]}),` 2`,(0,s.jsx)(e.p,{children:"The context is an important design decision in an Apollo server, because every resolver receives it and nothing else ambient is allowed in. It's powerful because a well-shaped context keeps your resolvers small, testable, and free of import-time coupling. It helps avoid reaching for module-level singletons, leaking state between requests, or firing one database query per row."}),` 3`,(0,s.jsx)(e.p,{children:"I'll use a small, recognizable e-commerce domain throughout (users, products, orders, order line items) so the examples stay concrete. My context has exactly three things on it:"}),` 4`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/server/types/RequestContext.ts 5import type { PrismaClient, User } from '@prisma/client'; 6import { DataLoaders } from '@/server/dataLoaders'; 7 8export interface RequestContext { 9 currentUser?: User; 10 prisma: PrismaClient; 11 dataLoaders: DataLoaders; 12}`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/server/types/RequestContext.ts"})}),` 13`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" type"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { PrismaClient, User } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@prisma/client'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 14`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { DataLoaders } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/server/dataLoaders'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 15`,(0,s.jsx)(e.span,{className:"line"}),` 16`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" interface"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" RequestContext"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 17`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
1770"},children:" currentUser"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"?:"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" User"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 18`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" prisma"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" PrismaClient"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 19`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" dataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" DataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 20`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})})]})]})}),` 21`,(0,s.jsxs)(e.ul,{children:[` 22`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.code,{children:"currentUser"}),": who's making the request, resolved from the auth token (or ",(0,s.jsx)(e.code,{children:"undefined"})," if anonymous)"]}),` 23`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.code,{children:"prisma"}),": the Prisma client the resolvers read and write through"]}),` 24`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.code,{children:"dataLoaders"}),": a fresh set of batching loaders, created once per request"]}),` 25`]}),` 26`,(0,s.jsxs)(e.p,{children:["Let's build the server around that shape and then dig into ",(0,s.jsx)(e.em,{children:"why"})," the last two are valuable."]}),` 27`,(0,s.jsx)(e.h2,{children:"The server bootstrap"}),` 28`,(0,s.jsx)(e.p,{children:"The entire server is one file. Here it is, then I'll walk through it:"}),` 29`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/server/index.ts 30import '@/env'; 31import { ApolloServer } from '@apollo/server'; 32import { startStandaloneServer } from '@apollo/server/standalone'; 33import { Prisma, type User } from '@prisma/client'; 34import { GraphQLError } from 'graphql'; 35import { z } from 'zod'; 36import { jwtSecret } from '@/constants/jwt'; 37import { prisma } from '@/db/prisma'; 38import { decorateValidationError } from '@/errors/decorateValidationError'; 39import { RequestContext } from '@/server/types/RequestContext'; 40import { verifyToken } from '@/utils/authToken'; 41import { createDataLoaders } from './dataLoaders'; 42import { schema } from './schema'; 43 44const port = parseInt(process.env.PORT || '3000', 10); 45 46const getToken = (authorizationHeader: string | undefined) => { 47 if (!authorizationHeader) { 48 return undefined; 49 } 50 const [scheme, parameters] = authorizationHeader.split(/ +/, 2); 51 return scheme === 'Bearer' ? parameters : undefined; 52}; 53 54const getCurrentUser = async (token: string | undefined): Promise<User | undefined> => { 55 if (!token) { 56 return undefined; 57 } 58 const payload = verifyToken({ token, secret: jwtSecret }); 59 const user = payload && (await prisma.user.findUnique({ where: { email: payload.email } })); 60 return user ?? undefined; 61}; 62 63const server = new ApolloServer<RequestContext>({ 64 schema, 65 formatError: (formattedError, error) => { 66 const { originalError } = error as GraphQLError; 67 if ( 68 originalError instanceof Prisma.PrismaClientKnownRequestError || 69 originalError instanceof z.ZodError 70 ) { 71 return decorateValidationError(formattedError, originalError); 72 } 73 if (originalError) { 74 console.error(originalError); 75 } 76 return formattedError; 77 }, 78}); 79 80startStandaloneServer(server, { 81 listen: { port }, 82 context: async ({ req }) => ({ 83 currentUser: await getCurrentUser(getToken(req.headers.authorization)), 84 prisma, 85 dataLoaders: createDataLoaders(), 86 }), 87}) 88 .then(({ url }) => { 89 console.log(\`ð Server started: \${url}\`); 90 }) 91 .catch((err) => { 92 throw err; 93 });`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/server/index.ts"})}),` 94`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/env'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 95`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { ApolloServer } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@apollo/server'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 96`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { startStandaloneServer } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@apollo/server/standalone'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 97`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { Prisma, "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"type"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" User } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@prisma/client'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 98`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { GraphQLError } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" 'graphql'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 99`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { z } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" 'zod'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 100`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { jwtSecret } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/constants/jwt'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 101`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { prisma } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/db/prisma'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 102`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { decorateValidationError } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/errors/decorateValidationError'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 103`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { RequestContext } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/server/types/RequestContext'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 104`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { verifyToken } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/utils/authToken'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 105`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { createDataLoaders } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" './dataLoaders'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 106`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { schema } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" './schema'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 107`,(0,s.jsx)(e.span,{className:"line"}),` 108`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" port"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" parseInt"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(process.env."}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"PORT"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ||"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '3000'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"10"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:");"})]}),` 109`,(0,s.jsx)(e.span,{className:"line"}),` 110`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"const"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" getToken"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
11070"},children:"authorizationHeader"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" string"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:") "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 111`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" if"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"!"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"authorizationHeader) {"})]}),` 112`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 113`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 114`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ["}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"scheme"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"parameters"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"] "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" authorizationHeader."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"split"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"/"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" +"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"/"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"2"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:");"})]}),` 115`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" scheme "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"==="}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" 'Bearer'"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ?"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" parameters "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 116`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"};"})}),` 117`,(0,s.jsx)(e.span,{className:"line"}),` 118`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"const"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" getCurrentUser"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
11870"},children:"token"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" string"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Promise"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"<"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"User"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"> "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 119`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" if"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"!"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"token) {"})]}),` 120`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 121`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 122`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" payload"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" verifyToken"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({ token, secret: jwtSecret });"})]}),` 123`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" user"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" payload "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"&&"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"await"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma.user."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"findUnique"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({ where: { email: payload.email } }));"})]}),` 124`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" user "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"??"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 125`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"};"})}),` 126`,(0,s.jsx)(e.span,{className:"line"}),` 127`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" server"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" new"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" ApolloServer"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"<"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"RequestContext"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:">({"})]}),` 128`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" schema,"})}),` 129`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" formatError"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
12970"},children:"formattedError"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"error"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:") "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 130`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"originalError"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" error "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"as"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" GraphQLError"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 131`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" if"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("})]}),` 132`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" originalError "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"instanceof"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Prisma"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"PrismaClientKnownRequestError"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ||"})]}),` 133`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" originalError "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"instanceof"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" z"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"ZodError"})]}),` 134`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ) {"})}),` 135`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" decorateValidationError"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(formattedError, originalError);"})]}),` 136`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 137`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" if"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" (originalError) {"})]}),` 138`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" console."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"error"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(originalError);"})]}),` 139`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 140`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" formattedError;"})]}),` 141`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" },"})}),` 142`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"});"})}),` 143`,(0,s.jsx)(e.span,{className:"line"}),` 144`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"startStandaloneServer"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(server, {"})]}),` 145`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" listen: { port },"})}),` 146`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" context"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ({ "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
14670"},children:"req"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ({"})]}),` 147`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" currentUser: "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"await"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" getCurrentUser"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"getToken"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(req.headers.authorization)),"})]}),` 148`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma,"})}),` 149`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" dataLoaders: "}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"createDataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(),"})]}),` 150`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }),"})}),` 151`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"})"})}),` 152`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"then"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(({ "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"url"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 153`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" console."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"log"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"`ð Server started: ${"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"url"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"}`"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:");"})]}),` 154`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" })"})}),` 155`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"catch"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"err"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:") "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 156`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" throw"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" err;"})]}),` 157`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" });"})})]})]})}),` 158`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.code,{children:"new ApolloServer<RequestContext>"})," is typed with the context interface, so every resolver gets full IntelliSense on ",(0,s.jsx)(e.code,{children:"context"})," and the compiler rejects any resolver that assumes a field the context doesn't actually provide."]}),` 159`,(0,s.jsxs)(e.p,{children:["The piece that runs on every single request is the ",(0,s.jsx)(e.code,{children:"context"})," factory:"]}),` 160`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes g
160ithub-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`context: async ({ req }) => ({ 161 currentUser: await getCurrentUser(getToken(req.headers.authorization)), 162 prisma, 163 dataLoaders: createDataLoaders(), 164}),`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"context"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ({ "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"req"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ({"})]}),` 165`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" currentUser: "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"await"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" getCurrentUser"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"getToken"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(req.headers.authorization)),"})]}),` 166`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma,"})}),` 167`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" dataLoaders: "}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"createDataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(),"})]}),` 168`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}),"})})]})]})}),` 169`,(0,s.jsx)(e.p,{children:"Each line handles one of the three context fields, which I'll dissect throughout the remainder of this post."}),` 170`,(0,s.jsxs)(e.h2,{children:[(0,s.jsx)(e.code,{children:"currentUser"}),": resolved per request"]}),` 171`,(0,s.jsx)(e.p,{children:"Authentication is request-scoped by definition, so it belongs in the context factory and nowhere else. The flow is deliberately small:"}),` 172`,(0,s.jsxs)(e.ol,{children:[` 173`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.code,{children:"getToken"})," pulls the ",(0,s.jsx)(e.code,{children:"Bearer <token>"})," value out of the ",(0,s.jsx)(e.code,{children:"Authorization"})," header (returning ",(0,s.jsx)(e.code,{children:"undefined"})," for anything that isn't a well-formed bearer scheme)."]}),` 174`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.code,{children:"getCurrentUser"})," verifies the JWT and, if valid, looks up the matching user."]}),` 175`]}),` 176`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`const getCurrentUser = async (token: string | undefined): Promise<User | undefined> => { 177 if (!token) { 178 return undefined; 179 } 180 const payload = verifyToken({ token, secret: jwtSecret }); 181 const user = payload && (await prisma.user.findUnique({ where: { email: payload.email } })); 182 return user ?? undefined; 183};`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"const"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" getCurrentUser"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
18370"},children:"token"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" string"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:")"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Promise"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"<"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"User"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"> "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 184`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" if"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"!"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"token) {"})]}),` 185`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 186`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 187`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" payload"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" verifyToken"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({ token, secret: jwtSecret });"})]}),` 188`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" user"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" payload "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"&&"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"await"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma.user."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"findUnique"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({ where: { email: payload.email } }));"})]}),` 189`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" user "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"??"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 190`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"};"})})]})]})}),` 191`,(0,s.jsx)(e.p,{children:"The JWT helper keeps verification honest by validating the decoded payload with Zod, so a malformed or tampered token can't smuggle a bad shape into the rest of the app:"}),` 192`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes g
192ithub-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/utils/authToken.ts 193import jwt from 'jsonwebtoken'; 194import { z } from 'zod'; 195import type { User } from '@prisma/client'; 196 197type TokenPayload = Pick<User, 'id' | 'email'>; 198 199const tokenSchema = z.object({ 200 id: z.string().uuid(), 201 email: z.string(), 202}); 203 204export const verifyToken = ({ token, secret }: { token: string; secret: string }): 205 | TokenPayload 206 | undefined => { 207 try { 208 const payload = jwt.verify(token, secret); 209 return tokenSchema.parse(payload); 210 } catch (err) { 211 return undefined; 212 } 213};`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/utils/authToken.ts"})}),` 214`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" jwt "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" 'jsonwebtoken'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 215`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { z } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" 'zod'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 216`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" type"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { User } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@prisma/client'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 217`,(0,s.jsx)(e.span,{className:"line"}),` 218`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"type"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" TokenPayload"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Pick"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"<"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"User"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'id'"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" 'email'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:">;"})]}),` 219`,(0,s.jsx)(e.span,{className:"line"}),` 220`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" tokenSchema"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" z."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"object"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({"})]}),` 221`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" id: z."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"string"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"}
221,children:"()."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"uuid"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(),"})]}),` 222`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" email: z."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"string"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(),"})]}),` 223`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"});"})}),` 224`,(0,s.jsx)(e.span,{className:"line"}),` 225`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" verifyToken"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ({ token, secret }: { token: string; secret: string }):"})]}),` 226`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" TokenPayload"})]}),` 227`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" |"}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" =>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 228`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" try"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 229`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" payload"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" jwt."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"verify"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(token, secret);"})]}),` 230`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" tokenSchema."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"parse"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(payload);"})]}),` 231`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"catch"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" (err) {"})]}),` 232`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" undefined"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 233`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 234`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"};"})})]})]})}),` 235`,(0,s.jsxs)(e.p,{children:["By the time a resolver runs, ",(0,s.jsx)(e.code,{children:"currentUser"})," is either a fully populated ",(0,s.jsx)(e.code,{children:"User"})," record or ",(0,s.jsx)(e.code,{children:"undefined"}),". Resolvers never parse headers or verify tokens themselves; they just check ",(0,s.jsx)(e.code,{children:"context.currentUser"}),". An authentication middleware can lean on that, and so can a plain resolver:"]}
235),` 236`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":"currentUser: (_parent, _args, { currentUser }) => currentUser ?? null,"})]}),(0,s.jsx)(e.code,{children:(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"currentUser"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"_parent"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"_args"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", { "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"currentUser"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" currentUser "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"??"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" null"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:","})]})})]})}),` 237`,(0,s.jsxs)(e.p,{children:["Notice that ",(0,s.jsx)(e.code,{children:"getCurrentUser"})," already goes through ",(0,s.jsx)(e.code,{children:"prisma.user"}),": even authentication reads through the same client everything else does. Which brings us to the first of the two interesting context fields."]}),` 238`,(0,s.jsxs)(e.h2,{children:[(0,s.jsx)(e.code,{children:"prisma"}),": why pass the client around at all?"]}),` 239`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.code,{children:"prisma"})," is just the generated ",(0,s.jsx)(e.a,{href:"https://www.prisma.io/",children:"Prisma"})," client, instantiated once and shared across the whole process:"]}),` 240`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/db/prisma.ts 241import { PrismaClient } from '@prisma/client'; 242import { isProduction } from '@/env'; 243 244const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; 245 246export const prisma = globalForPrisma.prisma ?? new PrismaClient(); 247 248if (!isProduction) { 249 globalForPrisma.prisma = prisma; 250}`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/db/prisma.ts"})}),` 251`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { PrismaClient } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@prisma/client'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 252`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { isProduction } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/env'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 253`,(0,s.jsx)(e.span,{className:"line"}),` 254`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" globalForPrisma"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" globalThis "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"as"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" unknown"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" as"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
25470"},children:"prisma"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"?:"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" PrismaClient"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" };"})]}),` 255`,(0,s.jsx)(e.span,{className:"line"}),` 256`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" prisma"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" globalForPrisma.prisma "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"??"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" new"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" PrismaClient"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"();"})]}),` 257`,(0,s.jsx)(e.span,{className:"line"}),` 258`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"if"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"!"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"isProduction) {"})]}),` 259`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" globalForPrisma.prisma "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma;"})]}),` 260`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})})]})]})}),` 261`,(0,s.jsxs)(e.p,{children:["The client owns a connection pool and a query engine, so it's meant to be created once and reused. The ",(0,s.jsx)(e.code,{children:"globalThis"})," guard just keeps hot reloads in development from spawning a fresh pool on every code change. The models themselves, and the relations between them, are declared in the Prisma schema rather than wired up in code:"]}),` 262`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"prisma",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"prisma"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// prisma/schema.prisma (abridged) 263model User { 264 id String @id @default(uuid()) 265 email String @unique 266 orders Order[] 267} 268 269model Order { 270 id String @id @default(uuid()) 271 userId String 272 createdAt DateTime @default(now()) 273 user User @relation(fields: [userId], references: [id]) 274 items OrderItem[] 275} 276 277model OrderItem { 278 id String @id @default(uuid()) 279 orderId String 280 productId String 281 quantity Int 282 order Order @relation(fields: [orderId], references: [id]) 283 product Product @relation(fields: [productId], references: [id]) 284} 285 286model Product { 287 id String @id @default(uuid()) 288 name String 289 orderItems OrderItem[] 290}`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// prisma/schema.prisma (abridged)"})}),` 291`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"model"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" User"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 292`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" id "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @id"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @default"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"uuid"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"}
292,children:"())"})]}),` 293`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" email "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @unique"})]}),` 294`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" orders "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"Order"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"[]"})]}),` 295`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})}),` 296`,(0,s.jsx)(e.span,{className:"line"}),` 297`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"model"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Order"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 298`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" id "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @id"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @default"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"uuid"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"())"})]}),` 299`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" userId "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"})]}),` 300`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" createdAt "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"DateTime"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @default"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"now"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"())"})]}),` 301`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" user "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"User"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @relation"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"fields"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ["}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"userId"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"], "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"references"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ["}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"id"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"])"})]}),` 302`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" items "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"OrderItem"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"[]"})]}),` 303`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})}),` 304`,(0,s.jsx)(e.span,{className:"line"}),` 305`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"model"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" OrderItem"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 306`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" id "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @id"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @default"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"uuid"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"}
306,children:"())"})]}),` 307`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" orderId "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"})]}),` 308`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" productId "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"})]}),` 309`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" quantity "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"Int"})]}),` 310`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" order "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"Order"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @relation"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"fields"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ["}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"orderId"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"], "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"references"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ["}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"id"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"])"})]}),` 311`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" product "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"Product"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @relation"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"fields"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ["}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"productId"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"], "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"references"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": ["}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"id"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"])"})]}),` 312`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})}),` 313`,(0,s.jsx)(e.span,{className:"line"}),` 314`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"model"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Product"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 315`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" id "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @id"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" @default"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"uuid"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"())"})]}),` 316`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" name "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"String"})]}),` 317`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" orderItems "}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:"OrderItem"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"[]"})]}),` 318`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})})]})]})}),` 319`,(0,s.jsxs)(e.p,{children:["Running ",(0,s.jsx)(e.code,{children:"prisma generate"})," turns that schema into a fully typed client, with every model hanging off it as a property: ",(0,s.jsx)(e.code,{children:"prisma.user"}),", ",(0,s.jsx)(e.code,{children:"prisma.order"}),", ",(0,s.jsx)(e.code,{children:"prisma.orderItem"}),", each exposing ",(0,s.jsx)(e.code,{children:"findUnique"}),", ",(0,s.jsx)(e.code,{children:"findMany"}),", ",(0,s.jsx)(e.code,{children:"create"}),", and the rest."]}),` 320`,(0,s.jsxs)(e.p,{children:["A reasonable question: ",(0,s.jsx)(e.code,{children:"prisma"})," is a plain module export, so a resolver could just ",(0,s.jsx)(e.code,{children:"import { prisma } from '@/db/prisma'"})," directly. Why thread it through the context as a field at all?"]}),` 321`,(0,s.jsx)(e.p,{children:"A few reasons, in roughly the order I care about them:"}),` 322`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.strong,{children:"1. Testability and seam control."})," When the data layer arrives through the context, a test can construct a context with a stand-in ",(0,s.jsx)(e.code,{children:"prisma"})," (a mock client, or one pointed at a test database) and a resolver doesn't know the difference. Resolvers become pure functions of ",(0,s.jsx)(e.code,{children:"(args, context)"}),": no hidden imports to intercept, no module mocking, no ",(0,s.jsx)(e.code,{children:"jest.mock('@/db/prisma')"})," at the top of every test file. The dependency is ",(0,s.jsx)(e.em,{children:"injected"}),", not reached for."]}),` 323`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.strong,{children:"2. One blessed surface."})," ",(0,s.jsx)(e.code,{children:"context.prisma"})," is the ",(0,s.jsx)(e.em,{children:"only"})," sanctioned way into the database from a resolver. That convention makes data access trivially greppable and gives me one obvious place to add cross-cutting behavior later (query logging, a read-replica router, per
323-tenant scoping, all of which Prisma's client extensions make easy) without editing hundreds of import statements."]}),` 324`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.strong,{children:"3. Consistency with everything else on the context."})," ",(0,s.jsx)(e.code,{children:"currentUser"})," and ",(0,s.jsx)(e.code,{children:"dataLoaders"})," are already request-scoped values handed in via context. Routing database access the same way means a resolver has a single mental model: ",(0,s.jsxs)(e.em,{children:["everything I need comes from ",(0,s.jsx)(e.code,{children:"context"}),"."]}),' No special cases, no "well, the client is different."']}),` 325`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.strong,{children:"4. It documents intent in the type."})," Because ",(0,s.jsx)(e.code,{children:"RequestContext"})," declares ",(0,s.jsx)(e.code,{children:"prisma: PrismaClient"}),", the context's type spells out exactly what a resolver is allowed to reach for. The capability is visible at the type level rather than implied by whatever a file happens to import."]}),` 326`,(0,s.jsxs)(e.p,{children:["In practice, resolvers that need direct, un-batched access (typically mutations and top-level queries) pull ",(0,s.jsx)(e.code,{children:"prisma"})," straight off the context:"]}),` 327`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/server/resolvers/userResolvers.ts 328export const userResolvers = { 329 Mutation: { 330 createUser: async (_parent, { input }, { prisma }) => { 331 const user = await prisma.user.create({ data: input }); 332 const token = createToken({ user, secret: jwtSecret }); 333 return { user, token }; 334 }, 335 createSession: async (_parent, { input }, { prisma }) => { 336 const user = await prisma.user.findUnique({ 337 where: { email: input.email }, 338 }); 339 validateCreateSessionInput(input, user); 340 const token = createToken({ user, secret: jwtSecret }); 341 return { user, token }; 342 }, 343 }, 344} satisfies Resolvers;`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/server/resolvers/userResolvers.ts"})}),` 345`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" userResolvers"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 346`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" Mutation: {"})}),` 347`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" createUser"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"_parent"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", { "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"input"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }, { "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"prisma"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 348`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" user"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" await"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma.user."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"create"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({ data: input });"})]}),` 349`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" token"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" createToken"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({ user, secret: jwtSecret });"})]}),` 350`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { user, token };"})]}),` 351`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" },"})}),` 352`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" createSession"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:": "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
35270"},children:"_parent"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", { "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"input"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }, { "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"prisma"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 353`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" user"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" await"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma.user."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"findUnique"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({"})]}),` 354`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" where: { email: input.email },"})}),` 355`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" });"})}),` 356`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" validateCreateSessionInput"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(input, user);"})]}),` 357`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" token"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" createToken"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({ user, secret: jwtSecret });"})]}),` 358`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { user, token };"})]}),` 359`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" },"})}),` 360`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" },"})}),` 361`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"} "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"satisfies"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Resolvers"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]})]})]})}),` 362`,(0,s.jsxs)(e.p,{children:["Reaching for ",(0,s.jsx)(e.code,{children:"prisma"})," directly is the right tool when you're loading or writing a single, known record. It is ",(0,s.jsx)(e.em,{children:"not"})," the right tool when you're resolving the same kind of related record once per item in a list, and that's where the third context field earns its place."]}),` 363`,(0,s.jsxs)(e.h2,{children:[(0,s.jsx)(e.code,{children:"dataLoaders"}),": why a fresh batch per request?"]}),` 364`,(0,s.jsxs)(e.p,{children:["The classic GraphQL performance trap is the ",(0,s.jsx)(e.strong,{children:"N+1 query problem"}),". Imagine a query that returns 50 order items and asks for each item's parent order:"]}),` 365`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"graphql",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"graphql"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`query { 366 orderItems { 367 id 368 quantity 369 order { 370 id 371 createdAt 372 } 373 } 374}`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"query"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 375`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
37570"},children:" orderItems"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 376`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" id"})}),` 377`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" quantity"})}),` 378`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" order"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 379`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" id"})}),` 380`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" createdAt"})}),` 381`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 382`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }"})}),` 383`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})})]})]})}),` 384`,(0,s.jsxs)(e.p,{children:["The naive ",(0,s.jsx)(e.code,{children:"OrderItem.order"})," resolver runs once per item. Fifty items means one query to fetch the items, then fifty more queries to fetch each order individually: 51 round-trips to the database for what should be two. With deeper nesting it gets quadratically worse."]}),` 385`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.a,{href:"https://github.com/graphql/dataloader",children:"DataLoader"})," fixes this by ",(0,s.jsx)(e.strong,{children:"batching"})," and ",(0,s.jsx)(e.strong,{children:"caching"})," within a single request. Instead of querying immediately, each ",(0,s.jsx)(e.code,{children:".load(id)"})," call registers the id; DataLoader collects every id requested during the same tick of the event loop and resolves them all in one batched query. It also memoizes: ask for the same id twice and the second call returns the cached result."]}),` 386`,(0,s.jsxs)(e.blockquote,{children:[` 387`,(0,s.jsxs)(e.p,{children:["Prisma does some batching of its own: it transparently coalesces multiple ",(0,s.jsx)(e.code,{children:"findUnique"})," calls made in the same tick into a single query. That overlaps with DataLoader but doesn't replace it. Prisma's batching only covers ",(0,s.jsx)(e.code,{children:"findUnique"})," lookups by id, not the filtered ",(0,s.jsx)(e.code,{children:"findMany"})," loads below, and it doesn't memoize across resolver paths within a request. The two are complementary."]}),` 388`]}),` 389`,(0,s.jsx)(e.p,{children:"I create one set of loaders per request:"}),` 390`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/server/dataLoaders/index.ts 391import { createOrderLoader } from './createOrderLoader'; 392import { createProductLoader } from './createProductLoader'; 393 394export const createDataLoaders = () => ({ 395 orderLoader: createOrderLoader(), 396 productLoader: createProductLoader(), 397}); 398 399export type DataLoaders = ReturnType<typeof createDataLoaders>;`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/server/dataLoaders/index.ts"})}),` 400`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { createOrderLoader } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" './createOrderLoader'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 401`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { createProductLoader } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" './createProductLoader'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 402`,(0,s.jsx)(e.span,{className:"line"}),` 403`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" createDataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" () "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ({"})]}),` 404`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" orderLoader: "}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"createOrderLoader"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"}
404,children:"(),"})]}),` 405`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" productLoader: "}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"createProductLoader"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(),"})]}),` 406`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"});"})}),` 407`,(0,s.jsx)(e.span,{className:"line"}),` 408`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" type"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" DataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" ReturnType"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"<"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"typeof"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" createDataLoaders>;"})]})]})]})}),` 409`,(0,s.jsxs)(e.p,{children:["A single loader is just a batch function: given a list of ids, return the records in the ",(0,s.jsx)(e.em,{children:"same order"}),", with a slot for every id even if it wasn't found."]}),` 410`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes github-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/server/dataLoaders/createOrderLoader.ts 411import DataLoader from 'dataloader'; 412import type { Order } from '@prisma/client'; 413import { prisma } from '@/db/prisma'; 414 415export const createOrderLoader = () => 416 new DataLoader(async (ids: readonly Order['id'][]) => { 417 const orders = await prisma.order.findMany({ 418 where: { id: { in: [...ids] } }, 419 }); 420 return ids.map((id) => orders.find((order) => order.id === id)); 421 });`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/server/dataLoaders/createOrderLoader.ts"})}),` 422`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" DataLoader "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" 'dataloader'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 423`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" type"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { Order } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@prisma/client'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 424`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"import"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" { prisma } "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"from"}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:" '@/db/prisma'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 425`,(0,s.jsx)(e.span,{className:"line"}),` 426`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" createOrderLoader"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" () "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"})]}),` 427`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" new"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" DataLoader"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB
42770"},children:"ids"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" readonly"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Order"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"["}),(0,s.jsx)(e.span,{style:{color:"#032F62","--shiki-dark":"#9ECBFF"},children:"'id'"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"][]) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 428`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" orders"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" await"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" prisma.order."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"findMany"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"({"})]}),` 429`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" where: { id: { in: ["}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"..."}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"ids] } },"})]}),` 430`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" });"})}),` 431`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ids."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"map"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"id"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:") "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" orders."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"find"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"order"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:") "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" order.id "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"==="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" id));"})]}),` 432`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" });"})})]})]})}),` 433`,(0,s.jsxs)(e.p,{children:["The contract DataLoader requires is subtle but important: the array you return must be ",(0,s.jsxs)(e.strong,{children:["exactly the same length as ",(0,s.jsx)(e.code,{children:"ids"}),", in the same order"]}),". That's why I map back over ",(0,s.jsx)(e.code,{children:"ids"})," and ",(0,s.jsx)(e.code,{children:"find"})," the matching row rather than just returning whatever ",(0,s.jsx)(e.code,{children:"findMany"})," produced, since the batch may come back in any order, and some ids may have no row at all."]}),` 434`,(0,s.jsxs)(e.blockquote,{children:[` 435`,(0,s.jsxs)(e.p,{children:["A nice bonus of routing loads through a function like this: the batch function is the natural place to shape each record before it reaches the schema, whether that's pulling in a relation with ",(0,s.jsx)(e.code,{children:"include"})," or mapping a derived field like ",(0,s.jsx)(e.code,{children:"lineTotal"})," onto every row, so resolvers receive records already in the form the schema expects."]}),` 436`]}),` 437`,(0,s.jsxs)(e.p,{children:["Now the ",(0,s.jsx)(e.code,{children:"OrderItem.order"})," resolver becomes a one-liner that participates in batching automatically:"]}),` 438`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes g
438ithub-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`// src/server/resolvers/orderItemResolvers.ts 439export const orderItemResolvers = { 440 OrderItem: { 441 order: composeResolvers(isAuthenticated(), async (orderItem, _args, { dataLoaders }) => { 442 const order = await dataLoaders.orderLoader.load(orderItem.orderId); 443 assertTruthy(order); 444 return order; 445 }), 446 }, 447} satisfies Resolvers;`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#6A737D","--shiki-dark":"#6A737D"},children:"// src/server/resolvers/orderItemResolvers.ts"})}),` 448`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" orderItemResolvers"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 449`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" OrderItem: {"})}),` 450`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" order: "}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"composeResolvers"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"("}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"isAuthenticated"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(), "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"async"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" ("}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"orderItem"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"_args"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:", { "}),(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:"dataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }) "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"=>"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 451`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" const"}),(0,s.jsx)(e.span,{style:{color:"#005CC5","--shiki-dark":"#79B8FF"},children:" order"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" ="}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" await"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" dataLoaders.orderLoader."}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:"load"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(orderItem.orderId);"})]}),` 452`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" assertTruthy"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"(order);"})]}),` 453`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" return"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" order;"})]}),` 454`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" }),"})}),` 455`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" },"})}),` 456`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"} "}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"satisfies"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" Resolvers"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]})]})]})}),` 457`,(0,s.jsxs)(e.p,{children:["Fifty order items now produce ",(0,s.jsx)(e.strong,{children:"one"})," batched ",(0,s.jsx)(e.code,{children:"WHERE id IN (...)"})," query for their orders instead of fifty. The resolver code reads as if it's loading a single order; DataLoader handles the coalescing underneath."]}),` 458`,(0,s.jsxs)(e.h3,{children:["Why ",(0,s.jsx)(e.em,{children:"per request"}
458),", and not a singleton?"]}),` 459`,(0,s.jsxs)(e.p,{children:["This detail is easy to overlook, so it's worth stating plainly: ",(0,s.jsx)(e.strong,{children:"the loaders are created inside the context factory"}),", which means a brand-new set is built for every request. That's deliberate, and the per-request lifetime is exactly what makes them safe."]}),` 460`,(0,s.jsxs)(e.p,{children:["DataLoader caches by id ",(0,s.jsx)(e.em,{children:"for the lifetime of the loader"}),". That caching is a feature within one request (don't load the same order twice while resolving one query) and a ",(0,s.jsx)(e.strong,{children:"bug"})," if it outlives the request:"]}),` 461`,(0,s.jsxs)(e.ul,{children:[` 462`,(0,s.jsxs)(e.li,{children:["A long-lived, shared loader would happily serve ",(0,s.jsx)(e.strong,{children:"stale data"}),": once an order is cached, a later request would keep seeing the old version even after it changed."]}),` 463`,(0,s.jsxs)(e.li,{children:["It would leak data ",(0,s.jsx)(e.strong,{children:"across users and requests"}),". Request A loads order ",(0,s.jsx)(e.code,{children:"123"}),"; the cache now holds it; request B (possibly a different user) calls ",(0,s.jsx)(e.code,{children:".load('123')"})," and gets A's cached copy, sidestepping any fresh authorization or scoping."]}),` 464`]}),` 465`,(0,s.jsxs)(e.p,{children:["Creating the loaders per request gives each request its own short-lived cache that is born and discarded with the request. You get the batching and de-duplication benefits exactly where they're safe, and none of the cross-request hazards. ",(0,s.jsx)(e.code,{children:"prisma"}),", by contrast, holds no per-request state (just a connection pool that's built to be shared), so it's instantiated once and the same reference is handed to every context. The split is intentional: ",(0,s.jsx)(e.strong,{children:"share the stateless data layer, isolate the stateful cache."})]}),` 466`,(0,s.jsx)(e.h2,{children:"The whole picture"}),` 467`,(0,s.jsxs)(e.p,{children:["Standing up the server comes down to one typed ",(0,s.jsx)(e.code,{children:"ApolloServer<RequestContext>"})," and a context factory that, on every request:"]}),` 468`,(0,s.jsxs)(e.ol,{children:[` 469`,(0,s.jsxs)(e.li,{children:[(0,s.jsxs)(e.strong,{children:["Resolves ",(0,s.jsx)(e.code,{children:"currentUser"})]})," from the bearer token, so authorization is just a property check."]}),` 470`,(0,s.jsxs)(e.li,{children:[(0,s.jsxs)(e.strong,{children:["Hands over ",(0,s.jsx)(e.code,{children:"prisma"})]})," (the shared, stateless client) as the single, injectable, type-documented door to the database."]}),` 471`,(0,s.jsxs)(e.li,{children:[(0,s.jsxs)(e.strong,{children:["Builds a fresh ",(0,s.jsx)(e.code,{children:"dataLoaders"})]})," set, so related records batch into single queries and the per-request cache can't leak across requests."]}),` 472`]}),` 473`,(0,s.jsxs)(e.p,{children:[(0,s.jsx)(e.code,{children:"prisma"})," is shared because it holds no request state; ",(0,s.jsx)(e.code,{children:"dataLoaders"})," is rebuilt because it holds exactly the kind of state that must not outlive the request. Keep those two facts straight and the context does its job: every resolver is a small, testable function of ",(0,s.jsx)(e.code,{children:"(args, context)"}),", with nothing ambient hiding in the imports."]}),` 474`,(0,s.jsx)(e.h2,{children:"Room for improvement"}),` 475`,(0,s.jsxs)(e.p,{children:["This design has one more step in it, and it's the one I'd take next: drop ",(0,s.jsx)(e.code,{children:"prisma"})," from the context entirely and route ",(0,s.jsx)(e.em,{children:"every"})," database read through a loader."]}),` 476`,(0,s.jsxs)(e.p,{children:["Right now the context offers two doors to the database. Mutations and top-level queries reach for ",(0,s.jsx)(e.code,{children:"prisma"})," directly; relation fields go through ",(0,s.jsx)(e.code,{children:"dataLoaders"}),". That split works, but it leaves a gap that's easy to fall into: nothing stops a resolver from calling ",(0,s.jsx)(e.code,{children:"prisma.order.findUnique"})," inside a ",(0,s.jsx)(e.code,{children:".map"})," over a list and quietly reintroducing the exact N+1 problem the loaders exist to prevent. The batched path is ",(0,s.jsx)(e.em,{children:"available"})," when it should be ",(0,s.jsx)(e.em,{children:"mandatory"}),"."]}),` 477`,(0,s.jsxs)(e.p,{children:["So make ",(0,s.jsx)(e.code,{children:"dataLoaders.order.load(id)"})," the only way to read a record. Every keyed lookup is then batched and accounted for by construction, an accidental N+1 takes real effort to write, and the context shrinks back to two honest fields:"]}),` 478`,(0,s.jsx)(s.Fragment,{children:(0,s.jsxs)(e.pre,{className:"shiki shiki-themes g
478ithub-light github-dark",style:{backgroundColor:"#fff","--shiki-dark-bg":"#24292e",color:"#24292e","--shiki-dark":"#e1e4e8"},tabIndex:"0","data-language":"typescript",children:[(0,s.jsxs)(e.div,{className:"code-block-header",children:[(0,s.jsx)(e.span,{className:"font-mono text-xs text-gray-600",children:"typescript"}),(0,s.jsx)(e.button,{className:"copy-code-btn ml-2",title:"Copy code","data-code":`export interface RequestContext { 479 currentUser?: User; 480 dataLoaders: DataLoaders; 481}`})]}),(0,s.jsxs)(e.code,{children:[(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"export"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:" interface"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" RequestContext"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:" {"})]}),` 482`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" currentUser"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:"?:"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" User"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 483`,(0,s.jsxs)(e.span,{className:"line",children:[(0,s.jsx)(e.span,{style:{color:"#E36209","--shiki-dark":"#FFAB70"},children:" dataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#D73A49","--shiki-dark":"#F97583"},children:":"}),(0,s.jsx)(e.span,{style:{color:"#6F42C1","--shiki-dark":"#B392F0"},children:" DataLoaders"}),(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:";"})]}),` 484`,(0,s.jsx)(e.span,{className:"line",children:(0,s.jsx)(e.span,{style:{color:"#24292E","--shiki-dark":"#E1E4E8"},children:"}"})})]})]})}),` 485`,(0,s.jsxs)(e.p,{children:["with ",(0,s.jsx)(e.code,{children:"prisma"})," demoted to an implementation detail that only the loaders module is allowed to import."]}),` 486`,(0,s.jsx)(e.p,{children:"This isn't free, but the cost is bounded and worth paying:"}),` 487`,(0,s.jsxs)(e.ul,{children:[` 488`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.strong,{children:"Writes still need the client."})," A loader batches reads; it can't ",(0,s.jsx)(e.code,{children:"create"})," or ",(0,s.jsx)(e.code,{children:"update"}),". So ",(0,s.jsx)(e.code,{children:"prisma"})," doesn't disappear so much as move behind a small set of mutation helpers, each of which ",(0,s.jsx)(e.code,{children:"prime"}),"s or ",(0,s.jsx)(e.code,{children:"clear"}),"s the relevant loader afterward so a later read in the same request never serves the pre-write cache."]}),` 489`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.strong,{children:"Filtered and paginated queries don't reduce to a single key."})," A top-level ",(0,s.jsx)(e.code,{children:"orders(filter, page)"})," query isn't a ",(0,s.jsx)(e.code,{children:".load(id)"}),"; it's an arbitrary ",(0,s.jsx)(e.code,{children:"findMany"}),". Those live on the same explicit helpers as the writes, while every by-key entity fetch, which is where N+1 actually bites, goes through a loader."]}),` 490`,(0,s.jsxs)(e.li,{children:[(0,s.jsx)(e.strong,{children:"More loaders to write."})," Each access pattern becomes its own loader. That's more boilerplate, but it's boilerplate that names every access pattern in one place and drags it into code review, which is most of the point."]}),` 491`]}),` 492`,(0,s.jsxs)(e.p,{children:["The principle underneath all three is simple: the fewer raw ",(0,s.jsx)(e.code,{children:"prisma"})," calls a resolver can make, the harder it is to bring back the N+1 problem the context was built to solve. Push it to the end and a resolver can't touch the database in an unbatched way even if it tries, and that's exactly the guarantee I want the context to make."]})]})}function o(r={}){const{wrapper:e}=r.components||{};return e?(0,s.jsx)(e,{...r,children:(0,s.jsx)(i,{...r})}):i(r)}export{o as default,a as frontmatter};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.