How the userId is added to the tRPC ctx in LobeChat source code?

In this article, we analyse how the currently logged-in userId is added to the tRPC ctx by reviewing LobeChat source code. I recommend reading this tRPC setup to relate to the concepts explained in this article.

But before that, I want to explain how I found this. I study LobeChat source code and was working on a project that involved tRPC + Supabase in a Next.js app router. I am following the similar codebase architecture to LobeChat’s.

LobeChat uses Clerk for Authentication, the feature I was working required auth wall before making a request to the backend. Since I am following the LobeChat codebase principles, I just had to figure out how LobeChat configured authedProcedure.

When reading open-source code, I always use documentation to set my direction, otherwise I would be lost as these projects massive. This way, at the step — Create a tRPC router, you will find this code below:

import * as trpcNext from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/routers/_app';
// export API handler
// @link https://trpc.io/docs/v11/server/adapters
export default trpcNext.createNextApiHandler({
 router: appRouter,
 createContext: () => ({}),
});

createContext in this documentation returns an object but that is not the case with LobeChat’s createContext. createContext is configured when you define your route, this means we should be looking at trpc/lambda/[trpc]/route.ts

createContext in LobeChat

You will find the createContext in lambda/[trpc]/route.ts. This below code is from LobeChat:

import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import type { NextRequest } from 'next/server';

import { pino } from '@/libs/logger';
import { createContext } from '@/server/context';
import { lambdaRouter } from '@/server/routers/lambda';

const handler = (req: NextRequest) =>
  fetchRequestHandler({
    /**
     * @link https://trpc.io/docs/v11/context
     */
    createContext: () => createContext(req),

    endpoint: '/trpc/lambda',

    onError: ({ error, path, type }) => {
      pino.info(`Error in tRPC handler (lambda) on path: ${path}, type: ${type}`);
      console.error(error);
    },

    req,
    router: lambdaRouter,
  });

export { handler as GET, handler as POST };

As you can see here, createContext is imported from @/server/context. Let’s now analyse @/server/context code. This below source code is picked from @/server/context.

/**
 * Creates context for an incoming request
 * @link https://trpc.io/docs/v11/context
 */
export const createContext = async (request: NextRequest): Promise<Context> => {
   // for API-response caching see https://trpc.io/docs/v11/caching
  const authorization = request.headers.get(LOBE_CHAT_AUTH_HEADER);
  let userId;
  let auth;
  if (enableClerk) {
    auth = getAuth(request);
    userId = auth.userId;
    return createContextInner({ authorizationHeader: authorization, clerkAuth: auth, userId });
   }
// …

In this code snippet, if enableClerk is true, createContextInner is called with auth in parameter object. This is it, this is how you set userId in createContext. userId is returned by auth.userId. createContext
from the definition above only has to return object with the context set.

createContextInner function:

You will find this below code at Line 19 in server/context.ts

/**
 * Inner function for `createContext` where we create the context.
 * This is useful for testing when we don't want to mock Next.js' request/response
 */
export const createContextInner = async (params?: {
 authorizationHeader?: string | null;
 clerkAuth?: ClerkAuth;
 nextAuth?: User;
 userId?: string | null;
}): Promise<AuthContext> => ({
 authorizationHeader: params?.authorizationHeader,
 clerkAuth: params?.clerkAuth,
 nextAuth: params?.nextAuth,
 userId: params?.userId,
});

About us:

At Thinkthroo, we study large open source projects and provide architectural guides. We have developed reusable Components, built with tailwind, that you can use in your project.

We offer Next.js, React and Node development services.

Book a meeting with us to discuss your project.

References:

  1. https://github.com/lobehub/lobe-chat/blob/main/src/server/context.ts#L19

  2. https://github.com/lobehub/lobe-chat/blob/main/src/app/(backend)/trpc/lambda/%5Btrpc%5D/route.ts

  3. https://trpc.io/docs/client/nextjs/setup#3-create-a-trpc-router

  4. https://trpc.io/docs/client/react/server-components