Skip to content

Commit b01a6ba

Browse files
styfleleerobijjk
authored
Add TS types for NextMiddleware (vercel#30578)
This allows TypeScript users to have type safety for middleware functions. - Closes vercel#30490 Co-authored-by: Lee Robinson <[email protected]> Co-authored-by: JJ Kasper <[email protected]>
1 parent 4410de9 commit b01a6ba

File tree

20 files changed

+194
-8
lines changed

20 files changed

+194
-8
lines changed

docs/api-reference/next/server.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,29 @@ description: Use Middleware to run code before a request is completed.
44

55
# next/server
66

7-
Middleware is created by using a `middleware` function that lives inside a `_middleware` file. The Middleware 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.
7+
The `next/server` module provides several exports for server-only helpers, such as [Middleware](/docs/middleware.md).
8+
9+
## NextMiddleware
10+
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.
812

913
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.
1014

11-
The function signature:
15+
The function signature is defined as follows:
1216

1317
```ts
14-
import type { NextRequest, NextFetchEvent } from 'next/server'
18+
type NextMiddlewareResult = NextResponse | Response | null | undefined
1519

16-
export type Middleware = (
20+
type NextMiddleware = (
1721
request: NextRequest,
1822
event: NextFetchEvent
19-
) => Promise<Response | undefined> | Response | undefined
23+
) => NextMiddlewareResult | Promise<NextMiddlewareResult>
24+
```
25+
26+
It can be imported from `next/server` with the following:
27+
28+
```ts
29+
import type { NextMiddleware } from 'next/server'
2030
```
2131

2232
The function can be a default export and as such, does **not** have to be named `middleware`. Though this is a convention. Also note that you only need to make the function `async` if you are running asynchronous code.

packages/next/server.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { NextFetchEvent } from 'next/dist/server/web/spec-extension/fetch-event'
22
export { NextRequest } from 'next/dist/server/web/spec-extension/request'
33
export { NextResponse } from 'next/dist/server/web/spec-extension/response'
4+
export { NextMiddleware } from 'next/dist/server/web/types'

packages/next/server/web/adapter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { RequestData, FetchEventResult } from './types'
1+
import type { NextMiddleware, RequestData, FetchEventResult } from './types'
22
import { DeprecationError } from './error'
33
import { fromNodeHeaders } from './utils'
44
import { NextFetchEvent } from './spec-extension/fetch-event'
@@ -7,7 +7,7 @@ import { NextResponse } from './spec-extension/response'
77
import { waitUntilSymbol } from './spec-compliant/fetch-event'
88

99
export async function adapter(params: {
10-
handler: (request: NextRequest, event: NextFetchEvent) => Promise<Response>
10+
handler: NextMiddleware
1111
page: string
1212
request: RequestData
1313
}): Promise<FetchEventResult> {

packages/next/server/web/spec-extension/response.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class NextResponse extends Response {
7373
})
7474
}
7575

76-
static redirect(url: string | NextURL, status = 302) {
76+
static redirect(url: string | NextURL | URL, status = 302) {
7777
if (!REDIRECTS.has(status)) {
7878
throw new RangeError(
7979
'Failed to execute "redirect" on "response": Invalid status code'

packages/next/server/web/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import type { I18NConfig } from '../config-shared'
2+
import type { NextRequest } from '../web/spec-extension/request'
3+
import type { NextFetchEvent } from '../web/spec-extension/fetch-event'
4+
import type { NextResponse } from './spec-extension/response'
25

36
export interface NodeHeaders {
47
[header: string]: string | string[] | undefined
@@ -31,3 +34,10 @@ export interface FetchEventResult {
3134
response: Response
3235
waitUntil: Promise<any>
3336
}
37+
38+
export type NextMiddlewareResult = NextResponse | Response | null | undefined
39+
40+
export type NextMiddleware = (
41+
request: NextRequest,
42+
event: NextFetchEvent
43+
) => NextMiddlewareResult | Promise<NextMiddlewareResult>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
i18n: {
3+
locales: ['en', 'fr', 'nl'],
4+
defaultLocale: 'en',
5+
},
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Index() {
2+
return <p className="title">Dynamic route</p>
3+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { NextMiddleware } from 'next/server'
2+
3+
export const middleware: NextMiddleware = function (request) {
4+
return new Response(null, {
5+
headers: {
6+
'req-url-basepath': request.nextUrl.basePath,
7+
'req-url-pathname': request.nextUrl.pathname,
8+
'req-url-params': JSON.stringify(request.page.params),
9+
'req-url-page': request.page.name || '',
10+
'req-url-query': request.nextUrl.searchParams.get('foo') || '',
11+
'req-url-locale': request.nextUrl.locale,
12+
},
13+
})
14+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Index() {
2+
return <p className="title">Static route</p>
3+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { NextMiddleware, NextResponse } from 'next/server'
2+
3+
export const middleware: NextMiddleware = async function (request) {
4+
const url = request.nextUrl
5+
6+
if (url.searchParams.get('foo') === 'bar') {
7+
url.pathname = '/redirects/new-home'
8+
url.searchParams.delete('foo')
9+
return Response.redirect(url)
10+
}
11+
12+
if (url.pathname === '/redirects/old-home') {
13+
url.pathname = '/redirects/new-home'
14+
return NextResponse.redirect(url)
15+
}
16+
}

0 commit comments

Comments
 (0)