0

I'm having an issue where my tRPC-configuration cannot access the express session on the request object.

I am using passport.js with google and facebook providers, and on any normal http-route (not on the tRPC router), I get the userinfo when calling req.user.

app.ts:

import * as trpc from '@trpc/server';
import * as trpcExpress from '@trpc/server/adapters/express';

const appRouter = trpc
    .router()
    .mutation('addTodo', {
        input: z.string(),
        resolve: ({input, ctx}) => {
            // Add a todo
        },
    });

const app = express();
app.use(
    session({
        secret: 'use an env-variable here',
    }),
);
app.use(passport.initialize());
app.use(passport.session());

app.use(
    '/trpc',
    trpcExpress.createExpressMiddleware({
        router: appRouter,
        createContext: (ctx: trpcExpress.CreateExpressContextOptions) => {
            // === HERE LIES THE ISSUE ===
            console.log(ctx.req.user);
            //                   ^ THIS RETURNS UNDEFINED
            return ctx;
        },
    }),
);

app.get("ping", (req, res) => {
    console.log(req.user);
    //                ^ THIS RETURNS THE USER
    res.send("pong");
})

It would be easy to say that tRPC doesn't support give you the user, but there must be some sort of workaround, right?

3 Answers3

0

im not sure how passport works, as i usually work with express sessions, but maybe the concept will be the same

function createContext(opts: trpcExpress.CreateExpressContextOptions) {
  let user = {};
  if (opts.req.session.user) {
    user = opts.req.session.user;
  }

  return {
    user
  };
}

type Context = inferAsyncReturnType<typeof createContext>;
const t = initTRPC.context<Context>().create();
const appRouter = t.router({
  hello: t.procedure.query(({ ctx, input }) => {
    console.log("user", ctx.req?.session?.user);
    console.log({ input });
    return "Hello world";
  }),

  session: t.procedure.query(({ ctx, input }) => {
    console.log({ input });
    ctx.req.session.user = { name: "jane" };
    return "session created";
  }),
});

app.use(
  "/trpc",
  trpcExpress.createExpressMiddleware({ router: appRouter, createContext })
);

tldr: check the req.session object for the user and then add it to the object the context returns

Ange Uwase
  • 561
  • 4
  • 10
0

I had a similar issue, albeit with typescript. I noticed the request types were different and had to type them differently to match, so I could have my session available.

If you are using typescript, you can type your context like this

import { CreateExpressContextOptions } from '@trpc/server/adapters/express'
import { Request } from 'express'
import session from 'express-session'

// create my session type by extending the session type from 'express-session' and adding my userId on it
export type Session = session.Session &
  Partial<session.SessionData> & { userId?: string }

// Replace the req object from trpc's CreateExpressContextOptions with the express request and add my session onto it
type ExpressRequest = Omit<CreateExpressContextOptions, 'req'> & {
  req: Request & { session: Session }
}

export const createContext = ({
  req,
  res,
}: ExpressRequest) => {
  return {
    req,
    res,
  }
}

export type Context = inferAsyncReturnType<typeof createContext>
Chano
  • 41
  • 1
  • 5
0

I added this to my client-side fetcher used by trpc

{
  credentials: "include"
}