Configuring Clerk on the latest release of t3 with tRPC

Could anyone share a working example of configuring Clerk on the latest release of t3 with tRPC? I'm trying to follow Theo's t3 tutorial but Clerk and t3 have evolved enough since the recording to get me confused what would be the best practice. I am particularly interested in basic examples of trpc.ts and middleware.ts files.
87 Replies
CsarChvz
CsarChvz12mo ago
Please! Integrate clerk with the new t3 app router! trpc.ts:/
Nico
Nico12mo ago
I put out a demo doing this yesterday - https://x.com/nicoalbanese10/status/1721560882994377087?s=20
CsarChvz
CsarChvz12mo ago
Big shout to nico! Ty man I did this repo https://github.com/CsarChvz/t3-appRouter-mantine-clerk-trpc
GitHub
GitHub - CsarChvz/t3-appRouter-mantine-clerk-trpc
Contribute to CsarChvz/t3-appRouter-mantine-clerk-trpc development by creating an account on GitHub.
Nico
Nico12mo ago
nice!
vibbin
vibbin12mo ago
And idea how to get Clerk auth to flow through into trpc context? I'm getting null values when trying to access my Clerk user details
import { initTRPC, TRPCError } from "@trpc/server";
import { type NextRequest } from "next/server";
import superjson from "superjson";
import { ZodError } from "zod";
import type {
SignedInAuthObject,
SignedOutAuthObject,
} from "@clerk/nextjs/server";
import { getAuth } from "@clerk/nextjs/server";

import { db } from "~/server/db";


interface CreateContextOptions {
req: NextRequest;
auth?: SignedInAuthObject | SignedOutAuthObject;
}

export const createInnerTRPCContext = (opts: CreateContextOptions) => {
const auth = getAuth(opts.req);
console.log(auth);

return {
...opts,
auth,
db,
};
};

export const createTRPCContext = (opts: { req: NextRequest }) => {
// Fetch stuff that depends on the request

return createInnerTRPCContext({
req: opts.req,
});
};
import { initTRPC, TRPCError } from "@trpc/server";
import { type NextRequest } from "next/server";
import superjson from "superjson";
import { ZodError } from "zod";
import type {
SignedInAuthObject,
SignedOutAuthObject,
} from "@clerk/nextjs/server";
import { getAuth } from "@clerk/nextjs/server";

import { db } from "~/server/db";


interface CreateContextOptions {
req: NextRequest;
auth?: SignedInAuthObject | SignedOutAuthObject;
}

export const createInnerTRPCContext = (opts: CreateContextOptions) => {
const auth = getAuth(opts.req);
console.log(auth);

return {
...opts,
auth,
db,
};
};

export const createTRPCContext = (opts: { req: NextRequest }) => {
// Fetch stuff that depends on the request

return createInnerTRPCContext({
req: opts.req,
});
};
CsarChvz
CsarChvz12mo ago
@vibbin having the same issue! :/
feldrok
feldrok12mo ago
I need to do this too
BootesVoid
BootesVoid12mo ago
Same
Nico
Nico12mo ago
I can't get this to work with t3 after a few hours of trying. It works with a regular next-app with kirimase (kirimase's regular implementation of trpc is using nextcachelink vs batchstreamlink which may be the reason). I give up though. Would defer to @JacobMGEvans (Rustular CAO) who may be able to help. One thing I would mention @vibbin is that when using the app directory, clerk docs says to use auth() rather than getAuth().
CsarChvz
CsarChvz12mo ago
@Nico yep! Also it gives me an error with trpc idk why
JacobMGEvans
JacobMGEvans12mo ago
Yeah, there was an issue with the types, is that error you are referring to?
JacobMGEvans
JacobMGEvans12mo ago
GitHub
fix(nextjs): Improve type inference for the return type of auth hel...
Description Backport of #2047 Checklist npm test runs as expected. npm run build runs as expected. (If applicable) JSDoc comments have been added or updated for any package exports (If appli...
CsarChvz
CsarChvz12mo ago
Emmm, no Let me show u
baddra
baddra12mo ago
Did anyone solve it?
KiKo
KiKo12mo ago
Also looking for a solution here I'm having issues whenever I try to make my root page public
BootesVoid
BootesVoid12mo ago
Any clerk call from a trpc router rretuns null values. I tried useUser but doesn't work
Nico
Nico12mo ago
@JacobMGEvans (Rustular CAO) no unfortunately clerk doesn't return a user in the auth() call. Clearing my cookies and cache and then logging in and console-logging the headers in the TRPC API route handler has some interesting findings that may lead to properly identifying the issue: 1) clerk cookies exist and seem like everything necessary for the auth function 2) clerk related headers conveying different message: [ 'x-clerk-auth-message', '' ], [ 'x-clerk-auth-reason', 'header-missing-non-browser' ], [ 'x-clerk-auth-status', 'signed-out' ], 'x-clerk-auth-message' => { name: 'x-clerk-auth-message', value: '' }, 'x-clerk-auth-reason' => { name: 'x-clerk-auth-reason', value: 'header-missing-non-browser' }, 'x-clerk-auth-status' => { name: 'x-clerk-auth-status', value: 'signed-out' }, Which is what i'm assuming is leading to the user object equalling null. I've been trying to find a solution but no luck yet. Any thoughts?
baddra
baddra12mo ago
based on what theo said on today's stream a solution would be replacing server.ts to:
import "server-only";
import { headers } from "next/headers";

import { appRouter } from "@/server/api/root";
import { auth } from "@clerk/nextjs";
import { db } from "@/server/db";

export const api = appRouter.createCaller({
auth: auth(),
headers: headers(),
db,
});
import "server-only";
import { headers } from "next/headers";

import { appRouter } from "@/server/api/root";
import { auth } from "@clerk/nextjs";
import { db } from "@/server/db";

export const api = appRouter.createCaller({
auth: auth(),
headers: headers(),
db,
});
CsarChvz
CsarChvz12mo ago
Does it works?
Spark
Spark12mo ago
this seems to work for me as a temporary solution, but how would I continue to split edge and lambda, like in the OpenStatus repo? I previously had: server.ts
export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
endingLink({
headers: () => {
return {
cookie: cookies().toString(),
"x-trpc-source": "rsc",
};
},
}),
],
});
export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
loggerLink({
enabled: (op) =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),
endingLink({
headers: () => {
return {
cookie: cookies().toString(),
"x-trpc-source": "rsc",
};
},
}),
],
});
shared.ts
const lambdas = ["clerkRouter"];

export const endingLink = (opts?: {
headers?: HTTPHeaders | (() => HTTPHeaders);
}) =>
((runtime) => {
const sharedOpts = {
headers: opts?.headers,
} satisfies Partial<HTTPBatchLinkOptions>;

const edgeLink = unstable_httpBatchStreamLink({
...sharedOpts,
url: `${getBaseUrl()}/api/trpc/edge`,
})(runtime);
const lambdaLink = unstable_httpBatchStreamLink({
...sharedOpts,
url: `${getBaseUrl()}/api/trpc/lambda`,
})(runtime);

return (ctx) => {
const path = ctx.op.path.split(".") as [string, ...string[]];
const endpoint = lambdas.includes(path[0]) ? "lambda" : "edge";

const newCtx = {
...ctx,
op: { ...ctx.op, path: path.join(".") },
};
return endpoint === "edge" ? edgeLink(newCtx) : lambdaLink(newCtx);
};
}) satisfies TRPCLink<AppRouter>;
const lambdas = ["clerkRouter"];

export const endingLink = (opts?: {
headers?: HTTPHeaders | (() => HTTPHeaders);
}) =>
((runtime) => {
const sharedOpts = {
headers: opts?.headers,
} satisfies Partial<HTTPBatchLinkOptions>;

const edgeLink = unstable_httpBatchStreamLink({
...sharedOpts,
url: `${getBaseUrl()}/api/trpc/edge`,
})(runtime);
const lambdaLink = unstable_httpBatchStreamLink({
...sharedOpts,
url: `${getBaseUrl()}/api/trpc/lambda`,
})(runtime);

return (ctx) => {
const path = ctx.op.path.split(".") as [string, ...string[]];
const endpoint = lambdas.includes(path[0]) ? "lambda" : "edge";

const newCtx = {
...ctx,
op: { ...ctx.op, path: path.join(".") },
};
return endpoint === "edge" ? edgeLink(newCtx) : lambdaLink(newCtx);
};
}) satisfies TRPCLink<AppRouter>;
baddra
baddra12mo ago
I don't know how merging routers affects a caller but I understand that the previous method makes an API request, and you define the runtime in /api/trpc/edge/[trpc]/route.ts. By using a caller, it doesn't make an API request, so you are now responsible for setting the runtime in the appropriate page.tsx routes
vibbin
vibbin12mo ago
Would be interested in listening to the stream/discussion - what platform was that on?
baddra
baddra12mo ago
Theo - t3․gg
YouTube
STREAM VOD: Testing with Prime, TS benchmarks, camera rants and more
Intro - 00:00:00OFFSET 35:30 - 00:35:31Prime unit testing - 00:40:11End of Prime Testing vid - 01:18:45TypeScript benchmarking tool - 01:25:36End of TS bench...
codeOverCoffee
codeOverCoffee12mo ago
I'm glad to know it's not just me because I spent the entire weekend trying to get Clerk running on T3 using app router and couldn't figure it out.
feldrok
feldrok12mo ago
Still can't figure this out 😦
JEM
JEM12mo ago
GitHub
bug: Recent Commit Breaks Clerk Based Authentication w/ tRPC · Issu...
Provide environment information "initVersion": "7.23.2" System: OS: Linux 5.15 Ubuntu 20.04.6 LTS (Focal Fossa) CPU: (16) x64 Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz Memory: 9....
JEM
JEM12mo ago
hey I was able to figure out the issue it was with a recent change to the headers in the trpc provider but it seems maybe theo mentioned a better fix on stream?
carnegiepilled
carnegiepilled12mo ago
any updates on clerk + trpc + app router? afaik there is still no complete implementation with no issues were you typing? @Spark
Spark
Spark12mo ago
I haven’t seen a full implementation yet. I have used createCaller with some success in server.ts, but I am running into issues with useMutation on the client atm.
pineappaul
pineappaul12mo ago
I have Clerk + T3 fully integrated, except for a nasty bug we are stuck on. With this bug, authenticated requests to the TRPC server can only be made after logging in and refreshing the page. (see https://discord.com/channels/966627436387266600/1174091052102197398/1174091052102197398) No idea why!
carnegiepilled
carnegiepilled12mo ago
any word from clerk devs yet? this is kinda insane trpc + app router has been out for so long
shikishikichangchang
Im discussing this with Roy at Clerk https://discord.com/channels/856971667393609759/1172416025271226409 the one possible "solution" is this, in createTRPCContext:
const sessionToken = opts.req.cookies.get("__session")?.value ?? "";
const decodedJwt = decodeJwt(sessionToken);
const session = await clerkClient.sessions.verifySession(
decodedJwt.payload.sid,
sessionToken,
);
const sessionToken = opts.req.cookies.get("__session")?.value ?? "";
const decodedJwt = decodeJwt(sessionToken);
const session = await clerkClient.sessions.verifySession(
decodedJwt.payload.sid,
sessionToken,
);
Caveat: subject to rate limit
royanger
royanger12mo ago
I was just made aware of this thead. The suggestion Theo made on stream seems like the correct path here. I suspect that once that's implemented the req.header might include the cookie information that clerk is expecting. What's the issue with Edge/Lambda @Spark? Does anyone happen to have a relatively basic repo trying Theo's suggestion they can share with me to look into this? And unfortunately most of the conversation with shikishikichangchang on the Clerk discord was more general stuff or talk about App Router. Its not really relevant here. You can solve "2) clerk related headers conveying different message:" by making the trpc route public. And since usually auth checks are done trpc side this isn't an issue. Add something like publicRoutes: ["/api/<your trpc route>"] or publicRoutes: ["/api(.*)"] to your middleware.ts. The latter makes all api routes public. However after that step the cookie from Clerk still isn't normally in the header. What Theo mentioned might solve that -- need to look into that.
baddra
baddra12mo ago
Using a caller fixes all my problems, but I had to tweak server.ts to make it build without errors, but still havent solved the issue using the proxyclient
import "server-only";
import { headers } from "next/headers";

import { appRouter } from "@/server/api/root";
import { auth } from "@clerk/nextjs";
import { db } from "@/server/db";

export function caller() {
// cant use auth() outside a function, build fails
return appRouter.createCaller({
auth: auth(),
headers: headers(),
db,
});
}
import "server-only";
import { headers } from "next/headers";

import { appRouter } from "@/server/api/root";
import { auth } from "@clerk/nextjs";
import { db } from "@/server/db";

export function caller() {
// cant use auth() outside a function, build fails
return appRouter.createCaller({
auth: auth(),
headers: headers(),
db,
});
}
Spark
Spark12mo ago
nothing Clerk specific, just that using createCaller forces you to set the runtime in page.tsx instead of the way I was splitting edge/lambda before
carnegiepilled
carnegiepilled12mo ago
@JacobMGEvans (Rustular CAO) can we get some more support on this issue? clerk has been silent + 100s people struggling
JacobMGEvans
JacobMGEvans12mo ago
What do you mean Clerk has been silent?
carnegiepilled
carnegiepilled12mo ago
@JacobMGEvans (Rustular CAO) it's quite clear – there are 6/7 open issues on discord, mutliple ad-hoc discussions in the general channels, and 2 issues on github – and there has been very minimal interaction between clerk staff and the community if at all
JacobMGEvans
JacobMGEvans12mo ago
On the Clerk Discord?
carnegiepilled
carnegiepilled12mo ago
i think between clerk, theo, and trpc discords there are 12ish open issues yes and almost 0 support from clerk officially
JacobMGEvans
JacobMGEvans12mo ago
We officially give support in the Clerk discord 🙂
carnegiepilled
carnegiepilled12mo ago
really? then why are there 4+ active issues on this and zero support from clerk in your server?
No description
carnegiepilled
carnegiepilled12mo ago
these are just the ones i follow
JacobMGEvans
JacobMGEvans12mo ago
Ping me & Roy in those
carnegiepilled
carnegiepilled12mo ago
definitely! also - i don't mean to be rude - just being transparent and voicing the signals from the community that the support on this has been quite far below what you'd usually expect for a p0 (!!!) issue for paying customers
pineappaul
pineappaul12mo ago
After speaking with Roy last night, I will be releasing a video for the team @JacobMGEvans (Rustular CAO) demonstrating one symptom, as he said this will be useful. I can tag you when this goes up as well. Eating some breakfast before I get going Whatever we can do to help the team troubleshoot the issue faster, here to help with as long as we have bandwidth !
JacobMGEvans
JacobMGEvans12mo ago
You're fine, it's frustrating. We had a week we're it was me and another DevRel doing tickets and we couldn't keep up. That's on us
carnegiepilled
carnegiepilled12mo ago
thank you for understanding!
pineappaul
pineappaul12mo ago
Discord is arguably one of the most confusing comms platforms too, my last start up we were Discord centric and it was a living hell, so we totally understand the thread spread
JacobMGEvans
JacobMGEvans12mo ago
Me and Roy are working on improving that, he wrote up some support features for a bot and I'm adding some stuff too
NotLuksus
NotLuksus12mo ago
There is still no clear solution for this right? Like a simple example repo?
pineappaul
pineappaul12mo ago
@NotLuksus I have Clerk working with tRPC in this example repo, however there is still one bug present in my implementation where the auth object on the tRPC router server-side is only updated upon a full-page reload. You can check the example repo here: https://github.com/paulmikulskis/popup
If you have a suggestion on how to complete the fix, please open a branch so all can benefit 🙏
The solution is likely due to needing to call auth() from the client-side state, but I am not 100% clear on how to do this just yet. Roy provided a reasonable approach that I want to dig into a bit more today for inserting the client-side Clerk auth state into the tRPC context. (see https://discord.com/channels/966627436387266600/1170848397570359376/1174696019720671304) The issue with createCaller is that this solution is somewhat of an anti-pattern, since we want to keep the tRPC Context as the "caller" @JacobMGEvans (Rustular CAO) @Roy Anger Video walkthrough as promised, with two replay recordings for demonstrating: - what happens when things don't work (Replay recording 1 👉 https://app.replay.io/recording/builderclerk-broken--beda20eb-378a-4000-9aa4-5b558d905a82) - what happens when things do work (Replay recording 2 👉 https://app.replay.io/recording/builderclerk-working-demo--dae89c78-6cef-45f9-862b-d9ce26abcde1) Video walkthrough (3-minutes): https://youtu.be/ct47SQF0NMo Would be great to know when you folks are all set with the Replay links so I can delete them.
JacobMGEvans
JacobMGEvans12mo ago
Did you add this to Clerk support channel too @pineappaul
pineappaul
pineappaul12mo ago
I have not. I Will add shortly
royanger
royanger12mo ago
Yes, please add this to your iframe thread so I can help you.
NotLuksus
NotLuksus12mo ago
While trying to figure this out I noticed that this is an issue that, at least for me, only occurs in dev. In my production deployment I have full access to the clerk user / auth objects in trpc
LeoMachado
LeoMachado12mo ago
Anyone got any lucky on getting this sorted out? Using the createCaller is indeed the best solution?
pineappaul
pineappaul12mo ago
@LeoMachado how do you plan on integrating the createCaller paradigm into your NextJS app? I might be totally missing the mark here, but it feels that this is an anti-pattern, so I am curious on your take there
royanger
royanger12mo ago
T3 has or is about to release a fix for this.
Spark
Spark12mo ago
anyone have a clerk example working with the newest create-t3-app? where do we set auth?
carnegiepilled
carnegiepilled12mo ago
i do
carnegiepilled
carnegiepilled12mo ago
GitHub
feat: skip http for trpc rsc calls by juliusmarminge · Pull Request...
Closes #1669 Closes #1656 Closes #1650 Closes #1639 Closes #1599 Adds a custom link that just invokes the procedures directly with a cached createContext function, instead of using http even for RS...
carnegiepilled
carnegiepilled12mo ago
depends tho– this is only required if you're using RSC ^^ if you don't want to use RSC for some reason, then the solution is even easier search the help threads for the cookies solution - its really easy
Spark
Spark12mo ago
right, I implemented those changes, but are you still setting auth in trpc.ts on the ctx?
carnegiepilled
carnegiepilled12mo ago
import { initTRPC, TRPCError } from "@trpc/server";
import { type NextRequest } from "next/server";
import superjson from "superjson";
import { ZodError } from "zod";

import { db } from "~/server/db";

import { clerkClient, decodeJwt, getAuth, type User, type SignedInAuthObject, SignedOutAuthObject } from '@clerk/nextjs/server';
import { auth } from "@clerk/nextjs";


interface CreateContextOptions {
headers: Headers;
db: typeof db;
// auth: User;
auth: SignedInAuthObject | SignedOutAuthObject;
}

export const createTRPCContext = async (opts: { headers: Headers }) => {
const session = auth();
// const session = getAuth(opts)

return {
db,
auth: session,
...opts,
};
};


const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

/**
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
*
* These are the pieces you use to build your tRPC API. You should import these a lot in the
* "/src/server/api/routers" directory.
*/

/**
* This is how you create new routers and sub-routers in your tRPC API.
*
* @see https://trpc.io/docs/router
*/
export const createTRPCRouter = t.router;

const isAuthed = t.middleware(({ next, ctx }) => {
if (!ctx.auth.userId) {
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
}
return next({
ctx: {
auth: ctx.auth,
db,
},
});
});


export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
import { initTRPC, TRPCError } from "@trpc/server";
import { type NextRequest } from "next/server";
import superjson from "superjson";
import { ZodError } from "zod";

import { db } from "~/server/db";

import { clerkClient, decodeJwt, getAuth, type User, type SignedInAuthObject, SignedOutAuthObject } from '@clerk/nextjs/server';
import { auth } from "@clerk/nextjs";


interface CreateContextOptions {
headers: Headers;
db: typeof db;
// auth: User;
auth: SignedInAuthObject | SignedOutAuthObject;
}

export const createTRPCContext = async (opts: { headers: Headers }) => {
const session = auth();
// const session = getAuth(opts)

return {
db,
auth: session,
...opts,
};
};


const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

/**
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
*
* These are the pieces you use to build your tRPC API. You should import these a lot in the
* "/src/server/api/routers" directory.
*/

/**
* This is how you create new routers and sub-routers in your tRPC API.
*
* @see https://trpc.io/docs/router
*/
export const createTRPCRouter = t.router;

const isAuthed = t.middleware(({ next, ctx }) => {
if (!ctx.auth.userId) {
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not authenticated" });
}
return next({
ctx: {
auth: ctx.auth,
db,
},
});
});


export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
@Spark yes
Spark
Spark12mo ago
awesome, thanks! didn't realize we could just use the auth() function instead of getAuth() now. have you been able to useMutation? I keep getting an error, but it might just be my mistake.
carnegiepilled
carnegiepilled12mo ago
what's the error?
Spark
Spark12mo ago
TypeError: queryClient.getMutationDefaults is not a function
TypeError: queryClient.getMutationDefaults is not a function
shikishikichangchang
auth is working? Is this for App router with RSC or pages?
KiKo
KiKo12mo ago
Working for me after the changes that were made last week or 2 Rsc app dir
hhhyh
hhhyh12mo ago
Has anyone got this to work and not return null for the auth object ?
KiKo
KiKo12mo ago
I got it to work but auth() only returned user.id but if you do currentUser() you get the full object
hhhyh
hhhyh12mo ago
Are there any api limits for doing that method? If possible could you share the code for how you did it? Thanks!
KiKo
KiKo12mo ago
No limits afaik but it was basically just replace the next auth call to getserversession with currentUser
hhhyh
hhhyh12mo ago
from which clerk libraries are those methods from ? Appreciate you helping Do you know if I have to add in these changes too or is this unrelated https://github.com/t3-oss/create-t3-app/pull/1670/files
KiKo
KiKo12mo ago
Yes those changes were necessary I had the version prior and just recreated the difference on my repo
hhhyh
hhhyh12mo ago
makes sense, if possible could you share what your context.ts file looks like? Also would this work if im using Pages router instead ? would I need to add in those changes in the t3 link above?
KiKo
KiKo12mo ago
If you are using pages router I don't know I was under the understanding that for pages router you could just use it but I haven't used it in a while
hhhyh
hhhyh12mo ago
In some repos it works I think for Pages router but in my main repo the auth returns null. Im still using import { withClerkMiddleware } from "@clerk/nextjs/server"; which is from @clerk/[email protected] I would ideally like to use import { authMiddleware } from "@clerk/nextjs"; which is in the latest version of @clerk/nextjs however when I upgrade I get the following error which is documented in this thread I put in clerk discord. Says it cant find the #components library. https://discord.com/channels/856971667393609759/1177080652630327326/1177080652630327326
KiKo
KiKo12mo ago
middleware.ts
import { authMiddleware } from "@clerk/nextjs";

// This example protects all routes including api/trpc routes
// Please edit this to allow other routes to be public as needed.
// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
export default authMiddleware({
publicRoutes: ["/", "/api/(.*)"],
});

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
import { authMiddleware } from "@clerk/nextjs";

// This example protects all routes including api/trpc routes
// Please edit this to allow other routes to be public as needed.
// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
export default authMiddleware({
publicRoutes: ["/", "/api/(.*)"],
});

export const config = {
matcher: ["/((?!.+\\.[\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
server/trpc.ts
/**
* YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
* 1. You want to modify request context (see Part 1).
* 2. You want to create a new middleware or type of procedure (see Part 3).
*
* TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
* need to use are documented accordingly near the end.
*/

import { currentUser } from "@clerk/nextjs";
import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

import { db } from "~/server/db";

/**
* 1. CONTEXT
*
* This section defines the "contexts" that are available in the backend API.
*
* These allow you to access things when processing a request, like the database, the session, etc.
*
* This helper generates the "internals" for a tRPC context. The API handler and RSC clients each
* wrap this and provides the required context.
*
* @see https://trpc.io/docs/server/context
*/
export const createTRPCContext = async (opts: { headers: Headers }) => {
const user = await currentUser();

return {
db,
user,
...opts,
};
};

/**
* 2. INITIALIZATION
*
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
* errors on the backend.
*/

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});
/**
* YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
* 1. You want to modify request context (see Part 1).
* 2. You want to create a new middleware or type of procedure (see Part 3).
*
* TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
* need to use are documented accordingly near the end.
*/

import { currentUser } from "@clerk/nextjs";
import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import { ZodError } from "zod";

import { db } from "~/server/db";

/**
* 1. CONTEXT
*
* This section defines the "contexts" that are available in the backend API.
*
* These allow you to access things when processing a request, like the database, the session, etc.
*
* This helper generates the "internals" for a tRPC context. The API handler and RSC clients each
* wrap this and provides the required context.
*
* @see https://trpc.io/docs/server/context
*/
export const createTRPCContext = async (opts: { headers: Headers }) => {
const user = await currentUser();

return {
db,
user,
...opts,
};
};

/**
* 2. INITIALIZATION
*
* This is where the tRPC API is initialized, connecting the context and transformer. We also parse
* ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
* errors on the backend.
*/

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});
part 2
/**
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
*
* These are the pieces you use to build your tRPC API. You should import these a lot in the
* "/src/server/api/routers" directory.
*/

/**
* This is how you create new routers and sub-routers in your tRPC API.
*
* @see https://trpc.io/docs/router
*/
export const createTRPCRouter = t.router;

/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;

/** Reusable middleware that enforces users are logged in before running the procedure. */
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.user?.id) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// infers the `session` as non-nullable
user: ctx.user,
},
});
});

/**
* Protected (authenticated) procedure
*
* If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
* the session is valid and guarantees `ctx.session.user` is not null.
*
* @see https://trpc.io/docs/procedures
*/
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
/**
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
*
* These are the pieces you use to build your tRPC API. You should import these a lot in the
* "/src/server/api/routers" directory.
*/

/**
* This is how you create new routers and sub-routers in your tRPC API.
*
* @see https://trpc.io/docs/router
*/
export const createTRPCRouter = t.router;

/**
* Public (unauthenticated) procedure
*
* This is the base piece you use to build new queries and mutations on your tRPC API. It does not
* guarantee that a user querying is authorized, but you can still access user session data if they
* are logged in.
*/
export const publicProcedure = t.procedure;

/** Reusable middleware that enforces users are logged in before running the procedure. */
const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
if (!ctx.user?.id) {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
ctx: {
// infers the `session` as non-nullable
user: ctx.user,
},
});
});

/**
* Protected (authenticated) procedure
*
* If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
* the session is valid and guarantees `ctx.session.user` is not null.
*
* @see https://trpc.io/docs/procedures
*/
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
TRPC Provider is
<TRPCReactProvider cookies={cookies().toString()}>
{children}
</TRPCReactProvider>
<TRPCReactProvider cookies={cookies().toString()}>
{children}
</TRPCReactProvider>
Running next 14.0 on clerk v2.27 with app router. full t3 app with drizzle to start and then adding clerk by following the nextjs tutorial on clerk docs, following the t3-app with auth example very closely to intergrate the trpc parts. You know you are doing something wrong once you get type errors
densley9
densley99mo ago
when you pass cookies into trpcreactprovider, what is done with those cookeis? @KiKo
KiKo
KiKo9mo ago
I'm simply following the example in create t3 app I just did what was there I'm guessing clerk needs the cookies in the trpc middleware
densley9
densley99mo ago
hmmm, i must be missing something, what example is this? And thanks for responding! Im banging my head over here, been at this for hours
KiKo
KiKo9mo ago
There was an example somewhere. This thread is a few months old now also Remembering now. I took clerk + trpc getting started guide and applied it to t3 app
KiKo
KiKo9mo ago
Integrate Clerk into your Next.js Pages Router app with tRPC | Clerk
Learn how to integrate Clerk into your Next.js application using tRPC. tRPC can be used with Clerk, but requires a few tweaks from a traditional Clerk + Next.js setup.
densley9
densley99mo ago
thanks! i appreciare it!
Want results from more Discord servers?
Add your server