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 me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on an interesting project. Send me an email at ramu.narasinga@gmail.com
My Github - https://github.com/ramu-narasinga
My website - https://ramunarasinga.com
My Youtube channel - https://www.youtube.com/@thinkthroo
Learning platform - https://thinkthroo.com
Codebase Architecture - https://app.thinkthroo.com/architecture
Best practices - https://app.thinkthroo.com/best-practices
Production-grade projects - https://app.thinkthroo.com/production-grade-projects
Top comments (0)