From b4102e983e663460ed910d1294a5084dde2050fd Mon Sep 17 00:00:00 2001 From: April Arcus Date: Wed, 15 Apr 2026 13:13:58 -0700 Subject: [PATCH] Fix SSR PostGraphile access for Next 15 dev Next 15 dev wraps page requests in a Proxy. Accessing `req.app` through that wrapper can interact badly with Express because the Express app is both a callable function and an object with route helpers. In practice, our SSR Apollo setup could end up tripping Express path handling with a non-string value, causing: TypeError: path.replace is not a function during `getDataFromTree`. Read the PostGraphile instance from the Express app once while installing SSR, then attach it directly to each SSR request as `req.pgl`. The server-side Apollo link now reads `req.pgl` instead of reaching back through `req.app.get("pgl")` during page rendering. This keeps SSR independent of Next's request proxy behavior and avoids accidentally invoking Express routing internals while building the server-side GraphQL link. --- @app/lib/src/withApollo.tsx | 2 +- @app/server/src/middleware/installSSR.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/@app/lib/src/withApollo.tsx b/@app/lib/src/withApollo.tsx index a010b31..2b6308e 100644 --- a/@app/lib/src/withApollo.tsx +++ b/@app/lib/src/withApollo.tsx @@ -90,7 +90,7 @@ function makeServerSideLink(req: any, res: any) { return new GraphileApolloLink({ req, res, - pgl: req.app.get("pgl"), + pgl: req.pgl, }); } diff --git a/@app/server/src/middleware/installSSR.ts b/@app/server/src/middleware/installSSR.ts index 52ac179..1416488 100644 --- a/@app/server/src/middleware/installSSR.ts +++ b/@app/server/src/middleware/installSSR.ts @@ -5,6 +5,7 @@ import type { GraphileApp } from "@app/lib" with { import { type Express } from "express"; import { createServer } from "http"; import next from "next"; +import { type PostGraphileInstance } from "postgraphile"; import { getUpgradeHandlers } from "../app"; @@ -38,14 +39,20 @@ export default async function installSSR(app: Express) { console.error(e); process.exit(1); }); + + const pgl = app.get("pgl") as PostGraphileInstance; + app.get("*", async (req, res) => { - const handler = await handlerPromise; const graphileApp: GraphileApp = { CSRF_TOKEN: req.csrfToken(), ROOT_URL: process.env.ROOT_URL || "http://localhost:5678", ...(process.env.T_AND_C_URL && { T_AND_C_URL: process.env.T_AND_C_URL }), }; + (req as any).graphileApp = graphileApp; + (req as any).pgl = pgl; + + const handler = await handlerPromise; handler(req, res); });