Skip to content

Commit f354f46

Browse files
authored
Deprecate nested Middleware in favor of root middleware (vercel#36772)
This PR deprecates declaring a middleware under `pages` in favour of the project root naming it after `middleware` instead of `_middleware`. This is in the context of having a simpler execution model for middleware and also ships some refactor work. There is a ton of a code to be simplified after this deprecation but I think it is best to do it progressively. With this PR, when in development, we will **fail** whenever we find a nested middleware but we do **not** include it in the compiler so if the project is using it, it will no longer work. For production we will **fail** too so it will not be possible to build and deploy a deprecated middleware. The error points to a page that should also be reviewed as part of **documentation**. Aside from the deprecation, this migrates all middleware tests to work with a single middleware. It also splits tests into multiple folders to make them easier to isolate and work with. Finally it ships some small code refactor and simplifications.
1 parent cc8ab99 commit f354f46

File tree

145 files changed

+2380
-2498
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+2380
-2498
lines changed

docs/advanced-features/i18n-routing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ module.exports = {
164164
Next, we can use [Middleware](/docs/middleware.md) to add custom routing rules:
165165

166166
```js
167-
// pages/_middleware.ts
167+
// middleware.ts
168168

169169
import { NextRequest, NextResponse } from 'next/server'
170170

docs/advanced-features/middleware.md

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ Middleware enables you to use code over configuration. This gives you full flexi
2424
npm install next@latest
2525
```
2626

27-
2. Then, create a `_middleware.ts` file under your `/pages` directory.
27+
2. Then, create a `middleware.ts` file under your project root directory.
2828

29-
3. Finally, export a middleware function from the `_middleware.ts` file.
29+
3. Finally, export a middleware function from the `middleware.ts` file.
3030

3131
```jsx
32-
// pages/_middleware.ts
32+
// middleware.ts
3333

3434
import type { NextFetchEvent, NextRequest } from 'next/server'
3535

@@ -42,7 +42,7 @@ In this example, we use the standard Web API Response ([MDN](https://developer.m
4242

4343
## API
4444

45-
Middleware is created by using a `middleware` function that lives inside a `_middleware` file. Its API is based upon the native [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), and [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) objects.
45+
Middleware is created by using a `middleware` function that lives inside a `middleware` file. Its API is based upon the native [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response), and [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) objects.
4646

4747
These native Web API objects are extended to give you more control over how you manipulate and configure a response, based on the incoming requests.
4848

@@ -75,31 +75,6 @@ Middleware can be used for anything that shares logic for a set of pages, includ
7575
7676
## Execution Order
7777
78-
If your Middleware is created in `/pages/_middleware.ts`, it will run on all routes within the `/pages` directory. The below example assumes you have `about.tsx` and `teams.tsx` routes.
79-
80-
```bash
81-
- package.json
82-
- /pages
83-
_middleware.ts # Will run on all routes under /pages
84-
index.tsx
85-
about.tsx
86-
teams.tsx
87-
```
88-
89-
If you _do_ have sub-directories with nested routes, Middleware will run from the top down. For example, if you have `/pages/about/_middleware.ts` and `/pages/about/team/_middleware.ts`, `/about` will run first and then `/about/team`. The below example shows how this works with a nested routing structure.
90-
91-
```bash
92-
- package.json
93-
- /pages
94-
index.tsx
95-
- /about
96-
_middleware.ts # Will run first
97-
about.tsx
98-
- /teams
99-
_middleware.ts # Will run second
100-
teams.tsx
101-
```
102-
10378
Middleware runs directly after `redirects` and `headers`, before the first filesystem lookup. This excludes `/_next` files.
10479
10580
## Deployment

docs/api-reference/next.config.js/custom-page-extensions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = {
1616

1717
> **Note**: The default value of `pageExtensions` is [`['tsx', 'ts', 'jsx', 'js']`](https://github.com/vercel/next.js/blob/f1dbc9260d48c7995f6c52f8fbcc65f08e627992/packages/next/server/config-shared.ts#L161).
1818
19-
> **Note**: configuring `pageExtensions` also affects `_document.js`, `_app.js`, `_middleware.js` as well as files under `pages/api/`. For example, setting `pageExtensions: ['page.tsx', 'page.ts']` means the following files: `_document.tsx`, `_app.tsx`, `_middleware.ts`, `pages/users.tsx` and `pages/api/users.ts` will have to be renamed to `_document.page.tsx`, `_app.page.tsx`, `_middleware.page.ts`, `pages/users.page.tsx` and `pages/api/users.page.ts` respectively.
19+
> **Note**: configuring `pageExtensions` also affects `_document.js`, `_app.js`, `middleware.js` as well as files under `pages/api/`. For example, setting `pageExtensions: ['page.tsx', 'page.ts']` means the following files: `_document.tsx`, `_app.tsx`, `middleware.ts`, `pages/users.tsx` and `pages/api/users.ts` will have to be renamed to `_document.page.tsx`, `_app.page.tsx`, `middleware.page.ts`, `pages/users.page.tsx` and `pages/api/users.page.ts` respectively.
2020
2121
## Including non-page files in the `pages` directory
2222

@@ -32,7 +32,7 @@ module.exports = {
3232

3333
Then rename your pages to have a file extension that includes `.page` (ex. rename `MyPage.tsx` to `MyPage.page.tsx`).
3434

35-
> **Note**: Make sure you also rename `_document.js`, `_app.js`, `_middleware.js`, as well as files under `pages/api/`.
35+
> **Note**: Make sure you also rename `_document.js`, `_app.js`, `middleware.js`, as well as files under `pages/api/`.
3636
3737
Without this config, Next.js assumes every tsx/ts/jsx/js file in the `pages` directory is a page or API route, and may expose unintended routes vulnerable to denial of service attacks, or throw an error like the following when building the production bundle:
3838

docs/api-reference/next/server.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The `next/server` module provides several exports for server-only helpers, such
88

99
## NextMiddleware
1010

11-
Middleware is created by using a `middleware` function that lives inside a `_middleware` file. The Middleware API is based upon the native [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request), [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects.
11+
Middleware is created by using a `middleware` function that lives inside a `middleware` file. The Middleware API is based upon the native [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request), [`FetchEvent`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent), and [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) objects.
1212

1313
These native Web API objects are extended to give you more control over how you manipulate and configure a response, based on the incoming requests.
1414

errors/manifest.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,10 @@
626626
"title": "middleware-relative-urls",
627627
"path": "/errors/middleware-relative-urls.md"
628628
},
629+
{
630+
"title": "nested-middleware",
631+
"path": "/errors/nested-middleware.md"
632+
},
629633
{
630634
"title": "deleting-query-params-in-middlewares",
631635
"path": "/errors/deleting-query-params-in-middlewares.md"

errors/middleware-new-signature.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Your application is using a Middleware function that is using parameters from the deprecated API.
66

77
```typescript
8-
// _middleware.js
8+
// middleware.js
99
import { NextResponse } from 'next/server'
1010

1111
export function middleware(event) {
@@ -24,7 +24,7 @@ export function middleware(event) {
2424
Update to use the new API for Middleware:
2525

2626
```typescript
27-
// _middleware.js
27+
// middleware.js
2828
import { NextResponse } from 'next/server'
2929

3030
export function middleware(request) {

errors/nested-middleware.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Nested Middleware
2+
3+
#### Why This Error Occurred
4+
5+
You are defining a middleware file in a location different from `<root>/middleware` which is not allowed.
6+
7+
While in beta, a middleware file under specific pages implied that it would _only_ be executed when pages below its declaration were matched.
8+
This execution model allowed the nesting of multiple middleware, which is hard to reason about and led to consequences such as dragging effects between different middleware executions.
9+
10+
The API has been removed in favor of a simpler model with a single root middleware.
11+
12+
#### Possible Ways to Fix It
13+
14+
To fix this error, declare your middleware in the root folder and use `NextRequest` parsed URL to define which path the middleware code should be executed for. For example, a middleware declared under `pages/about/_middleware.js` can be moved to `middleware`. A conditional can be used to ensure the middleware executes only when it matches the `about/*` path:
15+
16+
```typescript
17+
import type { NextRequest } from 'next/server'
18+
19+
export function middleware(request: NextRequest) {
20+
if (request.nextUrl.pathname.startsWith('/about')) {
21+
// Execute pages/about/_middleware.js
22+
}
23+
}
24+
```
25+
26+
If you have more than one middleware, you will need to combine them into a single file and model their execution depending on the request.

errors/no-server-import-in-page.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
### Why This Error Occurred
44

5-
`next/server` was imported outside of `pages/**/_middleware.{js,ts}`.
5+
`next/server` was imported outside of `middleware.{js,ts}`.
66

77
### Possible Ways to Fix It
88

9-
Only import and use `next/server` in a file located within the pages directory: `pages/**/_middleware.{js,ts}`.
9+
Only import and use `next/server` in a file located within the project root directory: `middleware.{js,ts}`.
1010

1111
```ts
12-
// pages/_middleware.ts
12+
// middleware.ts
1313

1414
import type { NextFetchEvent, NextRequest } from 'next/server'
1515

packages/eslint-plugin-next/lib/rules/no-server-import-in-page.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ const path = require('path')
33
module.exports = {
44
meta: {
55
docs: {
6-
description:
7-
'Disallow importing next/server outside of pages/_middleware.js',
6+
description: 'Disallow importing next/server outside of middleware.js',
87
recommended: true,
98
url: 'https://nextjs.org/docs/messages/no-server-import-in-page',
109
},
@@ -16,20 +15,18 @@ module.exports = {
1615
return
1716
}
1817

19-
const paths = context.getFilename().split('pages')
20-
const page = paths[paths.length - 1]
21-
18+
const filename = context.getFilename()
2219
if (
23-
!page ||
24-
page.includes(`${path.sep}_middleware`) ||
25-
page.includes(`${path.posix.sep}_middleware`)
20+
filename.startsWith('middleware.') ||
21+
filename.startsWith(`${path.sep}middleware.`) ||
22+
filename.startsWith(`${path.posix.sep}middleware.`)
2623
) {
2724
return
2825
}
2926

3027
context.report({
3128
node,
32-
message: `next/server should not be imported outside of pages/_middleware.js. See: https://nextjs.org/docs/messages/no-server-import-in-page`,
29+
message: `next/server should not be imported outside of middleware.js. See: https://nextjs.org/docs/messages/no-server-import-in-page`,
3330
})
3431
},
3532
}

0 commit comments

Comments
 (0)