Skip to content

Middleware in oRPC ​

Middleware is a powerful feature in oRPC that enables reusable and extensible procedures. It allows you to:

  • Intercept, hook into, or listen to a handler's execution.
  • Inject or guard the execution context.

Overview ​

Middleware is a function that takes a next function as a parameter and either returns the result of next or modifies the result before returning it.

ts
const 
authMiddleware
=
os
.
$context
<{
something
?: string }>() // <-- define dependent-context
.
middleware
(async ({
context
,
next
}) => {
// Execute logic before the handler const
result
= await
next
({
context
: { // Pass additional context
user
: {
id
: 1,
name
: 'John' }
} }) // Execute logic after the handler return
result
}) const
example
=
os
.
use
(
authMiddleware
)
.
handler
(async ({
context
}) => {
const
user
=
context
.
user
})

Dependent context ​

Before .middleware, you can .$context to specify the dependent context, which must be satisfied when the middleware is used.

Inline Middleware ​

Middleware can be defined inline within .use, which is useful for simple middleware functions.

ts
const example = os
  .use(async ({ context, next }) => {
    // Execute logic before the handler
    return next()
  })
  .handler(async ({ context }) => {
    // Handler logic
  })

Middleware Context ​

Middleware can be used to inject or guard the context.

ts
const 
setting
=
os
.
use
(async ({
context
,
next
}) => {
return
next
({
context
: {
auth
: await
auth
() // <-- inject auth payload
} }) }) .
use
(async ({
context
,
next
}) => {
if (!
context
.
auth
) { // <-- guard auth
throw new
ORPCError
('UNAUTHORIZED')
} return
next
({
context
: {
auth
:
context
.
auth
// <-- override auth
} }) }) .
handler
(async ({
context
}) => {
console
.
log
(
context
.
auth
) // <-- access auth
})

INFO

When you pass additional context to next, it will be merged with the existing context.

Middleware Input ​

Middleware can access input, enabling use cases like permission checks.

ts
const canUpdate = os.middleware(async ({ context, next }, input: number) => {
  // Perform permission check
  return next()
})

const ping = os
  .input(z.number())
  .use(canUpdate)
  .handler(async ({ input }) => {
    // Handler logic
  })

// Mapping input if necessary
const pong = os
  .input(z.object({ id: z.number() }))
  .use(canUpdate, input => input.id)
  .handler(async ({ input }) => {
    // Handler logic
  })

INFO

You can adapt a middleware to accept a different input shape by using .mapInput.

ts
const canUpdate = os.middleware(async ({ context, next }, input: number) => {
  return next()
})

// Transform middleware to accept a new input shape
const mappedCanUpdate = canUpdate.mapInput((input: { id: number }) => input.id)

Middleware Output ​

Middleware can also modify the output of a handler, such as implementing caching mechanisms.

ts
const cacheMid = os.middleware(async ({ context, next, path }, input, output) => {
  const cacheKey = path.join('/') + JSON.stringify(input)

  if (db.has(cacheKey)) {
    return output(db.get(cacheKey))
  }

  const result = await next({})

  db.set(cacheKey, result.output)

  return result
})

Concatenation ​

Multiple middleware functions can be combined using .concat.

ts
const concatMiddleware = aMiddleware
  .concat(os.middleware(async ({ next }) => next()))
  .concat(anotherMiddleware)

INFO

If you want to concatenate two middlewares with different input types, you can use .mapInput to align their input types before concatenation.

Built-in Middlewares ​

oRPC provides some built-in middlewares that can be used to simplify common use cases.

ts
import { onError, onFinish, onStart, onSuccess } from '@orpc/server'

const ping = os
  .use(onStart(() => {
    // Execute logic before the handler
  }))
  .use(onSuccess(() => {
    // Execute when the handler succeeds
  }))
  .use(onError(() => {
    // Execute when the handler fails
  }))
  .use(onFinish(() => {
    // Execute logic after the handler
  }))
  .handler(async ({ context }) => {
    // Handler logic
  })

Released under the MIT License.