Clerk logo

Clerk Docs

Ctrl + K
Go to clerkstage.dev

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.local
1

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.

1
import { withClerkMiddleware } from '@clerk/nextjs/server'
2
import { NextResponse } from 'next/server'
3
import type { NextRequest } from 'next/server'
4
5
export default withClerkMiddleware((_req: NextRequest) => {
6
return NextResponse.next()
7
})
8
9
// Stop Middleware running on static files
10
export const config = {
11
matcher: [
12
/*
13
* Match all request paths except for the ones starting with:
14
* - _next
15
* - static (static files)
16
* - favicon.ico (favicon file)
17
*/
18
"/(.*?trpc.*?|(?!static|.*\\..*|_next|favicon.ico).*)",
19
"/"
20
]
21
}

Using Clerk in your TRPC Context

To use Clerk in your TRPC context, you will need to use our server helper getAuth

1
import * as trpc from '@trpc/server'
2
import * as trpcNext from '@trpc/server/adapters/next'
3
import { getAuth, clerkClient } from '@clerk/nextjs/server'
4
import type { SignedInAuthObject,SignedOutAuthObject } from "@clerk/nextjs/dist/api";
5
6
interface AuthContext {
7
auth: SignedInAuthObject | SignedOutAuthObject;
8
}
9
10
export const createContextInner = async ({ auth }: AuthContext ) => {
11
return {
12
auth,
13
}
14
}
15
16
export const createContext = async (
17
opts: trpcNext.CreateNextContextOptions
18
) => {
19
return await createContextInner({ auth: getAuth(opts.req) })
20
}
21
22
export 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

1
import { router, publicProcedure } from '../trpc'
2
3
export const exampleRouter = router({
4
hello: publicProcedure.query(({ctx}) => {
5
return {
6
greeting: `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.

1
import { initTRPC, TRPCError } from '@trpc/server'
2
import superjson from 'superjson'
3
import { type Context } from './context'
4
5
const t = initTRPC.context<Context>().create({
6
transformer: superjson,
7
errorFormatter({ shape }) {
8
return shape
9
}
10
})
11
12
// check if the user is signed in, otherwise through a UNAUTHORIZED CODE
13
const isAuthed = t.middleware(({ next, ctx }) => {
14
if (!ctx.auth.userId) {
15
throw new TRPCError({ code: 'UNAUTHORIZED' })
16
}
17
return next({
18
ctx: {
19
auth: ctx.auth,
20
},
21
})
22
})
23
24
export const router = t.router
25
26
export const publicProcedure = t.procedure
27
28
// export this procedure to be used anywhere in your application
29
export 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.

1
import { router, protectedProcedure } from '../trpc'
2
3
export const protectedRouter = router({
4
hello: protectedProcedure.query(({ ctx }) => {
5
return {
6
secret: `${ctx.auth?.userId} is using a protected procedure`
7
}
8
})
9
})
10

Was this helpful?

Clerk © 2023