2

I am trying to put middleware into its own function but I am struggling to get proper typescript typing on it.

At the moment I am typing the middleware as follows, but this isn't ideal because the type information of context and metadata are lost after returning from the middleware.

Inside the middleware

import { MiddlewareFunction } from "@trpc/server/dist/declarations/src/internals/middlewares";
import { TRPCError } from "@trpc/server";


export const authMiddleware : MiddlewareFunction<any, any, any> = async ({ ctx, next, path, rawInput, type, meta }) => {
  if (!meta?.auth)
    return next();

  // some random logic

  return next();
}

And this is how I want to consume it

createRouter()
  .middleware(authMiddleware)
  .mutation('', {
    meta: {
      auth: "user",
      appCheck: true
    },
    input: object({
      workshopId: idSchema,
    }),
    resolve: async ({ input, ctx, type }) => {
    // Here ctx has been widened to any
    // ...

Thank you in advance.

manwingbb
  • 31
  • 5

3 Answers3

2
const t = initTRPC.context<Context>().create();
const middleware = t.middleware;

const authMiddleware = t.middleware(({ next, ctx }) => {
  if (!ctx.session) {
    throw new TRPCError({
      code: "UNAUTHORIZED",
    });
  }
  return next({
    ctx: {
      // Infers the `session` as non-nullable
      session: ctx.session,
    },
  });
})
Anass
  • 21
  • 1
1

I had the same problem (trpc v9.27), so I took the type directly from the middleware method on the trpc router.

import * as trpc from "@trpc/server"

// context for router
const createContext = async () => ({hi: "hi"})

type Context = trpc.inferAsyncReturnType<typeof createContext>

// router initialization function
const trpcRouter = () => trpc.router<Context>()

const router = trpcRouter()
// don't forget to add the return type for your middleware
// in the generic
const init = router.middleware<Context>
type MiddlewareFunction = Parameters<typeof init>[0]

// now use for your middlewares
const myMiddleware: MiddlewareFunction = ({next, ctx}) => {
    console.log("my context", ctx)
    next()
}

// go nuts and use it where you want
export const myCoolRoutes = router
    .middleware(myMiddleware)
    .query("coolio", ({ctx}) => true)

Do note that if you plan on mutating the context inside one of your middleware functions the router.middleware generic should reflect that. For example, if you want to add keys to your context in a middleware you'd define a new context type that adds your desired keys:

// add your keys to context
type keys = {newKey: number}
const mutatingMiddleware = router.middleware<Context & keys>
type Mutate = Parameters<typeof mutatingMiddleware>[0]

const mutatator: Mutate = ({ctx, next}) => {
    return next({ctx: {...ctx, newKey: 0}})
}

Hope this helps.

gr-dev
  • 11
  • 1
0

This is how I did it

import { MiddlewareFunction, ProcedureParams } from '@trpc/server'


export type BaseMiddlewareFunction<$ContextIn> = MiddlewareFunction<
  { _ctx_out: $ContextIn } & ProcedureParams,
  ProcedureParams
>



export const myMiddleWareFunc: BaseMiddlewareFunction<
  {
    req?: Request
  }
> = (opts) => {
  const req =  opts.ctx.req 

  return opts.next({ctx: {
    ...opts.ctx, 
    'foo': 'bar'
  }})
}

a11hard
  • 1,904
  • 4
  • 19
  • 41