How to set up Clerk with tRPC in app router?
Hi! I'm having trouble setting up Clerk in the t3 boilerplate using app router. I am quite confused it's the first time I'm trying the new architecture, I am doing this as an exercize and I am trying to understand how things are organized.
I think I should be editting the files:
- src/server/api/trpc (this is where the createTRPCCOntext is defined, so I guess I should add the auth object here):
export const createTRPCContext = async (
opts: { headers: Headers },
) => {
return {
db,
//auth: getAuth( ????
...opts,
};
};
- src/trpc/server.ts (here createTRPCContext is wrapped in cache, and I'm not sure how to proceed adding the auth here)
const createContext = cache(() => {
const heads = new Headers(headers());
heads.set("x-trpc-source", "rsc");
return createTRPCContext({
headers: heads,
});
});
- src/app/api/trpc/[trpc]/route.ts (here the conext is actually created I guess)
const createContext = async (req: NextRequest) => {
return createTRPCContext({
headers: req.headers,
//auth: getAuth(req), ??
});
};
So any help understanding all this structure would be greatly appreciated! Thanks!6 Replies
Hey @aspirante, I was trying to implement clerk+trpc today, and as I wanted to double check if I'm not missing something, I saw your question.
I think I figured it out. I used this video by Web Dev Cody as starting point, plus I double checked with docs.
First of all, you are on the right track with using
getAuth
in createTRPCContext
. But this doesn't work, as you don't have opts.req
. Instead, you should use auth()
as can be seen in docs here.
So now my code looks like this:
This should ensure that on publicProcedure
the ctx.auth.userId
is string | null
.
Now I just went ahead and created protectedProcedure
that looks like this:
It's rather simple trpc middlewere, that just check if userId
exists, and if not, throws an error.
You can also see that I'm redefining the ctx.auth
object. This ensures that ctx.auth.userId
is correctly typed as string
. Without it, it would be still string | null
as it is in publicProcedure
.Juraj98 thank you very much for the response. Its still confusing for me how tRPC works and why we have so much files to deal with it. But you helped me a lot! 😛
Its seems like magic, so this auth object is global?
Which auth object?
The one you get with this import
import { auth } from "@clerk/nextjs/server"
in server/trpc
Since Im using tRPC in this way, do I still need to wrap the app layout with the ClerkProvider?So... I think that technically you don't need the
<ClerkProvider/>
, if you're only concerned with auth on server. From docs: The <ClerkProvider> component wraps your React application to provide active session and user context to Clerk's hooks and other components.
I believe the auth
function you use in createTRPCContext
only needs middleware to be included. From [docs]:(https://clerk.com/docs/references/nextjs/auth) The auth() helper does require Middleware.
But personally, I would advise you to not omit the <ClerkProvider/>
. I'd be scared of forgetting I'm missing the provider few months later, and having things not work as I expect them to. You don't lose anything by including it.I see, I naively thought that the tRPC context already included all the functionalities of the <ClerkProvider,/> effectively being just a bigger, unified provider for all communications between server and client I would implement in the app. In that way, the ClerkProvider component would just be redundant. But now I see that is not the way the tRPC is working together with Clerk, thank you very much!