Is there a way to modify the latest `create-t3-app` to create a protected trpc procedure?

I created a new project with npm create-t3-app@latest with trpc, typescript, tailwind, prisma, and clerk. The newest version invokes a caller function to create two api definitions, one for the server and one for the client.

By default, it includes a publicProcedure definition, I want to create a privateProcedure which checks for authentication with clerk, meaning auth needs to be included in the context. So the change needs to be made to the create context function.

Here's what comes out of the box with create-t3-app:

// ./src/server/api/trpc.ts
export const createTRPCContext = async (opts: { headers: Headers }) => {
  return {
    db,
    ...opts,
  };
};

Here is what I thought I would be able to do to update it:

import { getAuth } from "@clerk/nextjs/server";

...

export const createTRPCContext = async (opts: { headers: Headers }) => {
  const { userId } = getAuth(opts.headers);
  
  return {
    db,
    userId,
    ...opts,
  };
};

...

export const privateProcedure = t.procedure.use(async ({ ctx, next }) => {
  if (!ctx.userId) {
    throw new TRPCError({
      code: "UNAUTHORIZED",
      message: "You must be logged in to perform this action",
    });
  }
  return next({
    ctx: {
      ...ctx,
      userId: ctx.userId,
    },
  });
});

The problem is, Typescript tells me the argument of type 'Headers' is not assignable to the type 'RequestLike'. To fix this I updated the type.

import type * as trpcNext from "@trpc/server/adapters/next";

...

export const createTRPCContext = async (opts: trpcNext.CreateNextContextOptions) => {
  const { userId } = getAuth(opts.req);

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

This works, but now the server-side api definition is not happy. I am not sure how to fix it because I'm a little fuzzy on what exactly is going on in this definition anyhow.

// ./src/trpc/server.ts
import "server-only";

import { headers } from "next/headers";
import { cache } from "react";

import { createCaller } from "@/server/api/root";
import { createTRPCContext } from "@/server/api/trpc";

/**
 * This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
 * handling a tRPC call from a React Server Component.
 */
const createContext = cache(() => {
  const heads = new Headers(headers());
  heads.set("x-trpc-source", "rsc");

  return createTRPCContext({
    headers: heads,
  });
});

export const api = createCaller(createContext);

The typescript error of course is in the invocation of createTRPCContext, which no longer expects the same type of input.

Upvotes: 0

Views: 277

Answers (1)

crud hole
crud hole

Reputation: 1

I ran into the same thing and was similarly confused at first.

if you look at https://trpc.io/docs/server/context they have a section on "inner and outer context" and it looks like create-t3-app is ~sort of~ following this by having a createContext in src/trpc/server and another in api/trpc/[trpc]/route.ts where the one in route.ts would be the "outer" context where req is available. So, in order to add a protected/private procedure, you'll want to follow the pattern from the trpc docs and either adapt it to ct3a's structure, or just change your app's structure to match the docs.

I went with the latter, defining an inner context creator, and updating the type passed to initTRPC to use the type of my inner context, and then using my inner context in trpc/server.ts and outer context in api/trpc/[trpc]/route.ts

hope that's helpful!

Upvotes: 0

Related Questions