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

Two 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.

1
import { authMiddleware } from "@clerk/nextjs";
2
3
export default authMiddleware();
4
5
export const config = {
6
matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
7
};

This will protect every page by default, and cause a redirect to hosted pages please read about middleware in our documentation

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