Docs

You are viewing an archived version of the docs.Go to latest version

Integrate Clerk into your Next.js Pages Router app with tRPC

You will learn the following:

  • Create a Clerk authentication context
  • Use the Clerk context in tRPC queries
  • Access the context data in your backend
  • Create a protected procedure
  • Use your protected procedure

Create Clerk authentication context

Clerk's Auth object includes important authentication information like the current user's session ID, user ID, and organization ID. It also contains methods to check for the current user's permissions and to retrieve their session token.

To add Clerk's authentication context (Auth object) to your tRPC server, create a context file that will be used to create the context for every tRPC query sent to the server. This context file will use the getAuth() helper from Clerk to access the user's Auth object.

src/server/context.ts
import * as trpc from '@trpc/server';
import * as trpcNext from '@trpc/server/adapters/next';
import { getAuth } from '@clerk/nextjs/server';
 
export const createContext = async (
  opts: trpcNext.CreateNextContextOptions
) => {
  return { auth: getAuth(opts.req) }
}

export type Context = trpc.inferAsyncReturnType<typeof createContext>;

Create tRPC context

Create the tRPC context to use the Clerk context in your tRPC queries.

src/pages/api/trpc/[trpc].ts
import { appRouter } from '@/server/routers'
import * as trpcNext from '@trpc/server/adapters/next'
import { createContext } from 'src/server/context';

export default trpcNext.createNextApiHandler({
  router: appRouter,
  createContext: createContext,
});

Access the context data in your backend

The tRPC context, or ctx, should now have access to the Clerk authentication context. Use ctx in your queries to access the context data in any procedure.

In the following example, the ctx is used to access the user's ID and return a greeting message. If the user is not signed in, the greeting will return hello! undefined.

src/server/routers/index.ts
import { router, publicProcedure } from '../trpc';

export const exampleRouter = router({
  hello: publicProcedure.query(({ctx}) => {
    return {
      greeting: `hello! ${ctx.auth?.userId}`
    }
  })
})

Create a protected procedure

In many applications, it's essential to restrict access to certain routes based on user authentication status. This ensures that sensitive data and functionality are only accessible to authorized users. tRPC middleware provides a powerful mechanism for implementing this protection within your application.

In the following example, tRPC middleware is used to access the ctx, which contains the user's authentication information. If the user's ID exists in the authentication context, this means that the user is signed in. If the user is not signed in, an UNAUTHORIZED error is thrown.

src/server/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server'
import superjson from 'superjson'
import { type Context } from './context'

const t = initTRPC.context<Context>().create({
  transformer: superjson,
  errorFormatter({ shape }) {
    return shape
  }
})

// check if the user is signed in, otherwise throw an UNAUTHORIZED code
const isAuthed = t.middleware(({ next, ctx }) => {
  if (!ctx.auth.userId) {
    throw new TRPCError({ code: 'UNAUTHORIZED' })
  }
  return next({
    ctx: {
      auth: ctx.auth,
    },
  })
})

export const router = t.router

export const publicProcedure = t.procedure

// export this procedure to be used anywhere in your application
export const protectedProcedure = t.procedure.use(isAuthed)

Use your protected procedure

Once you have created your procedure, you can use it in any router.

In the following example, the protected procedure is used to return a secret message that includes the user's ID. If the user is not signed in, the hello procedure will return an UNAUTHORIZED error, as configured in the step above.

src/server/routers/index.ts
import { router, protectedProcedure } from '../trpc'

export const protectedRouter = router({
  hello: protectedProcedure.query(({ ctx }) => {
    return {
      secret: `${ctx.auth?.userId} is using a protected procedure`
    }
  })
})

Feedback

What did you think of this content?

Last updated on