Context in oRPC
oRPC’s context mechanism provides a type-safe dependency injection pattern. It lets you supply required dependencies either explicitly or dynamically through middleware. There are two types:
- Initial Context: Provided explicitly when invoking a procedure.
- Execution Context: Generated during procedure execution, typically by middleware.
Initial Context
Initial context is used to define required dependencies (usually environment-specific) that must be passed when calling a procedure.
const base = os.$context<{ headers: Headers, env: { DB_URL: string } }>()
const getting = base
.handler(async ({ context }) => {
console.log(context.env)
})
export const router = { getting }
When calling that requires initial context, pass it explicitly:
import { RPCHandler } from '@orpc/server/fetch'
const handler = new RPCHandler(router)
export default function fetch(request: Request) {
handler.handle(request, {
context: { // <-- you must pass initial context here
headers: request.headers,
env: {
DB_URL: '***'
}
}
})
}
Execution context
Execution context is computed during the process lifecycle—usually via middleware. It can be used independently or combined with initial context.
import { cookies, headers } from 'next/headers'
const base = os.use(async ({ next }) => next({
context: {
headers: await headers(),
cookies: await cookies(),
},
}))
const getting = base.handler(async ({ context }) => {
context.cookies.set('key', 'value')
})
export const router = { getting }
When using execution context, you don’t need to pass any context manually:
import { RPCHandler } from '@orpc/server/fetch'
const handler = new RPCHandler(router)
export default function fetch(request: Request) {
handler.handle(request) // <-- no need to pass anything more
}
Combining Initial and Execution Context
Often you need both static and dynamic dependencies. Use initial context for environment-specific values (e.g., database URLs) and middleware (execution context) for runtime data (e.g., user authentication).
const base = os.$context<{ headers: Headers, env: { DB_URL: string } }>()
const requireAuth = base.middleware(async ({ context, next }) => {
const user = parseJWT(context.headers.get('authorization')?.split(' ')[1])
if (user) {
return next({ context: { user } })
}
throw new ORPCError('UNAUTHORIZED')
})
const dbProvider = base.middleware(async ({ context, next }) => {
const client = new Client(context.env.DB_URL)
try {
await client.connect()
return next({ context: { db: client } })
}
finally {
await client.disconnect()
}
})
const getting = base
.use(dbProvider)
.use(requireAuth)
.handler(async ({ context }) => {
console.log(context.db)
console.log(context.user)
})