diff --git a/pages/aws/common_issues.mdx b/pages/aws/common_issues.mdx index 172c0caa..8b061201 100644 --- a/pages/aws/common_issues.mdx +++ b/pages/aws/common_issues.mdx @@ -8,6 +8,11 @@ This will output **A LOT** of additional logs to the console.This also disable m OPEN_NEXT_DEBUG=true npx open-next@latest build ``` +A few other environment variables can help debugging at runtime: + +- `OPEN_NEXT_REQUEST_ID_HEADER` - When set, the `x-opennext-requestid` header is added to the responses (it is always added in debug mode). Available since `@opennextjs/aws` 3.7.0. +- `OPEN_NEXT_ERROR_LOG_LEVEL` - The minimum level of the internal OpenNext errors that are logged: `0` (or `debug`), `1` (or `warn`, the default) or `2` (or `error`). + #### Cannot find module next You might stumble upon this error inside cloudwatch logs: `Cannot find module 'next'`. It is likely that you are in a monorepo and you have several lock files. **Just make sure that you have a single lock file in the root of your project.** @@ -32,6 +37,9 @@ You should also not add sharp as a dependency unless absolutely necessary, the i - `next/dist/compiled/babel-packages` - `next/dist/compiled/amphtml-validator` +Since `@opennextjs/aws` 3.10.0, OpenNext also excludes the platform specific native binaries for non-Linux platforms (i.e. `@swc/core-darwin-arm64`) that are traced when building on macOS or Windows. +You can set the `OPEN_NEXT_SKIP_PLATFORM_FILTER` environment variable to `true` at build time to disable this filter. + ##### Remove sourcemaps Source maps could add a lot to the bundle, excluding them from standalone output could be worth it. diff --git a/pages/aws/config.mdx b/pages/aws/config.mdx index 59b8a084..366b17b8 100644 --- a/pages/aws/config.mdx +++ b/pages/aws/config.mdx @@ -1,9 +1,10 @@ ### Build Arguments -There is two build arguments that you can pass to the `open-next build` command: +These are the build arguments that you can pass to the `open-next build` command: - `--config-path` - This is the path to the configuration file that you want to use. By default, it will look for `open-next.config.ts` in the current working directory. This needs to be relative to the current working directory. - `--node-externals` - You can configure externals for the esbuild compilation of the `open-next.config.ts` file (i.e `--node-externals @aws-sdk/*,open-next/dist/queue/*`) +- `--dangerously-use-unsupported-next-version` - Since `@opennextjs/aws` 3.9.13, the build fails when the Next.js version is no longer supported by the Next.js team (see the [Next.js support policy](https://nextjs.org/support-policy)) as it may contain unpatched security vulnerabilities. This flag allows building anyway. ### Configuration File diff --git a/pages/aws/config/custom_overrides.mdx b/pages/aws/config/custom_overrides.mdx index c9b29ca9..8f175e9e 100644 --- a/pages/aws/config/custom_overrides.mdx +++ b/pages/aws/config/custom_overrides.mdx @@ -312,7 +312,7 @@ const config = { export default config; ``` -[Included](https://github.com/opennextjs/opennextjs-aws/tree/main/packages/open-next/src/overrides/tagCache) ones are `'dynamodb' | 'dynamodb-lite' | 'fs-dev' | 'dummy'` +[Included](https://github.com/opennextjs/opennextjs-aws/tree/main/packages/open-next/src/overrides/tagCache) ones are `'dynamodb' | 'dynamodb-lite' | 'dynamodb-nextMode' | 'fs-dev' | 'fs-dev-nextMode' | 'dummy'` ## Custom Origin Resolver @@ -418,7 +418,7 @@ import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; const config = { default: {}, middleware: { - assetResolver: import("./customAssetResolver").then((mod) => mod.default), + assetResolver: () => import("./customAssetResolver").then((mod) => mod.default), }, } satisfies OpenNextConfig; diff --git a/pages/aws/config/overrides/automatic_cdn_invalidation.mdx b/pages/aws/config/overrides/automatic_cdn_invalidation.mdx index c9a9c098..49c2e4db 100644 --- a/pages/aws/config/overrides/automatic_cdn_invalidation.mdx +++ b/pages/aws/config/overrides/automatic_cdn_invalidation.mdx @@ -7,6 +7,34 @@ It will be called for `revalidatePath`, `revalidateTag` and `res.revalidate()`. If you want to better understand how to implement your own Automatic CDN Invalidation, the easiest way would be to take a look at the existing [included Automatic CDN Invalidation](https://github.com/opennextjs/opennextjs-aws/tree/main/packages/open-next/src/overrides/cdnInvalidation) +## Custom implementations + +An Automatic CDN Invalidation override must implement `invalidatePaths` and can optionally implement `invalidateTags`: + +```ts +type CDNInvalidationHandler = { + name: string; + invalidatePaths: (paths: CDNPath[]) => Promise; + invalidateTags?: (tags: string[]) => Promise; +}; +``` + +### `invalidatePaths` + +Invalidates the CDN entries of the given paths. Each path includes the path before (`initialPath`) and after (`rawPath`) routing as well as the routes that matched while resolving the request (`resolvedRoutes`). + +For `revalidateTag` and `revalidatePath`, the paths are resolved from the tags by the [Tag Cache](/aws/config/overrides/tag_cache). Tag caches in `nextMode` can only resolve paths when they implement the optional `getPathsByTags` method. + +### `invalidateTags` + +Available since `@opennextjs/aws` 4.1.14 + +If your CDN supports invalidating its cache by tags, you can implement `invalidateTags` to invalidate the original cache tags directly instead of resolving them to paths. + +- It is called once per `revalidateTag` or `revalidatePath` call, after the tag cache has been updated, with the tags as passed to Next.js: hard tags from `revalidateTag()` and soft tags (prefixed with `_N_T_/`) from `revalidatePath()`. Tags are not expanded, i.e. the hard tags of the paths matching a soft tag are not added. +- When it is provided, `invalidatePaths` is no longer called for `revalidateTag` and `revalidatePath`. It is still called for `res.revalidate()` in the Pages Router. +- The CDN integration must associate cached entries with the same tags, both hard and soft, for the invalidation to have an effect. + ## Included Automatic CDN Invalidation ### dummy diff --git a/pages/aws/config/overrides/converter.mdx b/pages/aws/config/overrides/converter.mdx index b23346d1..cfa722e4 100644 --- a/pages/aws/config/overrides/converter.mdx +++ b/pages/aws/config/overrides/converter.mdx @@ -27,7 +27,7 @@ The `aws-cloudfront` Converter is a converter for Cloudfront (i.e. lambda@edge) The `edge` Converter is the converter for everything that uses `Request` and `Response`. At the moment with the builtin one that's only Cloudflare. -### converter +### node The `node` Converter is a converter for classic Node.js server. diff --git a/pages/aws/config/overrides/incremental_cache.mdx b/pages/aws/config/overrides/incremental_cache.mdx index e0cbc312..2967ad17 100644 --- a/pages/aws/config/overrides/incremental_cache.mdx +++ b/pages/aws/config/overrides/incremental_cache.mdx @@ -8,6 +8,8 @@ If you want to better understand how to implement your own IncrementalCache, the One thing to note is that it is not used at build time, only at runtime. This means that you'll have to upload the cache yourself if you want to use the prebuilt routes/pages (And this is mandatory for ISR/SSG routes with `fallback:false`). All the cache files are present in the `.open-next/cache` folder. The one under `BUILD_ID` are for the ISR/SSG cache and the one under `__fetch/BUILD_ID` are for the fetch cache. +`BUILD_ID` is the OpenNext build id (`process.env.OPEN_NEXT_BUILD_ID` at runtime): the Next.js [`deploymentId`](https://nextjs.org/docs/app/api-reference/config/next-config-js/deploymentId) when set and the Next.js build id otherwise. +This applies to every supported Next.js version. It matters most on Next.js 16.2+, where the Next.js build id is the same for every deployment when `deploymentId` is set. ## Included IncrementalCache diff --git a/pages/aws/config/overrides/queue.mdx b/pages/aws/config/overrides/queue.mdx index c4d2961e..53cd61d4 100644 --- a/pages/aws/config/overrides/queue.mdx +++ b/pages/aws/config/overrides/queue.mdx @@ -1,13 +1,17 @@ import { Callout } from "nextra/components"; This override is used by OpenNext to trigger the revalidation of stale routes. -Before sending the response to the client, OpenNext will check if the route is stale and if it is, it will call the queue override to revalidate the route. +When a stale route is served, OpenNext calls the `send` method of the queue override to revalidate the route: + +- When the route is rendered by the Next server, `send` is called once the response is sent (in `waitUntil` when the wrapper provides one). +- With [cache interception](/aws/inner_workings/cache_interception), `send` is awaited before the stale response is returned. If it throws, the error is logged and the request falls back to the Next server. If you want to better understand how to implement your own Queue, the easiest way would be to take a look at the existing [included Queue](https://github.com/opennextjs/opennextjs-aws/tree/main/packages/open-next/src/overrides/queue). Couple of things to note : - The default implementation use an SQS queue. This has the main advantage of being able to control the concurrency of the revalidations as well as avoiding trigerring the revalidation multiple times for the same route. +- The promise returned by `send` should resolve once the message is accepted (or handed to `waitUntil`), not once the route is revalidated, so that the response is not delayed. - You don't have to use a queue at all. You could trigger the revalidation directly in the Queue override itself. You can see a very simple implementation of this [in the `direct` override](https://github.com/opennextjs/opennextjs-aws/blob/main/packages/open-next/src/overrides/queue/direct.ts). ## Included Queue @@ -17,6 +21,8 @@ Couple of things to note : The SQS Queue will send a message to an SQS queue for each route that needs to be revalidated. It is used by default if you don't provide any Queue in your configuration. It uses the `@aws-sdk/client-sqs` to interact with SQS. +Since `@opennextjs/aws` 4.1.6, the message is sent in the background when the wrapper provides a `waitUntil` (i.e. `aws-lambda-streaming` or `aws-apigw-streaming`) so that it does not delay the response. Otherwise, the response is sent once the message has been sent. The same applies to `sqs-lite`. + #### Requirements - You need to provide the `REVALIDATION_QUEUE_URL` environment variable to your server. @@ -35,7 +41,9 @@ This implementation is a lighter version of the `sqs` Queue as it uses `aws4fetc The Direct Queue will directly trigger the revalidation of the route in the Queue override itself. It is useful for development purposes and when you don't want to use a queue. - Be careful with this implementation as it could lead to multiple revalidations of the same route. + Be careful with this implementation as it could lead to multiple revalidations of the same route. As `send` + resolves once the route is revalidated, it also delays the stale response when cache interception is + enabled. ### dummy diff --git a/pages/aws/config/overrides/tag_cache.mdx b/pages/aws/config/overrides/tag_cache.mdx index a5f78e9f..23aabbcd 100644 --- a/pages/aws/config/overrides/tag_cache.mdx +++ b/pages/aws/config/overrides/tag_cache.mdx @@ -1,3 +1,5 @@ +import { Callout } from "nextra/components"; + This override is used by the [`cache` adapter](https://github.com/opennextjs/opennextjs-aws/blob/main/packages/open-next/src/adapters/cache.ts) that is provided by OpenNext to the NextServer. It is also used by OpenNext if `enableCacheInterception` is set to `true` in the configuration and by the `initializationFunction` for prepopulating the cache. It is used for the cache tags (i.e. `revalidateTag` and `revalidatePath`). It does not handle anything related to the ISR/SSG cache or the fetch cache used by Next.js. @@ -7,9 +9,33 @@ The current implementation differs from the way Next.js standalone handle cache We choose to go another way. We store tags for every path that exists in the cache. This way, we can easily know if a path is stale by just checking the path instead of every tags used. This approach is more write heavy but it's a tradeoff we are willing to make. This also has the drawback of needing to prepopulate the cache with all the tags that are used in the application. This is done by the `initializationFunction`. -We might allow to choose how cache tags are handled in the future depending on the `TagCache` implementation. If you need this feature, feel free to open an issue on the [GitHub repository](https://github.com/opennextjs/opennextjs-aws). +## Modes + +A `TagCache` can work in one of two modes, set with its `mode` property: + +- `original` (default) - The approach described above. Tags are stored for every path, so a single lookup per path is needed on read. It needs to be prepopulated at build time and is write heavy. It allows the [automatic CDN invalidation](/aws/config/overrides/automatic_cdn_invalidation) to resolve the paths of a tag. +- `nextMode` - Similar to the way Next.js handles tags. Only the time at which a tag has been revalidated is stored, and every tag of an entry is checked on read. It doesn't need to be prepopulated and writes very little, but reads more. Path resolution for the automatic CDN invalidation is only available when the implementation provides the optional `getPathsByTags` method. + +## Custom implementations + +If you want to better understand how to implement your own TagCache, the easiest way would be to take a look at the existing [included TagCache](https://github.com/opennextjs/opennextjs-aws/tree/main/packages/open-next/src/overrides/tagCache) and at the [types](https://github.com/opennextjs/opennextjs-aws/blob/main/packages/open-next/src/types/overrides.ts). + +### Stale-while-revalidate + +Since `@opennextjs/aws` 3.10.0, `revalidateTag(tag, profile)` (Next.js 16+) can mark the entries as stale instead of expiring them right away. Stale entries are still served while they are revalidated in the background. + +To support it, a `TagCache` must store the optional `stale` and `expire` timestamps (in milliseconds since epoch) passed to `writeTags` and implement the optional `isStale` method: + +- `original` mode: `isStale(path: string, lastModified?: number): Promise` +- `nextMode`: `isStale(tags: string[], lastModified?: number): Promise` -If you want to better understand how to implement your own TagCache, the easiest way would be to take a look at the existing [included TagCache](https://github.com/opennextjs/opennextjs-aws/tree/main/packages/open-next/src/overrides/tagCache). +Without `isStale`, stale entries are not detected, only expired ones are. + + + The signature of `writeTags` changed in `@opennextjs/aws` 3.10.0. It now receives `OriginalTagCacheWriteInput[]` + (`{ tag, path, revalidatedAt?, stale?, expire? }`) in the `original` mode and `NextModeTagCacheWriteInput[]` + (`string | { tag, stale?, expire? }`) in `nextMode`. Custom implementations must be updated. + ## Included TagCache @@ -35,7 +61,13 @@ This implementation is a lighter version of the `dynamodb` TagCache as it uses ` ### dynamodb-nextMode -TBA +The DynamoDBNextMode TagCache will store the time at which each tag has been revalidated in a DynamoDB table. It works in `nextMode` and uses `aws4fetch` to interact with DynamoDB. +It doesn't need to be prepopulated by the `initializationFunction`, and it doesn't support resolving the paths of a tag for the automatic CDN invalidation. + +##### Requirements + +- You need to provide the `CACHE_DYNAMO_TABLE`, `CACHE_BUCKET_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_SESSION_TOKEN` environment variables to your server. +- DynamoDB table should have a primary key `path` of type `String` and a sort key `tag` of type `String`. (It can reuse the same table as the one used by the default `tagCache`) ### dummy @@ -44,3 +76,7 @@ The Dummy TagCache is a dummy implementation that will throw an exception. It sh ### fs-dev The FsDev TagCache is a simple implementation that stores the cache tags in a file in the `.open-next/cache` folder and interact with it using the filesystem. It is useful for development purposes only. + +### fs-dev-nextMode + +The FsDevNextMode TagCache is a simple implementation that works in `nextMode` and stores the cache tags in memory. It is useful for development purposes only. diff --git a/pages/aws/config/overrides/wrapper.mdx b/pages/aws/config/overrides/wrapper.mdx index 1728b3c1..85e90580 100644 --- a/pages/aws/config/overrides/wrapper.mdx +++ b/pages/aws/config/overrides/wrapper.mdx @@ -7,7 +7,7 @@ Couple of things to note : - If you want to use streaming (for the `node` runtime of next), you'll need to create a `StreamCreator` and pass it to the `handler`. You can find some example in the [node](https://github.com/opennextjs/opennextjs-aws/blob/main/packages/open-next/src/overrides/wrappers/node.ts) or [aws-lambda-streaming](https://github.com/opennextjs/opennextjs-aws/blob/main/packages/open-next/src/overrides/wrappers/aws-lambda-streaming.ts) wrapper. - If you don't use streaming (like in the default `aws-lambda` wrapper), you may still need to provide a fake `StreamCreator` to the `handler` to avoid a weird issue with Node itself (see [here](https://github.com/opennextjs/opennextjs-aws/blob/f685ddea8f8a5c82591dc02713aff7138f2d9896/packages/open-next/src/overrides/wrappers/aws-lambda.ts#L49-L65) for an example and a more thorough explanation). - If you use the `edge` runtime of Next (either for the external middleware or for an `edge` route or page), you don't need the `StreamCreator` at all. -- If you are in a serverless environment and it supports `waitUntil`, you should pass it to the handler as it is done in the [cloudflare-edge wrapper](https://github.com/opennextjs/opennextjs-aws/blob/87b59e59a8a0bf1e83d2d1df9efa0ad3bfc912f5/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts#L65-L67). This might not be necessary depending on where you run it (for example the `aws-lambda-streaming` or the `node` wrapper doesn't need it.) +- If you are in a serverless environment and it supports `waitUntil`, you should pass it to the handler as it is done in the [cloudflare-edge wrapper](https://github.com/opennextjs/opennextjs-aws/blob/87b59e59a8a0bf1e83d2d1df9efa0ad3bfc912f5/packages/open-next/src/overrides/wrappers/cloudflare-edge.ts#L65-L67). The `aws-lambda-streaming` and `aws-apigw-streaming` wrappers provide their own `waitUntil` (pending promises are awaited before the response stream is closed). This might not be necessary depending on where you run it (for example the `node` wrapper doesn't need it as it is a long running server.) ## Included Wrappers @@ -22,12 +22,48 @@ The `aws-lambda` Wrapper is the default wrapper for AWS Lambda. It is used by de ### aws-lambda-streaming -The `aws-lambda-streaming` Wrapper is a wrapper that allows you to use streaming in AWS Lambda. Streaming must be enabled for this lambda. +The `aws-lambda-streaming` Wrapper is a wrapper that allows you to use streaming in AWS Lambda. Streaming must be enabled for this lambda and it must be invoked through a Lambda Function URL. + +The response is compressed (`br`, `gzip` or `deflate`) according to the `Accept-Encoding` header of the request. + +#### Features + +- [x] Streaming +- [x] Proper support for `waitUntil` (available since `@opennextjs/aws` 4.1.6) +- [x] Compression + +### aws-apigw-streaming + +Available since `@opennextjs/aws` 4.1.0 + +The `aws-apigw-streaming` Wrapper allows you to use streaming when the server function is behind an [API Gateway REST API with response streaming](https://aws.amazon.com/about-aws/whats-new/2025/11/api-gateway-response-streaming-rest-apis/) (`ResponseTransferMode: STREAM`) instead of a Lambda Function URL. + +It should be used with the `aws-apigw-v1` (REST API) or `aws-apigw-v2` (HTTP API) converter. + +Unlike `aws-lambda-streaming`, it never compresses the response because API Gateway streaming does not support content encoding. Compression should be handled by the CDN in front of API Gateway (e.g. CloudFront). #### Features - [x] Streaming - [x] Proper support for `waitUntil` +- [ ] Compression + +### aws-lambda-compressed + +Available since `@opennextjs/aws` 3.5.6 + +The `aws-lambda-compressed` Wrapper is the same as the `aws-lambda` Wrapper but it also compresses the response (`br`, `gzip` or `deflate`) according to the `Accept-Encoding` header of the request. +It can be useful to reduce the size of the response as AWS Lambda has a limit of 6MB for the response payload. + +#### Features + +- [ ] Streaming +- [ ] Proper support for `waitUntil` +- [x] Compression + +### Compression + +The Brotli compression quality used by the `aws-lambda-streaming` and `aws-lambda-compressed` wrappers can be set with the `BROTLI_QUALITY` environment variable (from `0` to `11`). It defaults to `6`. ### cloudflare-edge diff --git a/pages/aws/config/reference.mdx b/pages/aws/config/reference.mdx index cad02f40..7c4db2cc 100644 --- a/pages/aws/config/reference.mdx +++ b/pages/aws/config/reference.mdx @@ -40,7 +40,7 @@ Each server config will be under it's own key in the config object. Here are all This is the entrypoint of the server. Be aware that it might not be enough to change this to make it work on said platform. This is only the entrypoint, you might need to change other parts of the server to make it work on the platform. - Possible values are: `aws-lambda`, `aws-lambda-streaming`, `cloudflare-edge`, `cloudflare-node`, `node`, `express-dev`, `dummy` or a `LazyLoadedOverride` that will return the wrapper. + Possible values are: `aws-lambda`, `aws-lambda-compressed`, `aws-lambda-streaming`, `aws-apigw-streaming`, `cloudflare-edge`, `cloudflare-node`, `node`, `express-dev`, `dummy` or a `LazyLoadedOverride` that will return the wrapper. #### [`converter`](overrides/converter) @@ -61,17 +61,22 @@ Each server config will be under it's own key in the config object. Here are all ##### `arch` - This is the architecture of the server. It is a string and by default, it is set to `x64`. + This is the architecture of the server. It is passed to npm as `--cpu` to select the platform specific optional dependencies (i.e. sharp's `@img/*` packages). + It is not set by default. Note that when `install` is not set for the image optimization server, sharp is installed for `arm64`, `linux` and `glibc`. Possible values are: `x64`, `arm64` - ##### `nodeVersion` + ##### `os` - This option will be passed to the postinstall script of sharp. It is used by `node-gyp`. + The operating system of the server. It is passed to npm as `--os`. By default, it is set to `linux`. + + ##### `nodeVersion` - Deprecated + + This option is ignored since `@opennextjs/aws` 4.1.14 because npm no longer supports the `--target` flag. ##### `libc` - Override libc of native modules to install. + Override libc of native modules to install. It is passed to npm as `--libc`. Possible values are: `glibc`, `musl` @@ -121,6 +126,18 @@ Each server config will be under it's own key in the config object. Here are all This option is deprecated and doesn't work for Next 14.2+. + ### `routePreloadingBehavior` + + Next.js loads the code of a route the first time it is requested. This option allows to preload all the routes of the server ahead of time to reduce the latency of the first requests. + You should do your own testing to choose the one that suits you best. Route preloading requires Next.js 15+. + + Possible values are: + + - `none` - No preloading of the routes. This is the default. + - `withWaitUntil` - Preload the routes in the background of the first request using the `waitUntil` provided by the wrapper. It falls back to `none` when the wrapper doesn't provide a `waitUntil` (i.e. the `aws-lambda-streaming`, `aws-apigw-streaming` and Cloudflare wrappers provide one). + - `onWarmerEvent` - Preload the routes on the warmer event. It must be implemented by the wrapper, it is only supported by the `aws-lambda-streaming` and `aws-apigw-streaming` wrappers. + - `onStart` - Preload the routes before the handler is created. This is a blocking operation that may increase the cold start time a lot. It is useful for long running servers or in serverless with careful testing. + ### `override` For both default and splitted server, in addition to `override` in the common options, you can also use the following options: @@ -135,13 +152,13 @@ Each server config will be under it's own key in the config object. Here are all Add possibility to override the default tag cache. Used for revalidateTags and revalidatePath. - Possible values are: `dynamodb`, `dynamodb-lite`, `fs-dev`, `dummy` or a `LazyLoadedOverride` that will return the tag cache. + Possible values are: `dynamodb`, `dynamodb-lite`, `dynamodb-nextMode`, `fs-dev`, `fs-dev-nextMode`, `dummy` or a `LazyLoadedOverride` that will return the tag cache. #### [`queue`](overrides/queue) Add possibility to override the default queue. Used for ISR revalidation requests. - Possible values are: `sqs`, `sqs-lite`, `dummy` or a `LazyLoadedOverride` that will return the queue. + Possible values are: `sqs`, `sqs-lite`, `direct`, `dummy` or a `LazyLoadedOverride` that will return the queue. #### [`proxyExternalRequest`](overrides/proxy_external_request) @@ -153,7 +170,7 @@ Each server config will be under it's own key in the config object. Here are all Add possibility to override the CDN invalidation override. Used for on-demand invalidation with `revalidatePath()`, `revalidateTag()` and `res.revalidate()`. - Possible values are: `cloudfront`, `dummy` or a `LazyLoadedOverride` that will return the invalidatePaths. This override is disabled by default. + Possible values are: `cloudfront`, `dummy` or a `LazyLoadedOverride` that will return the CDN invalidation handler. This override is disabled by default (`dummy`). @@ -239,6 +256,8 @@ Each server config will be under it's own key in the config object. Here are all Possible values are: `dynamodb`, `dynamodb-lite`, `fs-dev`, `dummy` or a `LazyLoadedOverride` that will return the tag cache. + Tag caches in `nextMode` (i.e. `dynamodb-nextMode`) do not need to be initialized. + @@ -318,5 +337,13 @@ headersAndCookiesPriority: (event) => { }, ``` +#### `middlewareHeadersOverrideNextConfigHeaders` + +Available since `@opennextjs/aws` 3.8.0 + +When set to `true`, the response headers set by the middleware take precedence over the headers set via the [`headers` option](https://nextjs.org/docs/app/api-reference/config/next-config-js/headers) of the Next config, in line with the [Next.js execution order](https://nextjs.org/docs/app/api-reference/file-conventions/middleware#execution-order). + +It's a boolean and by default, it is set to `false` (the headers from the Next config take precedence). + diff --git a/pages/aws/index.mdx b/pages/aws/index.mdx index ca5d6ab5..f23e482f 100644 --- a/pages/aws/index.mdx +++ b/pages/aws/index.mdx @@ -28,7 +28,7 @@ We need your help keeping it up to date and feature complete. Make sure to [**jo --- -OpenNext aims to support all Next.js 15 features and is widely deployed in production across multiple platforms. +OpenNext aims to support all Next.js 15 and 16 features and is widely deployed in production across multiple platforms. [NHS England](https://github.com/nhs-england-tools/terraform-aws-opennext), [Udacity](https://engineering.udacity.com/deploying-next-js-on-the-edge-with-sst-is-sst-the-game-changer-its-claimed-to-be-1f05a0abc27c), [Gymshark UK](https://uk.gymshark.com), [SST](https://sst.dev), [Cloudflare](https://www.cloudflare.com) diff --git a/pages/aws/inner_workings.mdx b/pages/aws/inner_workings.mdx index f1488d99..61049c69 100644 --- a/pages/aws/inner_workings.mdx +++ b/pages/aws/inner_workings.mdx @@ -2,7 +2,7 @@ When calling `open-next build`, OpenNext **runs `next build`** to build the Next #### Building the Next.js app -OpenNext runs the `build` script in your `package.json` file. Depending on the lock file found in the app, the corresponding packager manager will be used. Either `npm run build`, `yarn build`, or `pnpm build` will be run. For more on customizing the build command, see [overriding the build command](/aws/advanced/options#custom-build-command). +OpenNext runs the `build` script in your `package.json` file. Depending on the lock file found in the app, the corresponding packager manager will be used. Either `npm run build`, `yarn build`, or `pnpm build` will be run. For more on customizing the build command, see [the `buildCommand` option](/aws/config/reference). #### Transforming the build output diff --git a/pages/aws/inner_workings/caching.mdx b/pages/aws/inner_workings/caching.mdx index a3439ab6..695b33ad 100644 --- a/pages/aws/inner_workings/caching.mdx +++ b/pages/aws/inner_workings/caching.mdx @@ -19,9 +19,13 @@ They use this header by default `s-maxage=YOUR_REVALIDATION_TIME, stale-while-re **All of these issues are automatically fixed for you in OpenNext** +Next.js can also set a cacheable `Cache-Control` header on some error responses (i.e. `notFound()` on an ISR route). To prevent the CDN from caching a transient error for the whole revalidation period, OpenNext overrides the `Cache-Control` header of 404 and 500 responses with `private, no-cache, no-store, max-age=0, must-revalidate`. You can opt out by setting the `OPEN_NEXT_DANGEROUSLY_SET_ERROR_HEADERS` environment variable to `true`. + ### Cloudfront cache invalidation -When you manually revalidate the Next.js cache for a specific page, the ISR cache files stored on S3 will be updated. However, it is still necessary to invalidate the CloudFront cache: +When you manually revalidate the Next.js cache for a specific page, the ISR cache files stored on S3 will be updated. However, it is still necessary to invalidate the CloudFront cache. + +This can be done automatically by the [Automatic CDN Invalidation](/aws/config/overrides/automatic_cdn_invalidation) override (i.e. `cdnInvalidation: "cloudfront"`). Otherwise you can invalidate the CloudFront cache manually: ```js filename="pages/api/revalidate.js" export default async function handler(req, res) { @@ -31,7 +35,7 @@ export default async function handler(req, res) { } ``` -If the pages router is in use, you must also invalidate the `_next/data/BUILD_ID/foo.json` path. The value for `BUILD_ID` can be found in the `.next/BUILD_ID` build output and can be accessed at runtime via the `process.env.NEXT_BUILD_ID` environment variable. +If the pages router is in use, you must also invalidate the `_next/data/BUILD_ID/foo.json` path. The value for `BUILD_ID` is the Next.js build id. It can be found in the `.next/BUILD_ID` build output and can be accessed at runtime via the `process.env.NEXT_BUILD_ID` environment variable. ```ts await invalidateCloudFrontPaths(["/foo", `/_next/data/${process.env.NEXT_BUILD_ID}/foo.json`]); @@ -75,7 +79,9 @@ await invalidateCloudFrontPaths(["/page/a", "/page/b", "/page/c"]); await invalidateCloudFrontPaths(["/page/*"]); ``` -For on-demand revalidation via the [`next/cache` module](https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating#using-on-demand-revalidation), if you want to retrieve the associated paths for a given tag, you can use this function: +For on-demand revalidation via the [`next/cache` module](https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating#using-on-demand-revalidation), if you want to retrieve the associated paths for a given tag, you can use this function. + +Note that the tags are prefixed with the OpenNext build id (`process.env.OPEN_NEXT_BUILD_ID`), which is the Next.js [`deploymentId`](https://nextjs.org/docs/app/api-reference/config/next-config-js/deploymentId) when set and the Next.js build id otherwise. ```ts import { DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb"; @@ -92,11 +98,11 @@ async function getPaths(tag: string) { "#tag": "tag", }, ExpressionAttributeValues: { - ":tag": { S: `${process.env.NEXT_BUILD_ID}/${tag}` }, + ":tag": { S: `${process.env.OPEN_NEXT_BUILD_ID}/${tag}` }, }, }) ); - return Items?.map((item) => item.path?.S?.replace(`${process.env.NEXT_BUILD_ID}/`, "") ?? "") ?? []; + return Items?.map((item) => item.path?.S?.replace(`${process.env.OPEN_NEXT_BUILD_ID}/`, "") ?? "") ?? []; } catch (e) { console.error("Failed to get by tag", e); return []; diff --git a/pages/aws/inner_workings/components/overview.mdx b/pages/aws/inner_workings/components/overview.mdx index d415d6f0..abf498ee 100644 --- a/pages/aws/inner_workings/components/overview.mdx +++ b/pages/aws/inner_workings/components/overview.mdx @@ -9,7 +9,7 @@ This is also mostly for advanced use cases (Overriding some default, provide sup ## General -- For the node runtime, you need at least Node 18. +- For the node runtime, you need a Node.js version supported by your version of Next.js (Node.js 20.9+ for Next.js 16, Node.js 18.18+ for Next.js 15). - For the edge runtime, you can use both Node 18+ or cloudflare workers with `node_compat` flag enabled (Cloudflare workers support is experimental) - Open-next doesn't work well on Windows. We recommend using WSL2 or a Linux VM. @@ -63,9 +63,16 @@ The `handler` is the original handler that is being wrapped. The `converter` is Here is a list of the provided wrappers: - `aws-lambda` - The default AWS Lambda wrapper - It is the default wrapper -- `aws-lambda-streaming` - The AWS Lambda wrapper with streaming support +- `aws-lambda-compressed` - The AWS Lambda wrapper with compression support +- `aws-lambda-streaming` - The AWS Lambda wrapper with streaming support (Lambda Function URL) +- `aws-apigw-streaming` - The AWS Lambda wrapper with streaming support behind an API Gateway - `node` - The Node wrapper - Create a node server, not suitable for serverless -- `cloudflare` - The Cloudflare wrapper - For Cloudflare Worker +- `cloudflare-edge` - The Cloudflare wrapper for the edge runtime - For Cloudflare Workers +- `cloudflare-node` - The Cloudflare wrapper for the node runtime - Used by `@opennextjs/cloudflare` +- `express-dev` - The Express wrapper - For development only +- `dummy` - The Dummy wrapper - Forwards the event to the handler + +See the [wrapper override](/aws/config/overrides/wrapper) page for more details. ##### Aws Lambda streaming diff --git a/pages/aws/inner_workings/components/server/node.mdx b/pages/aws/inner_workings/components/server/node.mdx index 69287322..85035f52 100644 --- a/pages/aws/inner_workings/components/server/node.mdx +++ b/pages/aws/inner_workings/components/server/node.mdx @@ -52,6 +52,8 @@ File in S3 should follow the following structure: - `Key_Prefix/BUILD_ID/path/to/page.cache` - For the cache of a page - `Key_Prefix/__fetch/BUILD_ID/fetch-cache-key` - For the fetch cache +`BUILD_ID` is the OpenNext build id (`process.env.OPEN_NEXT_BUILD_ID`): the Next.js `deploymentId` when set and the Next.js build id otherwise. + The default S3 Incremental Cache can be configured using the following environment variables: ###### Environment Variables @@ -110,6 +112,9 @@ interface QueueMessage { MessageBody: { host: string; url: string; + // Time of the cache entry (ms since epoch) + lastModified: number; + eTag: string; }; MessageGroupId: string; } @@ -120,7 +125,9 @@ export interface Queue { } ``` -The send function will be called when a page is marked as `STALE`. You don't have to use a queue here, you can use any other mechanism to send the message to the revalidation worker or even do the revalidation in the same process. +The send function will be called when a page is marked as `STALE`. + +`MessageBody.lastModified` is the time of the cache entry, so a queue can skip the messages for entries that were revalidated since. Before `@opennextjs/aws` 4.1.16, it was `1` for pages made stale by `revalidateTag`. `MessageDeduplicationId` is derived from the path, the `ETag` and the time of the entry (`1` for every entry made stale by a tag, so that they are deduplicated). You don't have to use a queue here, you can use any other mechanism to send the message to the revalidation worker or even do the revalidation in the same process. #### Shared Environment Variables diff --git a/pages/aws/migration.mdx b/pages/aws/migration.mdx index f1b0d2d3..178e7c99 100644 --- a/pages/aws/migration.mdx +++ b/pages/aws/migration.mdx @@ -21,7 +21,7 @@ Here are the old build args and their new equivalent: Everything should already be set up for you. But there are a few things you should know: -- Streaming by default is disabled in open-next (for now). It is very **experimental** right now see [here](/aws/inner_workings/streaming) for more info. +- Streaming is disabled by default in OpenNext. To enable it in AWS Lambda, use the `aws-lambda-streaming` or `aws-apigw-streaming` [wrapper](/aws/config/overrides/wrapper). - Middleware is not run for static file and for image requests with open-next. #### AWS Amplify