Using TRPC
Learn how to integrate Clerk into your Next.js using TRPC.
TRPC can be used with Clerk but requires a few tweaks from a traditional Clerk + Next.js setup.
Prefer to get started quickly? Check out the following repo which implements the essentials
Installation
Install @clerk/nextjs
npm i @clerk/nextjs
Set Environment Keys
Three environment variables for Clerk's SDK need to be set. Below is an example of your .env.local
file. To get the respective keys go to the API Keys page in the Clerk dashboard.
env.local1
Configure Middleware
Middleware needs to be placed at the root of your project. This allows you to use Clerk server-side APIs.
For TRPC projects, the `src` directory is usually the root directory. Make sure the middleware file is at the root of your project.
1import { withClerkMiddleware } from '@clerk/nextjs/server'2import { NextResponse } from 'next/server'3import type { NextRequest } from 'next/server'45export default withClerkMiddleware((_req: NextRequest) => {6return NextResponse.next()7})89// Stop Middleware running on static files10export const config = {11matcher: [12/*13* Match all request paths except for the ones starting with:14* - _next15* - static (static files)16* - favicon.ico (favicon file)17*/18"/(.*?trpc.*?|(?!static|.*\\..*|_next|favicon.ico).*)"19]20}
Using Clerk in your TRPC Context
To use Clerk in your TRPC context, you will need to use our server helper getAuth
1import * as trpc from '@trpc/server'2import * as trpcNext from '@trpc/server/adapters/next'3import { getAuth, clerkClient } from '@clerk/nextjs/server'4import type { SignedInAuthObject,SignedOutAuthObject } from "@clerk/nextjs/dist/api";56interface AuthContext {7auth: SignedInAuthObject | SignedOutAuthObject;8}910export const createContextInner = async ({ auth }: AuthContext ) => {11return {12auth,13}14}1516export const createContext = async (17opts: trpcNext.CreateNextContextOptions18) => {19return await createContextInner({ auth: getAuth(opts.req) })20}2122export type Context = trpc.inferAsyncReturnType<typeof createContext>23
Accessing the context data in your application
Once you have context in your application you can access the data in any procedure by using ctx
in your queries
1import { router, publicProcedure } from '../trpc'23export const exampleRouter = router({4hello: publicProcedure.query(({ctx}) => {5return {6greeting: `hello! ${ctx.auth?.userId}`7}8})9})
Using TRPC Middleware
In most cases you need to protect your routes from users who aren't signed in or a specific user type. You can create a protected procedure.
1import { initTRPC, TRPCError } from '@trpc/server'2import superjson from 'superjson'3import { type Context } from './context'45const t = initTRPC.context<Context>().create({6transformer: superjson,7errorFormatter({ shape }) {8return shape9}10})1112// check if the user is signed in, otherwise through a UNAUTHORIZED CODE13const isAuthed = t.middleware(({ next, ctx }) => {14if (!ctx.auth.userId) {15throw new TRPCError({ code: 'UNAUTHORIZED' })16}17return next({18ctx: {19auth: ctx.auth,20},21})22})2324export const router = t.router2526export const publicProcedure = t.procedure2728// export this procedure to be used anywhere in your application29export const protectedProcedure = t.procedure.use(isAuthed)30
Once you have created your procedure, you can use it in any router. The example below uses the protected procedure and returns the user's name.
1import { router, protectedProcedure } from '../trpc'23export const protectedRouter = router({4hello: protectedProcedure.query(({ ctx }) => {5return {6secret: `${ctx.auth?.userId} is using a protected procedure`7}8})9})10