oRPC
background

Context

Typesafe dependency injection pattern for oRPC.

Introduction

In oRPC, context is a mechanism for managing dependencies and data throughout your application. Context comes in two forms:

  • Initial Context: Supplied explicitly when invoking a procedure or router.
  • Middleware Context: Created or modified by middleware, injected automatically during execution.

Whenever possible, prefer Middleware Context. It simplifies procedure and router invocations by reducing the need to supply context manually.

import {  } from '@orpc/server'
import {  } from 'zod'
import {  } from 'next/headers'
 
type  = { : 'fake-db', ?: { : string } }
const  = .<>() // define `initial context`
 
const  = .<>().(async (, , ) => {
    const  = await ()
 
    // the `user` is `middleware context`, because it is created by middleware
    return .({
        : {
            : .('Authorization') ? { : 'example' } : ,
        }
    })
})
 
export const  = .({
    : .((, , ) => {
        // ^ context is combined from `initial context` and `middleware context`
    }),
})

Middleware Context

Middleware context is the context that is created or modified by middleware. If your procedure only depends on Middleware Context, you can call it or use it as a Server Action directly.

import { ,  } from '@orpc/server'
import {  } from 'next/headers'
 
const  = .(async (, , ) => {
    return .({
        : {
            : 'fake-db',
        }
    })
})
 
const  = .(async (, , ) => {
    const  = await ()
    const  = .('Authorization') ? { : 'example' } : 
 
    if (!) {
        throw new ({ : 'UNAUTHORIZED' })
    }
 
    return .({
        : {
            ,
        }
    })
})
 
export const  = .({
    : 
        .()
        .((, , ) => {
            // ^ context is fully typed
        }),
})
 
// You can call this procedure directly without manually providing context
const  = await .()
 
import { ,  } from '@orpc/server/fetch'
import { ,  } from '@orpc/openapi/fetch'
 
export function (: Request) {
    // No need to pass context; middleware handles it
    return ({
        ,
        ,
        : [
            (), 
            ()
        ],
    })
}

Initial Context

Initial Context is explicitly provided when invoking a procedure or router. This pattern is useful for server-side applications where dependencies can be initialized during each request, rather than relying on global mechanisms like headers or cookies in Next.js.

import { , ,  } from '@orpc/server'
import { ,  } from '@orpc/server/fetch'
import { ,  } from '@orpc/openapi/fetch'
 
type  = { ?: { : string }, : 'fake-db' }
 
const  = .<>()
 
const  = .((, , ) => {
    if(!.) {
        throw new ({ : 'UNAUTHORIZED' })
    }
 
    return .({
        : {
            : .
        }
    })
})
 
export const  = .({
    : 
        .()
        .((, , ) => {
            // ^ context is fully typed
        }),
})
 
export function (: Request) {
    // Initialize context explicitly for each request
    const  = 'fake-db' as 
    const  = ..('Authorization') ? { : 'example' } : 
 
    return ({
        ,
        ,
        : { ,  },
        : [(), ()],
    })
}
 
// If you want to call this procedure or use as server action
//  you must create another caller with context by using `createProcedureCaller` or `createRouterCaller`
const  = ({
    : .,
    : async () => {
        // some logic to create context
        return { 
            : 'fake-db' as ,
            : { : 'example' },
         }
    },
})
 
const  = await ()

Summary

  • Middleware Context is managed by middleware and automatically applied, requiring no additional input during invocation.
  • Initial Context must be explicitly provided when invoking a procedure or router.
  • Combining both types of context allows for a powerful, type-safe way to manage dependencies in your application.

On this page