Skip to content

Commit 126f46c

Browse files
authored
Only add x-fah-middleware header to routes that have middleware enabled (#325)
* only add x-fah-middleware routes to routes that have middleware enabled * start tests * fix bug and fix unit tests * fixup e2e tests * fix lint error * fix e2e * bump adapter version
1 parent ecbc67e commit 126f46c

File tree

6 files changed

+71
-46
lines changed

6 files changed

+71
-46
lines changed

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@apphosting/adapter-nextjs/e2e/middleware.spec.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ before(() => {
1515
});
1616

1717
describe("middleware", () => {
18-
it("should have x-fah-adapter header and x-fah-middleware header on all routes", async () => {
18+
it("should have x-fah-adapter header on all routes", async () => {
1919
const routes = [
2020
"/",
2121
"/ssg",
@@ -33,6 +33,15 @@ describe("middleware", () => {
3333
`nextjs-${adapterVersion}`,
3434
`Route ${route} missing x-fah-adapter header`,
3535
);
36+
}
37+
});
38+
39+
it("should have x-fah-middleware header on middleware routes", async () => {
40+
// Middleware is configured to run on these routes via run-local.ts with-middleware setup function
41+
const routes = ["/ssg", "/ssr"];
42+
43+
for (const route of routes) {
44+
const response = await fetch(posix.join(host, route));
3645
assert.equal(
3746
response.headers.get("x-fah-middleware"),
3847
"true",

packages/@apphosting/adapter-nextjs/e2e/run-local.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ const scenarios: Scenario[] = [
3535
setup: async (cwd: string) => {
3636
// Create a middleware.ts file
3737
const middlewareContent = `
38-
import type { NextRequest } from 'next/server'
38+
import type { NextRequest } from "next/server";
3939
4040
export function middleware(request: NextRequest) {
4141
// This is a simple middleware that doesn't modify the request
42-
console.log('Middleware executed', request.nextUrl.pathname);
42+
console.log("Middleware executed", request.nextUrl.pathname);
4343
}
4444
4545
export const config = {
46-
matcher: '/((?!api|_next/static|_next/image|favicon.ico).*)',
46+
matcher: ["/ssg", "/ssr"],
4747
};
4848
`;
4949

packages/@apphosting/adapter-nextjs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@apphosting/adapter-nextjs",
3-
"version": "14.0.12",
3+
"version": "14.0.13",
44
"main": "dist/index.js",
55
"description": "Experimental addon to the Firebase CLI to add web framework support",
66
"repository": {

packages/@apphosting/adapter-nextjs/src/overrides.spec.ts

+23-19
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ describe("route overrides", () => {
8585
assert.deepStrictEqual(updatedManifest, expectedManifest);
8686
});
8787

88-
it("should add middleware header when middleware exists", async () => {
88+
it("should add middleware header only to routes for which middleware is enabled", async () => {
8989
const { addRouteOverrides } = await importOverrides;
9090
const initialManifest: RoutesManifest = {
9191
version: 3,
@@ -109,8 +109,8 @@ describe("route overrides", () => {
109109
page: "/",
110110
matchers: [
111111
{
112-
regexp: "^/.*$",
113-
originalSource: "/:path*",
112+
regexp: "/hello",
113+
originalSource: "/hello",
114114
},
115115
],
116116
},
@@ -130,7 +130,7 @@ describe("route overrides", () => {
130130
fs.readFileSync(routesManifestPath, "utf-8"),
131131
) as RoutesManifest;
132132

133-
assert.strictEqual(updatedManifest.headers.length, 1);
133+
assert.strictEqual(updatedManifest.headers.length, 2);
134134

135135
const expectedManifest: RoutesManifest = {
136136
version: 3,
@@ -150,9 +150,13 @@ describe("route overrides", () => {
150150
key: "x-fah-adapter",
151151
value: "nextjs-1.0.0",
152152
},
153-
{ key: "x-fah-middleware", value: "true" },
154153
],
155154
},
155+
{
156+
source: "/hello",
157+
regex: "/hello",
158+
headers: [{ key: "x-fah-middleware", value: "true" }],
159+
},
156160
],
157161
};
158162

@@ -172,13 +176,13 @@ describe("next config overrides", () => {
172176
...config,
173177
images: {
174178
...(config.images || {}),
175-
...(config.images?.unoptimized === undefined && config.images?.loader === undefined
176-
? { unoptimized: true }
179+
...(config.images?.unoptimized === undefined && config.images?.loader === undefined
180+
? { unoptimized: true }
177181
: {}),
178182
},
179183
});
180184
181-
const config = typeof originalConfig === 'function'
185+
const config = typeof originalConfig === 'function'
182186
? async (...args) => {
183187
const resolvedConfig = await originalConfig(...args);
184188
return fahOptimizedConfig(resolvedConfig);
@@ -194,12 +198,12 @@ describe("next config overrides", () => {
194198
const { overrideNextConfig } = await importOverrides;
195199
const originalConfig = `
196200
// @ts-check
197-
201+
198202
/** @type {import('next').NextConfig} */
199203
const nextConfig = {
200204
/* config options here */
201205
}
202-
206+
203207
module.exports = nextConfig
204208
`;
205209

@@ -213,7 +217,7 @@ describe("next config overrides", () => {
213217
normalizeWhitespace(`
214218
// @ts-nocheck
215219
const originalConfig = require('./next.config.original.js');
216-
220+
217221
${nextConfigOverrideBody}
218222
219223
module.exports = config;
@@ -225,14 +229,14 @@ describe("next config overrides", () => {
225229
const { overrideNextConfig } = await importOverrides;
226230
const originalConfig = `
227231
// @ts-check
228-
232+
229233
/**
230234
* @type {import('next').NextConfig}
231235
*/
232236
const nextConfig = {
233237
/* config options here */
234238
}
235-
239+
236240
export default nextConfig
237241
`;
238242

@@ -257,7 +261,7 @@ describe("next config overrides", () => {
257261
const { overrideNextConfig } = await importOverrides;
258262
const originalConfig = `
259263
// @ts-check
260-
264+
261265
export default (phase, { defaultConfig }) => {
262266
/**
263267
* @type {import('next').NextConfig}
@@ -280,7 +284,7 @@ describe("next config overrides", () => {
280284
import originalConfig from './next.config.original.mjs';
281285
282286
${nextConfigOverrideBody}
283-
287+
284288
export default config;
285289
`),
286290
);
@@ -290,11 +294,11 @@ describe("next config overrides", () => {
290294
const { overrideNextConfig } = await importOverrides;
291295
const originalConfig = `
292296
import type { NextConfig } from 'next'
293-
297+
294298
const nextConfig: NextConfig = {
295299
/* config options here */
296300
}
297-
301+
298302
export default nextConfig
299303
`;
300304

@@ -307,9 +311,9 @@ describe("next config overrides", () => {
307311
normalizeWhitespace(`
308312
// @ts-nocheck
309313
import originalConfig from './next.config.original';
310-
314+
311315
${nextConfigOverrideBody}
312-
316+
313317
module.exports = config;
314318
`),
315319
);

packages/@apphosting/adapter-nextjs/src/overrides.ts

+33-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AdapterMetadata, MiddlewareManifest } from "./interfaces.js";
1+
import { AdapterMetadata } from "./interfaces.js";
22
import {
33
loadRouteManifest,
44
writeRouteManifest,
@@ -146,9 +146,12 @@ export async function validateNextConfigOverride(
146146
* Modifies the app's route manifest (routes-manifest.json) to add Firebase App Hosting
147147
* specific overrides (i.e headers).
148148
*
149-
* This function adds the following headers to all routes:
149+
* It adds the following headers to all routes:
150150
* - x-fah-adapter: The Firebase App Hosting adapter version used to build the app.
151+
*
152+
* It also adds the following headers to all routes for which middleware is enabled:
151153
* - x-fah-middleware: When middleware is enabled.
154+
*
152155
* @param appPath The path to the app directory.
153156
* @param distDir The path to the dist directory.
154157
* @param adapterMetadata The adapter metadata.
@@ -158,38 +161,47 @@ export async function addRouteOverrides(
158161
distDir: string,
159162
adapterMetadata: AdapterMetadata,
160163
) {
161-
const middlewareManifest = loadMiddlewareManifest(appPath, distDir);
162164
const routeManifest = loadRouteManifest(appPath, distDir);
165+
166+
// Add the adapter version to all routes
163167
routeManifest.headers.push({
164168
source: "/:path*",
165169
headers: [
166170
{
167171
key: "x-fah-adapter",
168172
value: `nextjs-${adapterMetadata.adapterVersion}`,
169173
},
170-
...(middlewareExists(middlewareManifest)
171-
? [
172-
{
173-
key: "x-fah-middleware",
174-
value: "true",
175-
},
176-
]
177-
: []),
178174
],
179175
/*
180-
NextJs converts the source string to a regex using path-to-regexp (https://github.com/pillarjs/path-to-regexp) at
181-
build time: https://github.com/vercel/next.js/blob/canary/packages/next/src/build/index.ts#L1273.
182-
This regex is then used to match the route against the request path.
176+
NextJs converts the source string to a regex using path-to-regexp (https://github.com/pillarjs/path-to-regexp) at
177+
build time: https://github.com/vercel/next.js/blob/canary/packages/next/src/build/index.ts#L1273.
178+
This regex is then used to match the route against the request path.
183179
184-
This regex was generated by building a sample NextJs app with the source string `/:path*` and then inspecting the
185-
routes-manifest.json file.
186-
*/
180+
This regex was generated by building a sample NextJs app with the source string `/:path*` and then inspecting the
181+
routes-manifest.json file.
182+
*/
187183
regex: "^(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))?(?:/)?$",
188184
});
189185

190-
await writeRouteManifest(appPath, distDir, routeManifest);
191-
}
186+
// Add the middleware header to all routes for which middleware is enabled
187+
const middlewareManifest = loadMiddlewareManifest(appPath, distDir);
188+
const rootMiddleware = middlewareManifest.middleware["/"];
189+
if (rootMiddleware?.matchers) {
190+
console.log("Middleware detected, adding middleware headers to matching routes");
191+
192+
rootMiddleware.matchers.forEach((matcher) => {
193+
routeManifest.headers.push({
194+
source: matcher.originalSource,
195+
headers: [
196+
{
197+
key: "x-fah-middleware",
198+
value: "true",
199+
},
200+
],
201+
regex: matcher.regexp,
202+
});
203+
});
204+
}
192205

193-
function middlewareExists(middlewareManifest: MiddlewareManifest) {
194-
return Object.keys(middlewareManifest.middleware).length > 0;
206+
await writeRouteManifest(appPath, distDir, routeManifest);
195207
}

0 commit comments

Comments
 (0)