Vilian
Vilian15mo ago

nextjs app router `fetchRequestHandler`'s createContext doesn't run.

Hey there, I have the following code:
// /src/app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '~/trpc/root';
import { ENV } from '~/env';
import { createTRPCContext } from '~/trpc/trpc';

const handler = (req: Request) =>
fetchRequestHandler({
req,
router: appRouter,
endpoint: '/api/trpc',
onError: ({ path, error }) => {
if (ENV.NODE_ENV !== 'development') return;
console.error(
`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message} `
);
},
createContext: createTRPCContext,
});

export { handler as GET, handler as POST };
// /src/app/api/trpc/[trpc]/route.ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '~/trpc/root';
import { ENV } from '~/env';
import { createTRPCContext } from '~/trpc/trpc';

const handler = (req: Request) =>
fetchRequestHandler({
req,
router: appRouter,
endpoint: '/api/trpc',
onError: ({ path, error }) => {
if (ENV.NODE_ENV !== 'development') return;
console.error(
`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message} `
);
},
createContext: createTRPCContext,
});

export { handler as GET, handler as POST };
13 Replies
Vilian
VilianOP15mo ago
// /src/trpc/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import superjson from 'superjson';
import { ZodError } from 'zod';
import { getServerAuthSession } from '~/auth/auth';

export const createTRPCContext = async () => {
console.log('Context');
return await getServerAuthSession();
};

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

export const createTRPCRouter = t.router;

const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
console.log(ctx);
if (!ctx.session?.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: {
session: { ...ctx.session, user: ctx.session.user },
},
});
});

export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
// /src/trpc/trpc.ts
import { initTRPC, TRPCError } from '@trpc/server';
import superjson from 'superjson';
import { ZodError } from 'zod';
import { getServerAuthSession } from '~/auth/auth';

export const createTRPCContext = async () => {
console.log('Context');
return await getServerAuthSession();
};

const t = initTRPC.context<typeof createTRPCContext>().create({
transformer: superjson,
errorFormatter({ shape, error }) {
return {
...shape,
data: {
...shape.data,
zodError:
error.cause instanceof ZodError ? error.cause.flatten() : null,
},
};
},
});

export const createTRPCRouter = t.router;

const enforceUserIsAuthed = t.middleware(({ ctx, next }) => {
console.log(ctx);
if (!ctx.session?.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next({
ctx: {
session: { ...ctx.session, user: ctx.session.user },
},
});
});

export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
When i invoke any trpc endpoint with an http request, the request goes through, but the context does not run. I'm pretty sure the createTRPCContext should be invoked on every request/batch of requests
sachin
sachin15mo ago
are you passing createContext to fetchRequestHandler ah nvm it’s in the post body this is definitely a bug can you create an issue with a minimal reproduction?
Vilian
VilianOP15mo ago
I'll try to Hey @sachinraja sorry for the ping, however, do you guys have an example tRPC with react 13 app router ?
sachin
sachin15mo ago
GitHub
trpc/examples/.experimental/next-app-dir at main · trpc/trpc
🧙‍♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy. - trpc/trpc
Vilian
VilianOP15mo ago
Last question: Are you sure what i sent above is a bug? Should the createContext run on every single request made to trpc ?
sachin
sachin15mo ago
yes
Vilian
VilianOP15mo ago
What if it's used with appRouter.createCaller?
sachin
sachin15mo ago
yes but you have to pass createContext to that too
Vilian
VilianOP15mo ago
Well there were some example with appRouter.createCaller(await createContext()) But as we know, this means top level await... which is not supported afaik.
sachin
sachin15mo ago
you shouldnt be calling createCaller at the top level
Vilian
VilianOP15mo ago
Do you have an example/snippet you could link me to see?
sachin
sachin15mo ago
what are you using it for
Vilian
VilianOP15mo ago
Well nothing so far. I'm very new to react space and i've been meaning to setup myself a nextjs app router project template to use I'm trying to understand how the whole structure maps out and works under the hood.