Skip to content

Commit

Permalink
docs: improve docs around geolocation and IP headers (#59719)
Browse files Browse the repository at this point in the history
Based on feedback from #47793, I
made some improvements around the geolocation docs. Specifically around
`request.ip`, `request.geo`, and how to access these values. I noticed
there was a bit of a divergence, as some of the `NextRequest` and
`NextResponse` docs were split out for the App Router section, but not
all.

This PR finishes that swing by removing the previous catch-all for
`next/server` in the Pages Router docs and splits them into individual
docs pages.

Wrote a lil' thread about this:
https://twitter.com/leeerob/status/1736543599339172121

---------

Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com>
  • Loading branch information
leerob and delbaoliveira authored Dec 18, 2023
1 parent e3c2552 commit 1c65c55
Show file tree
Hide file tree
Showing 14 changed files with 267 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,28 @@ export function middleware(request) {
}
```

### `waitUntil` and `NextFetchEvent`

The `NextFetchEvent` object extends the native [`FetchEvent`](https://developer.mozilla.org/docs/Web/API/FetchEvent) object, and includes the [`waitUntil()`](https://developer.mozilla.org/docs/Web/API/ExtendableEvent/waitUntil) method.

The `waitUntil()` method takes a promise as an argument, and extends the lifetime of the Middleware until the promise settles. This is useful for performing work in the background.

```ts filename="middleware.ts"
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'

export function middleware(req: NextRequest, event: NextFetchEvent) {
event.waitUntil(
fetch('https://my-analytics-platform.com', {
method: 'POST',
body: JSON.stringify({ pathname: req.nextUrl.pathname }),
})
)

return NextResponse.next()
}
```

## Advanced Middleware Flags

In `v13.1` of Next.js two additional flags were introduced for middleware, `skipMiddlewareUrlNormalize` and `skipTrailingSlashRedirect` to handle advanced use cases.
Expand Down
57 changes: 48 additions & 9 deletions docs/02-app/02-api-reference/04-functions/headers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,63 @@ const headersList = headers()
`headers()` can be used in combination with [Suspense for Data Fetching](/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating).

```jsx filename="app/page.js"
import { Suspense } from 'react'
import { headers } from 'next/headers'

async function getUser() {
const headersInstance = headers()
const authorization = headersInstance.get('authorization')
// Forward the authorization header
async function User() {
const authorization = headers().get('authorization')
const res = await fetch('...', {
headers: { authorization },
headers: { authorization }, // Forward the authorization header
})
return res.json()
}
const user = await res.json()

export default async function UserPage() {
const user = await getUser()
return <h1>{user.name}</h1>
}

export default function Page() {
return (
<Suspense fallback={null}>
<User />
</Suspense>
)
}
```

#### IP Address

`headers()` can be used to get the IP address of the client.

```jsx filename="app/page.js"
import { Suspense } from 'react'
import { headers } from 'next/headers'

function IP() {
const FALLBACK_IP_ADDRESS = '0.0.0.0'
const forwardedFor = headers().get('x-forwarded-for')

if (forwardedFor) {
return forwardedFor.split(',')[0] ?? FALLBACK_IP_ADDRESS
}

return headers().get('x-real-ip') ?? FALLBACK_IP_ADDRESS
}

export default function Page() {
return (
<Suspense fallback={null}>
<IP />
</Suspense>
)
}
```
In addition to `x-forwarded-for`, `headers()` can also read:
- `x-real-ip`
- `x-forwarded-host`
- `x-forwarded-port`
- `x-forwarded-proto`
## Version History
| Version | Changes |
Expand Down
69 changes: 69 additions & 0 deletions docs/02-app/02-api-reference/04-functions/next-request.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: NextRequest
description: API Reference for NextRequest.
---

{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}

NextRequest extends the [Web Request API](https://developer.mozilla.org/docs/Web/API/Request) with additional convenience methods.

## `cookies`
Expand Down Expand Up @@ -82,6 +84,73 @@ request.nextUrl.pathname
request.nextUrl.searchParams
```

The following options are available:

<PagesOnly>

| Property | Type | Description |
| ----------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `basePath` | `string` | The [base path](/docs/pages/api-reference/next-config-js/basePath) of the URL. |
| `buildId` | `string` \| `undefined` | The build identifier of the Next.js application. Can be [customized](/docs/pages/api-reference/next-config-js/generateBuildId). |
| `defaultLocale` | `string` \| `undefined` | The default locale for [internationalization](/docs/pages/building-your-application/routing/internationalization). |
| `domainLocale` | | |
| - `defaultLocale` | `string` | The default locale within a domain. |
| - `domain` | `string` | The domain associated with a specific locale. |
| - `http` | `boolean` \| `undefined` | Indicates if the domain is using HTTP. |
| `locales` | `string[]` \| `undefined` | An array of available locales. |
| `locale` | `string` \| `undefined` | The currently active locale. |
| `url` | `URL` | The URL object. |

</PagesOnly>

<AppOnly>

| Property | Type | Description |
| -------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `basePath` | `string` | The [base path](/docs/app/api-reference/next-config-js/basePath) of the URL. |
| `buildId` | `string` \| `undefined` | The build identifier of the Next.js application. Can be [customized](/docs/app/api-reference/next-config-js/generateBuildId). |
| `url` | `URL` | The URL object. |
| `pathname` | `string` | The pathname of the URL. |
| `searchParams` | `Object` | The search parameters of the URL. |

> **Note:** The internationalization properties from the Pages Router are not available for usage in the App Router. Learn more about [internationalization with the App Router](/docs/app/building-your-application/routing/internationalization).
</AppOnly>

## `ip`

The `ip` property is a string that contains the IP address of the request. This value can optionally be provided by your hosting platform.

> **Good to know:** On [Vercel](https://vercel.com/docs/frameworks/nextjs?utm_source=next-site&utm_medium=docs&utm_campaign=next-website), this value is provided by default. On other platforms, you can use the [`X-Forwarded-For`](https://developer.mozilla.org/docs/Web/HTTP/Headers/X-Forwarded-For) header to provide the IP address.
```ts
// Provided by Vercel
request.ip
// Self-hosting
request.headers.get('X-Forwarded-For')
```

## `geo`

The `geo` property is an object that contains the geographic information of the request. This value can optionally be provided by your hosting platform.

> **Good to know:** On [Vercel](https://vercel.com/docs/frameworks/nextjs?utm_source=next-site&utm_medium=docs&utm_campaign=next-website), this value is provided by default. On other platforms, you can use the [`X-Forwarded-For`](https://developer.mozilla.org/docs/Web/HTTP/Headers/X-Forwarded-For) header to provide the IP address, then use a [third-party service](https://ip-api.com/) to lookup the geographic information.
```ts
// Provided by Vercel
request.geo.city
request.geo.country
request.geo.region
request.geo.latitude
request.geo.longitude

// Self-hosting
function getGeo(request) {
let ip = request.headers.get('X-Forwarded-For')
// Use a third-party service to lookup the geographic information
}
```

## Version History

| Version | Changes |
Expand Down
2 changes: 2 additions & 0 deletions docs/02-app/02-api-reference/04-functions/next-response.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: NextResponse
description: API Reference for NextResponse.
---

{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}

NextResponse extends the [Web Response API](https://developer.mozilla.org/docs/Web/API/Response) with additional convenience methods.

## `cookies`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
title: permanentRedirect
description: API Reference for the permanentRedirect function.
related:
links:
- app/api-reference/functions/redirect
---

The `permanentRedirect` function allows you to redirect the user to another URL. `permanentRedirect` can be used in Server Components, Client Components, [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
Expand Down
22 changes: 22 additions & 0 deletions docs/02-app/02-api-reference/04-functions/redirect.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
title: redirect
description: API Reference for the redirect function.
related:
links:
- app/api-reference/functions/permanentRedirect
---

The `redirect` function allows you to redirect the user to another URL. `redirect` can be used in Server Components, Client Components, [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations).
Expand Down Expand Up @@ -59,6 +62,25 @@ export default async function Profile({ params }) {

> **Good to know**: `redirect` does not require you to use `return redirect()` as it uses the TypeScript [`never`](https://www.typescriptlang.org/docs/handbook/2/functions.html#never) type.
## FAQ

### Why does `redirect` use 307 and 308?

When using `redirect()` you may notice that the status codes used are `307` for a temporary redirect, and `308` for a permanent redirect. While traditionally a `302` was used for a temporary redirect, and a `301` for a permanent redirect, many browsers changed the request method of the redirect, from a `POST` to `GET` request when using a `302`, regardless of the origins request method.

Taking the following example of a redirect from `/users` to `/people`, if you make a `POST` request to `/users` to create a new user, and are conforming to a `302` temporary redirect, the request method will be changed from a `POST` to a `GET` request. This doesn't make sense, as to create a new user, you should be making a `POST` request to `/people`, and not a `GET` request.

The introduction of the `307` status code means that the request method is preserved as `POST`.

- `302` - Temporary redirect, will change the request method from `POST` to `GET`
- `307` - Temporary redirect, will preserve the request method as `POST`

The `redirect()` method uses a `307` by default, instead of a `302` temporary redirect, meaning your requests will _always_ be preserved as `POST` requests.

[Learn more](https://developer.mozilla.org/docs/Web/HTTP/Redirections) about HTTP Redirects.

## Version History

| Version | Changes |
| --------- | ---------------------- |
| `v13.0.0` | `redirect` introduced. |
71 changes: 71 additions & 0 deletions docs/02-app/02-api-reference/04-functions/userAgent.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: userAgent
description: The userAgent helper extends the Web Request API with additional properties and methods to interact with the user agent object from the request.
---

{/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}

The `userAgent` helper extends the [Web Request API](https://developer.mozilla.org/docs/Web/API/Request) with additional properties and methods to interact with the user agent object from the request.

```ts filename="middleware.ts" switcher
import { NextRequest, NextResponse, userAgent } from 'next/server'

export function middleware(request: NextRequest) {
const url = request.nextUrl
const { device } = userAgent(request)
const viewport = device.type === 'mobile' ? 'mobile' : 'desktop'
url.searchParams.set('viewport', viewport)
return NextResponse.rewrite(url)
}
```

```js filename="middleware.js" switcher
import { NextResponse, userAgent } from 'next/server'

export function middleware(request) {
const url = request.nextUrl
const { device } = userAgent(request)
const viewport = device.type === 'mobile' ? 'mobile' : 'desktop'
url.searchParams.set('viewport', viewport)
return NextResponse.rewrite(url)
}
```

## `isBot`

A boolean indicating whether the request comes from a known bot.

## `browser`

An object containing information about the browser used in the request.

- `name`: A string representing the browser's name, or `undefined` if not identifiable.
- `version`: A string representing the browser's version, or `undefined`.

## `device`

An object containing information about the device used in the request.

- `model`: A string representing the model of the device, or `undefined`.
- `type`: A string representing the type of the device, such as `console`, `mobile`, `tablet`, `smarttv`, `wearable`, `embedded`, or `undefined`.
- `vendor`: A string representing the vendor of the device, or `undefined`.

## `engine`

An object containing information about the browser's engine.

- `name`: A string representing the engine's name. Possible values include: `Amaya`, `Blink`, `EdgeHTML`, `Flow`, `Gecko`, `Goanna`, `iCab`, `KHTML`, `Links`, `Lynx`, `NetFront`, `NetSurf`, `Presto`, `Tasman`, `Trident`, `w3m`, `WebKit` or `undefined`.
- `version`: A string representing the engine's version, or `undefined`.

## `os`

An object containing information about the operating system.

- `name`: A string representing the name of the OS, or `undefined`.
- `version`: A string representing the version of the OS, or `undefined`.

## `cpu`

An object containing information about the CPU architecture.

- `architecture`: A string representing the architecture of the CPU. Possible values include: `68k`, `amd64`, `arm`, `arm64`, `armhf`, `avr`, `ia32`, `ia64`, `irix`, `irix64`, `mips`, `mips64`, `pa-risc`, `ppc`, `sparc`, `sparc64` or `undefined`
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ In some rare cases, you might need to assign a custom status code for older HTTP

## Other Redirects

- Inside [API Routes](/docs/pages/api-reference/functions/next-server), you can use `res.redirect()`.
- Inside [API Routes](/docs/pages/building-your-application/routing/api-routes) and [Route Handlers](/docs/app/building-your-application/routing/route-handlers), you can redirect based on the incoming request.
- Inside [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props) and [`getServerSideProps`](/docs/pages/building-your-application/data-fetching/get-server-side-props), you can redirect specific pages at request-time.

## Version History
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
}
```

- `req`: An instance of [http.IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage), plus some [pre-built middlewares](/docs/pages/api-reference/functions/next-server)
- `res`: An instance of [http.ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse), plus some [helper functions](/docs/pages/api-reference/functions/next-server)
- `req`: An instance of [http.IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage)
- `res`: An instance of [http.ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse)

## HTTP Methods

Expand Down
7 changes: 7 additions & 0 deletions docs/03-pages/02-api-reference/02-functions/next-request.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: NextRequest
description: API Reference for NextRequest.
source: app/api-reference/functions/next-request
---

{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}
7 changes: 7 additions & 0 deletions docs/03-pages/02-api-reference/02-functions/next-response.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: NextResponse
description: API Reference for NextResponse.
source: app/api-reference/functions/next-response
---

{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */}
Loading

0 comments on commit 1c65c55

Please sign in to comment.