T
tRPC

❓-help

Using tRPC in Next.js Middleware

FFinn1/29/2024
Hello, I am quite new to tRPC so forgive me if I'm asking something quite obvious/dumb. I was introduced to tRPC by the t3-stack, that I discovered when I started with Next.js. Now onto the question. I am storing user session inside a database and want to validate a user that has a session_token cookie by its value. So, the value of the cookie needs to exist in the database. That way I can authenticate the request as well as associate a user. The problem I was facing is that I was having a hard time trying to access tRPC routes from the Next.js middleware. The t3 stack came with a pre-configured tRPC server and react client. Both of them do not work inside the Next.js middleware, because it runs on the Edge runtime. I have found some posts here that were asking a similar question, but none of them gave me an answer for my question. One of the posts helped solving the problem by suggesting to create a new client with a httpBatchLink that points to the api/trpc route. And that works the way it's suppose to. But right now, I export 3 api variables:
// src/trpc/react.tsx
export const api = createTRPCReact<AppRouter>();
// src/trpc/react.tsx
export const api = createTRPCReact<AppRouter>();
// src/trpc/server.ts
const createContext = cache(() => {
const heads = new Headers(headers());
// ...
});

export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
// ...
() =>
({ op }) =>
observable((observer) => {
createContext()
// ...
}),
],
});
// src/trpc/server.ts
const createContext = cache(() => {
const heads = new Headers(headers());
// ...
});

export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
// ...
() =>
({ op }) =>
observable((observer) => {
createContext()
// ...
}),
],
});
// src/trpc/edge.ts
export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
fetch(url, options) {
return fetch(url, {
...options,
});
},
}),
],
});
// src/trpc/edge.ts
export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
httpBatchLink({
url: `${getBaseUrl()}/api/trpc`,
fetch(url, options) {
return fetch(url, {
...options,
});
},
}),
],
});
Would you say, that what I've done is okay, or am I doing something completely unnecessary here?
FFinn1/29/2024
For reference, the whole template can be found here: https://github.com/t3-oss/create-t3-app/tree/main/cli/template/extras/src/trpc
BBillyBob2/23/2024
@Finn Did you have to fix the content-length and type headers with this solution ?
FFinn2/23/2024
Hi, I don't recall needing to change anything for it to work. But its been some time. I gave up trying to create my own authentication system and just started using lucia auth. Right now I use this api to validate requests in middleware.ts
import { createTRPCProxyClient, httpBatchLink, loggerLink } from "@trpc/client";

import { type AppRouter } from "@/server/api/root";
import { getUrl, transformer } from "./shared";

export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
loggerLink({
enabled: op =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),

httpBatchLink({
url: getUrl(),
fetch(url, options) {
return fetch(url, {
...options,
});
},
}),
],
});
import { createTRPCProxyClient, httpBatchLink, loggerLink } from "@trpc/client";

import { type AppRouter } from "@/server/api/root";
import { getUrl, transformer } from "./shared";

export const api = createTRPCProxyClient<AppRouter>({
transformer,
links: [
loggerLink({
enabled: op =>
process.env.NODE_ENV === "development" ||
(op.direction === "down" && op.result instanceof Error),
}),

httpBatchLink({
url: getUrl(),
fetch(url, options) {
return fetch(url, {
...options,
});
},
}),
],
});
But I still don't like that I have 3 different apis, for react, server and edge runtime. But I really don't have any clue as to what I could change for it to work otherwise.
BBillyBob2/23/2024
So you are not doing anything in Middleware right now? i had to do this:
headers(opts) {
// Any of the passed operations can override the headers
const headersOverride = opts.opList.find(
(op) => op.context?.headersOverride
)?.context.headersOverride as Record<string, string> | undefined



if (headersOverride) {
// console.log('headersOverride', headersOverride)
// console.log('headers', Object.fromEntries(headers()))
delete headersOverride['content-length']
delete headersOverride['content-type']
return headersOverride
}

const h = Object.fromEntries(headers())
delete h['content-length']
delete h['content-type']

return h
}
headers(opts) {
// Any of the passed operations can override the headers
const headersOverride = opts.opList.find(
(op) => op.context?.headersOverride
)?.context.headersOverride as Record<string, string> | undefined



if (headersOverride) {
// console.log('headersOverride', headersOverride)
// console.log('headers', Object.fromEntries(headers()))
delete headersOverride['content-length']
delete headersOverride['content-type']
return headersOverride
}

const h = Object.fromEntries(headers())
delete h['content-length']
delete h['content-type']

return h
}
FFinn2/23/2024
At the moment my middleware is just this:
export async function middleware(req: NextRequest): Promise<NextResponse> {
if (!isProtectedRoute(req.nextUrl.pathname)) return NextResponse.next();

const authCookie = cookies().get(SESSION_COOKIE_NAME);

if (!authCookie) {
return NextResponse.redirect(new URL("/signin", req.url));
}

const session = await api.session.validate.query({
sessionId: authCookie.value,
});

if (!session) {
return NextResponse.redirect(new URL("/signin", req.url));
}

return NextResponse.next();
}
export async function middleware(req: NextRequest): Promise<NextResponse> {
if (!isProtectedRoute(req.nextUrl.pathname)) return NextResponse.next();

const authCookie = cookies().get(SESSION_COOKIE_NAME);

if (!authCookie) {
return NextResponse.redirect(new URL("/signin", req.url));
}

const session = await api.session.validate.query({
sessionId: authCookie.value,
});

if (!session) {
return NextResponse.redirect(new URL("/signin", req.url));
}

return NextResponse.next();
}
I think that you should better create a new thread for that, if you're having problems with your code. I am very much not qualified to give any advice on tRPC whatsoever. I just took a quick look at the create-t3-app template and went on with it. The configuration of tRPC is a myth to me. Sorry mate.
BBillyBob2/23/2024
interesting that this works
const session = await api.session.validate.query({
sessionId: authCookie.value,
});
const session = await api.session.validate.query({
sessionId: authCookie.value,
});
to call tRPC in the nextJS middleware was why I needed to change the headers are you on 14.1 ?
FFinn2/23/2024
yes
BBillyBob2/23/2024
me too. i wonder if they changed something. because i am sure this didnt work before,
headers() {
return {
cookie: cookies().toString(),
}
},
headers() {
return {
cookie: cookies().toString(),
}
},
Because now all i need is this ^
FFinn2/23/2024
Well, the cookies are missing in the request to the API for me as well - if that's what you mean. That's why I just read the cookie in the middleware and pass it into the api call, rather than reading the cookie within the procedure itself.

Looking for more? Join the community!

Recommended Posts
need help refreshing websocketWe currently are using tRPC w/ react and websockets. We’re using the URL of the websocket as the autrouter is crashing when in separate fileWhen i use router merging and have e.g users router in separate file, i import router from trpc.ts aHow do I pass a Generic to a trpc query procedure?I want to to something like this: ```ts type AppIdsWithConfig = typeof kodixCareAppId | typeof calenIs there something to be done about trpc errors and solidjs/seroval?Basically if you `throw error` in trpc route, solidjs seroval fails to serialize it during SSR. I doHow can i createCaller from a NextJs App Router if my server uses express tRPC adapter?I'm using the express adapter for the server side of tRPC, and the client is a NextJS AppRouter app.Is there a way to refetch a query with new parameters?Hi I'm using tRPC in a Next.js app and I have a button that a user can click to get the latest dataMultiple Routers with WebsocketsHi. I am looking to create two routers with either the same websocket connection or two unique websoIs there any benefit to putting the db connection in the context versus having it as an global var?Can I do this? ```ts export const challengesRouter = router({ getChallengeById: privateProcedure Controller is already closed crashIm using trpc with nextjs 14 app router and started to see that my app crashes due to such error. DiThis page needs to bail out of prerendering at this point because it used revalidate: 0I've been testing out partial prerendering with next using tRPC but having some issues. Everytime How to infer query types on client?```ts export const tenantRouter = router({ getTenants: procedure.query(() => { return { hello:NextJS Output TypingsQuick question probably stupid, I learned about this library yesterday and completely migrated from 'react-server-dom-webpack/client' errorHey, i am trying to get started with TRPC but getting this error ```shell ../../node_modules/.pnpm/React Query client errors on 401I'm throwing a `TRPCError({ code: "UNAUTHORIZED" })` in a middleware, and handling error states retuExtremely slow queriesI'm having elapsed time of 2 to 5 seconds and I just switched from mongo to postgres. Is it the DB lTRPC works but getting unpredictable Typescript warnings for a barebones trpc monorepo projectHi, TRPC is working as expected but there are many warnings that feels very "off" for simple proceduKirimase's tRPC usage CRUD routeHey, using Nico's Kirimase tRPC package. I am trying to call the queries and mutations, in the db/scClient `tsc` checking server codeI have a trpc server and vite/react client set up in a pnpm monorepo. The VSCode type checking is beUnable to set up a client side subscription to a trpc route that supports websocketsI wrote the code on the client side here to query the subscription: https://github.com/sumanthneerumwhat's this union type doing(Zod)?I have 2 types of user, guest and registered(schemas). I have a union type with both and the guest d