From f174bb50039c561da5372a3aa23c650d1142df52 Mon Sep 17 00:00:00 2001 From: Janka Uryga Date: Tue, 10 Dec 2024 16:04:44 +0100 Subject: [PATCH 1/4] docs: supporting after for providers --- .../10-deploying/index.mdx | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/docs/01-app/02-building-your-application/10-deploying/index.mdx b/docs/01-app/02-building-your-application/10-deploying/index.mdx index 7452831a0b594..a3e81f892d25f 100644 --- a/docs/01-app/02-building-your-application/10-deploying/index.mdx +++ b/docs/01-app/02-building-your-application/10-deploying/index.mdx @@ -297,6 +297,23 @@ If you don't need a mix of both static and dynamic components, you can make your + + +### Using `after` + +#### Self-hosting + +`after` is fully supported in `next start`. + +Note that when you want to stop the server, you should give it a chance to gracefully shutdown by sending `SIGINT`/`SIGTERM` and waiting. +This will allow the server to wait for any pending callbacks/promises passed to `after` to finish. + +#### Serverless hosting + +`after` is fully supported on Vercel. If you're using another serverless deployment platform, `after` may or may not work depending on platform support. + + + ## Manual Graceful Shutdowns @@ -331,3 +348,85 @@ if (process.env.NEXT_MANUAL_SIG_HANDLE) { ``` + + + +## For providers: supporting `after` on your serverless platform + +If you’re a hosting provider and want your users to be able to use `after` on your platform, this section applies to you. + +### Background + +Using `after` in a serverless context requires a way to wait for asynchronous tasks to finish after the response has been sent. +We achieve this using a primitive called `waitUntil(promise)`, which extends the lifetime of a serverless invocation +until all promises passed to `waitUntil` have settled. +On Vercel, this is done via [https://vercel.com/docs/functions/functions-api-reference#waituntil](https://vercel.com/docs/functions/functions-api-reference#waituntil). + +If you want your users to be able to run `after`, you will have to provide you own implementation of `waitUntil` that behaves in an analogous way. + +### Interface + +When `after` is called, Next.js will access `waitUntil` like this: + +```jsx +const RequestContext = globalThis[Symbol.for('@next/request-context')] +const contextValue = RequestContext?.get() +const waitUntil = context?.waitUntil +``` + +Which means that `globalThis[Symbol.for('@next/request-context')]` is expected to contain an object like this: + +```tsx +type NextRequestContext = { + get(): NextRequestContextValue | undefined +} + +type NextRequestContextValue = { + waitUntil?: (promise: Promise) => void +} +``` + +### Examples - providing `waitUntil` to Next.js + +Depending on your requirements, this can be implemented in multiple ways. + +#### Example: A wrapper around an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) + +```tsx +import { AsyncLocalStorage } from 'node:async_hooks' + +const RequestContextStorage = new AsyncLocalStorage() + +// Define and inject the accessor that next.js will use +const RequestContext: NextRequestContext = { + get() { + return RequestContextStorage.getStore() + }, +} +globalThis[Symbol.for('@next/request-context')] = RequestContext + +const handler = (req, res) => { + const contextValue = { waitUntil: YOUR_WAITUNTIL } + // Provide the value + return RequestContextStorage.run(contextValue, () => nextJsHandler(req, res)) +} +``` + +#### Example: A global object + +Note that this may not be appropriate depending on how concurrency is handled on your platform. + +```tsx +const RequestContext: NextRequestContext = { + get() { + return { waitUntil: YOUR_WAITUNTIL } + }, +} +globalThis[Symbol.for('@next/request-context')] = RequestContext + +const handler = (req, res) => { + return nextJsHandler(req, res) +} +``` + + From f946deaed10169a853b94488b1a033baa66c95a0 Mon Sep 17 00:00:00 2001 From: Janka Uryga Date: Tue, 10 Dec 2024 17:03:30 +0100 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> --- .../10-deploying/index.mdx | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/docs/01-app/02-building-your-application/10-deploying/index.mdx b/docs/01-app/02-building-your-application/10-deploying/index.mdx index a3e81f892d25f..11aba5ef490cb 100644 --- a/docs/01-app/02-building-your-application/10-deploying/index.mdx +++ b/docs/01-app/02-building-your-application/10-deploying/index.mdx @@ -305,12 +305,13 @@ If you don't need a mix of both static and dynamic components, you can make your `after` is fully supported in `next start`. -Note that when you want to stop the server, you should give it a chance to gracefully shutdown by sending `SIGINT`/`SIGTERM` and waiting. -This will allow the server to wait for any pending callbacks/promises passed to `after` to finish. + Note that when you want to stop the server, you should allow it to gracefully shutdown by sending `SIGINT`/`SIGTERM` and waiting. This will allow the server to wait for any pending callbacks/promises passed to `after` to finish. #### Serverless hosting -`after` is fully supported on Vercel. If you're using another serverless deployment platform, `after` may or may not work depending on platform support. +`after` is fully supported on Vercel. If you're using another serverless deployment platform, check if your provider supports `after`. + +> **For platform providers**: see the [supporting `after` section](#for-providers-supporting-after-on-your-serverless-platform) for more information. @@ -353,16 +354,9 @@ if (process.env.NEXT_MANUAL_SIG_HANDLE) { ## For providers: supporting `after` on your serverless platform -If you’re a hosting provider and want your users to be able to use `after` on your platform, this section applies to you. - -### Background - -Using `after` in a serverless context requires a way to wait for asynchronous tasks to finish after the response has been sent. -We achieve this using a primitive called `waitUntil(promise)`, which extends the lifetime of a serverless invocation -until all promises passed to `waitUntil` have settled. -On Vercel, this is done via [https://vercel.com/docs/functions/functions-api-reference#waituntil](https://vercel.com/docs/functions/functions-api-reference#waituntil). +Using `after` in a serverless context requires waiting for asynchronous tasks to finish after the response has been sent. In Next.js and Vercel, this is achieved using a primitive called `waitUntil(promise)`, which extends the lifetime of a serverless invocation until all promises passed to [`waitUntil`](https://vercel.com/docs/functions/functions-api-reference#waituntil) have settled. -If you want your users to be able to run `after`, you will have to provide you own implementation of `waitUntil` that behaves in an analogous way. +If you want your users to be able to run `after`, you will have to provide your implementation of `waitUntil` that behaves in an analogous way. ### Interface @@ -386,7 +380,7 @@ type NextRequestContextValue = { } ``` -### Examples - providing `waitUntil` to Next.js +### Examples Depending on your requirements, this can be implemented in multiple ways. @@ -412,7 +406,7 @@ const handler = (req, res) => { } ``` -#### Example: A global object +#### A global object Note that this may not be appropriate depending on how concurrency is handled on your platform. From 8eb1e8ea2961251dbed43f16efa3cd5e00903001 Mon Sep 17 00:00:00 2001 From: Janka Uryga Date: Tue, 10 Dec 2024 21:10:09 +0100 Subject: [PATCH 3/4] remove global from example --- .../10-deploying/index.mdx | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/docs/01-app/02-building-your-application/10-deploying/index.mdx b/docs/01-app/02-building-your-application/10-deploying/index.mdx index 11aba5ef490cb..03173a0bb491d 100644 --- a/docs/01-app/02-building-your-application/10-deploying/index.mdx +++ b/docs/01-app/02-building-your-application/10-deploying/index.mdx @@ -305,11 +305,11 @@ If you don't need a mix of both static and dynamic components, you can make your `after` is fully supported in `next start`. - Note that when you want to stop the server, you should allow it to gracefully shutdown by sending `SIGINT`/`SIGTERM` and waiting. This will allow the server to wait for any pending callbacks/promises passed to `after` to finish. +Note that when you want to stop the server, you should allow it to gracefully shutdown by sending `SIGINT`/`SIGTERM` and waiting. This will allow the server to wait for any pending callbacks/promises passed to `after` to finish. #### Serverless hosting -`after` is fully supported on Vercel. If you're using another serverless deployment platform, check if your provider supports `after`. +`after` is fully supported on Vercel. If you're using another serverless deployment platform, check if your provider supports `after`. > **For platform providers**: see the [supporting `after` section](#for-providers-supporting-after-on-your-serverless-platform) for more information. @@ -380,11 +380,7 @@ type NextRequestContextValue = { } ``` -### Examples - -Depending on your requirements, this can be implemented in multiple ways. - -#### Example: A wrapper around an [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) +### Example ```tsx import { AsyncLocalStorage } from 'node:async_hooks' @@ -406,21 +402,4 @@ const handler = (req, res) => { } ``` -#### A global object - -Note that this may not be appropriate depending on how concurrency is handled on your platform. - -```tsx -const RequestContext: NextRequestContext = { - get() { - return { waitUntil: YOUR_WAITUNTIL } - }, -} -globalThis[Symbol.for('@next/request-context')] = RequestContext - -const handler = (req, res) => { - return nextJsHandler(req, res) -} -``` - From 239fd4dc23436462bf49cb24f81b191d8f8bb6e1 Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Wed, 11 Dec 2024 11:51:48 -0600 Subject: [PATCH 4/4] Update index.mdx --- .../10-deploying/index.mdx | 123 ++++++++---------- 1 file changed, 56 insertions(+), 67 deletions(-) diff --git a/docs/01-app/02-building-your-application/10-deploying/index.mdx b/docs/01-app/02-building-your-application/10-deploying/index.mdx index 03173a0bb491d..d8dcfec3536a5 100644 --- a/docs/01-app/02-building-your-application/10-deploying/index.mdx +++ b/docs/01-app/02-building-your-application/10-deploying/index.mdx @@ -299,19 +299,62 @@ If you don't need a mix of both static and dynamic components, you can make your -### Using `after` - -#### Self-hosting - -`after` is fully supported in `next start`. - -Note that when you want to stop the server, you should allow it to gracefully shutdown by sending `SIGINT`/`SIGTERM` and waiting. This will allow the server to wait for any pending callbacks/promises passed to `after` to finish. - -#### Serverless hosting - -`after` is fully supported on Vercel. If you're using another serverless deployment platform, check if your provider supports `after`. - -> **For platform providers**: see the [supporting `after` section](#for-providers-supporting-after-on-your-serverless-platform) for more information. +### `after` + +[`after`](/docs/app/api-reference/functions/after) is fully supported when self-hosting with `next start`. + +When stopping the server, ensure a graceful shutdown by sending `SIGINT` or `SIGTERM` signals and waiting. This allows the Next.js server to wait until after pending callback functions or promises used inside `after` have finished. + +If you want to use `after` on custom infrastructure, check your provider documentation to view support for `after`. + +
+ Reference: supporting `after` for serverless platforms + Using `after` in a serverless context requires waiting for asynchronous tasks to finish after the response has been sent. In Next.js and Vercel, this is achieved using a primitive called `waitUntil(promise)`, which extends the lifetime of a serverless invocation until all promises passed to [`waitUntil`](https://vercel.com/docs/functions/functions-api-reference#waituntil) have settled. + + If you want your users to be able to run `after`, you will have to provide your implementation of `waitUntil` that behaves in an analogous way. + + When `after` is called, Next.js will access `waitUntil` like this: + + ```jsx + const RequestContext = globalThis[Symbol.for('@next/request-context')] + const contextValue = RequestContext?.get() + const waitUntil = context?.waitUntil + ``` + + Which means that `globalThis[Symbol.for('@next/request-context')]` is expected to contain an object like this: + + ```tsx + type NextRequestContext = { + get(): NextRequestContextValue | undefined + } + + type NextRequestContextValue = { + waitUntil?: (promise: Promise) => void + } + ``` + + Here is an example of the implementation. + + ```tsx + import { AsyncLocalStorage } from 'node:async_hooks' + + const RequestContextStorage = new AsyncLocalStorage() + + // Define and inject the accessor that next.js will use + const RequestContext: NextRequestContext = { + get() { + return RequestContextStorage.getStore() + }, + } + globalThis[Symbol.for('@next/request-context')] = RequestContext + + const handler = (req, res) => { + const contextValue = { waitUntil: YOUR_WAITUNTIL } + // Provide the value + return RequestContextStorage.run(contextValue, () => nextJsHandler(req, res)) + } + ``` +
@@ -349,57 +392,3 @@ if (process.env.NEXT_MANUAL_SIG_HANDLE) { ``` - - - -## For providers: supporting `after` on your serverless platform - -Using `after` in a serverless context requires waiting for asynchronous tasks to finish after the response has been sent. In Next.js and Vercel, this is achieved using a primitive called `waitUntil(promise)`, which extends the lifetime of a serverless invocation until all promises passed to [`waitUntil`](https://vercel.com/docs/functions/functions-api-reference#waituntil) have settled. - -If you want your users to be able to run `after`, you will have to provide your implementation of `waitUntil` that behaves in an analogous way. - -### Interface - -When `after` is called, Next.js will access `waitUntil` like this: - -```jsx -const RequestContext = globalThis[Symbol.for('@next/request-context')] -const contextValue = RequestContext?.get() -const waitUntil = context?.waitUntil -``` - -Which means that `globalThis[Symbol.for('@next/request-context')]` is expected to contain an object like this: - -```tsx -type NextRequestContext = { - get(): NextRequestContextValue | undefined -} - -type NextRequestContextValue = { - waitUntil?: (promise: Promise) => void -} -``` - -### Example - -```tsx -import { AsyncLocalStorage } from 'node:async_hooks' - -const RequestContextStorage = new AsyncLocalStorage() - -// Define and inject the accessor that next.js will use -const RequestContext: NextRequestContext = { - get() { - return RequestContextStorage.getStore() - }, -} -globalThis[Symbol.for('@next/request-context')] = RequestContext - -const handler = (req, res) => { - const contextValue = { waitUntil: YOUR_WAITUNTIL } - // Provide the value - return RequestContextStorage.run(contextValue, () => nextJsHandler(req, res)) -} -``` - -