Architecture overview
OpenNext does not create the underlying infrastructure. You can create the infrastructure for your app with your preferred tool — SST, AWS CDK, Terraform, Serverless Framework, etc.
This is the recommended setup.
We are going to take a look at every component created by OpenNext.
Asset files
OpenNext will create a .open-next/assets
folder containing the hashed and un-hashed files. Those files can be directly served without going through the server function. Files in .open-next/assets
should be served from the root of your website. For example, the file .open-next/assets/favicon.ico
should be served from /favicon.ico
.
There are two types of files in the .open-next/assets
folder:
Hashed files
These are files with a hash component in the file name. Hashed files are be found in the .open-next/assets/_next
folder, such as .open-next/assets/_next/static/css/0275f6d90e7ad339.css
. The hash values in the filenames are guaranteed to change when the content of the files is modified. Therefore, hashed files should be cached both at the CDN level and at the browser level. The recommended cache control setting for these file is
public,max-age=31536000,immutable
Un-hashed files
Other files inside the .open-next/assets
folder are copied from your app's public/
folder, such as .open-next/assets/favicon.ico
. The filename for un-hashed files may remain unchanged when the content is modified. Un-hashed files should be cached at the CDN level, but not at the browser level. When the content of un-hashed files is modified, the CDN cache should be invalidated on deploy. The recommended cache control setting for these file is
public,max-age=0,s-maxage=31536000,must-revalidate
Cache files
OpenNext will create a .open-next/cache
folder containing cache data used by the incremental cache(i.e. ISR and SSG routes). These files should not be publicly accessible.
There are two types of caches in the .open-next/cache
folder:
- Route cache: This cache includes
html
andjson
orrsc
files that are prerendered during the build. They are merged into a single.cache
file. They are used to seed the revalidation cache. - Fetch cache: This cache includes fetch call responses, which might contain sensitive information. Make sure these files are not publicly accessible.
Image optimization backend
This backend handles image optimization requests when the Next.js <Image>
component is used. The sharp (opens in a new tab) library, which is bundled with the function, is used to convert the image. The library is compiled against the selected architecture (by default arm64
) and is intended to run on Node.
Note that the image optimization backend responds with the Cache-Control
header, so the image will be cached both at the CDN level and at the browser level.
See Image Optimization for more details.
Servers Lambda backend
These backends handles all other types of requests from the Next.js app, including Server-side Rendering (SSR) requests, Incremental Static Regeneration (ISR) requests, Static Site Generation requests (SSG) and API requests. OpenNext builds the Next.js app in standalone mode. The standalone mode generates a .next
folder containing the NextServer class that handles requests and a node_modules
folder with all the dependencies needed to run the NextServer
. The structure looks like this:
.next/ -> NextServer
node_modules/ -> dependencies
The server backend adapter wraps around NextServer
and exports a handler function that supports the Lambda request and response. The server-function
bundle looks like this:
.next/ -> NextServer
+ .open-next/
node_modules/ -> dependencies
+ index.mjs -> server function adapter
Monorepo
In the case of a monorepo, the build output looks slightly different. For example, if the app is located in packages/web
, the build output looks like this:
packages/
web/
.next/ -> NextServer
node_modules/ -> dependencies from root node_modules (optional)
node_modules/ -> dependencies from package node_modules
In this case, the server function adapter needs to be created inside packages/web
next to .next/
. This is to ensure that the adapter can import dependencies from both node_modules
folders. It is not a good practice to have the Lambda configuration coupled with the project structure, so instead of setting the Lambda handler to packages/web/index.mjs
, we will add a wrapper index.mjs
at the server-function
bundle root that re-exports the adapter. The resulting structure looks like this:
packages/
web/
.next/ -> NextServer
+ .open-next/
node_modules/ -> dependencies from root node_modules (optional)
+ index.mjs -> server function adapter
node_modules/ -> dependencies from package node_modules
+ index.mjs -> adapter wrapper
This ensures that the function handler remains at index.mjs
.
Revalidation backend
OpenNext will create a .open-next/revalidation-function
folder containing the revalidation backend.
This backend is supposed to handle revalidation requests from the revalidation queue. The revalidation queue is a FIFO queue that contains messages for revalidating routes. The revalidation backend polls the queue for messages and sends a HEAD request to the specified route for revalidation.
Warmer backend
OpenNext will create a .open-next/warmer-function
folder containing the warmer backend.
Read more on how warming works.
Tag Provider backend
This is used to populate the revalidation table with tags.