### Install Dependencies for SST Source: https://context7.com/opennextjs/docs/llms.txt Run this command after initializing SST to install project dependencies. ```bash npm install ``` -------------------------------- ### Install Sharp on Default Server (ARM64) Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/reference.mdx Example of installing the `sharp` package with ARM64 architecture on the default server function. This configuration is useful for image optimization on AWS Lambda. ```typescript import type { OpenNextConfig } from '@opennextjs/aws/types/open-next'; const config = { default: { install: { packages: ['sharp@0.33.5'], ``` -------------------------------- ### Install SST Dependencies Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Command to install SST dependencies and resolve type issues after initial setup. ```sh cd apps/next-site && sst install ``` -------------------------------- ### Netlify CLI Initialization Source: https://context7.com/opennextjs/docs/llms.txt Initialize a new Netlify project using the Netlify CLI. This command guides you through connecting your repository and setting up the project. ```bash # Using Netlify CLI netlify init ``` -------------------------------- ### Preview OpenNext Application Locally Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/cli.mdx Populates the local cache and starts a local development server using wrangler dev for local previewing. ```bash pnpm opennextjs-cloudflare preview ``` -------------------------------- ### Migrate Existing Next.js Project to OpenNext Cloudflare Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/cli.mdx Automates the setup for converting a standard Next.js project into an OpenNext-compatible one, including dependency installation, configuration file creation, and R2 bucket setup. ```bash npx @opennextjs/cloudflare migrate ``` -------------------------------- ### Run SST Development Server with Nx Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Command to start the SST development server, integrating with Nx for the build process. ```sh sst dev "nx dev next-site" ``` -------------------------------- ### Splitted Server Routes Configuration Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/reference.mdx Example of configuring routes for a splitted server, specifying which routes to include. ```typescript routes: ["app/api/test/route", "app/page", "pages/admin"] ``` -------------------------------- ### Cloudflare Local Development Setup Source: https://context7.com/opennextjs/docs/llms.txt Initialize OpenNext Cloudflare bindings for local development using `initOpenNextCloudflareForDev`. Optionally enable remote bindings with the `experimental.remoteBindings` option. ```typescript // next.config.ts import type { NextConfig } from "next"; import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; const nextConfig: NextConfig = { /* config options here */ }; export default nextConfig; // Initialize Cloudflare dev bindings initOpenNextCloudflareForDev(); // For remote bindings during development // initOpenNextCloudflareForDev({ // experimental: { remoteBindings: true } // }); ``` -------------------------------- ### Default OpenNext Configuration Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/reference.mdx Example of a default OpenNext configuration object, specifying architecture. ```typescript import type { OpenNextConfig } from "@open-next/types"; const config = { // ... other options runtime: "node", architecture: { build: { format: "esm", esbuild: { target: "node18", external: [ "@aws-sdk/*", "@sparticuz/chromium", "sharp", "next", "react", "react-dom", ], }, // ... other esbuild options }, // ... other architecture options }, // ... other options } satisfies OpenNextConfig; export default config; ``` -------------------------------- ### Use aws4fetch for Reduced Cold Starts Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/simple_example.mdx Enable aws4fetch for S3, DynamoDB, and SQS interactions to reduce cold start times. Requires OpenNext v3.0.3+. ```typescript import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; const config = { default: { override: { tagCache: "dynamodb-lite", incrementalCache: "s3-lite", queue: "sqs-lite", }, }, } satisfies OpenNextConfig; export default config; ``` -------------------------------- ### Install @opennextjs/cloudflare Package Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/get-started.mdx Install the latest version of the @opennextjs/cloudflare package to integrate Next.js with Cloudflare Workers. ```sh npm install @opennextjs/cloudflare@latest ``` -------------------------------- ### Splitted Server Patterns Configuration Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/reference.mdx Example of configuring CloudFront compatible patterns to match routes for a splitted server. ```typescript patterns: [ "/api/*", "/page", ] ``` -------------------------------- ### Install Wrangler CLI Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/get-started.mdx Install the Wrangler CLI as a dev dependency. Ensure you use version 3.99.0 or later for compatibility with @opennextjs/cloudflare. ```sh npm install --save-dev wrangler@latest ``` -------------------------------- ### Get Help for a Specific OpenNext Cloudflare CLI Command Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/cli.mdx Use this to get detailed help and usage information for a specific command within the opennextjs-cloudflare CLI. ```bash pnpm opennextjs-cloudflare --help ``` -------------------------------- ### Configure Static Asset Caching with _headers Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/get-started.mdx Add a `public/_headers` file to configure caching for static assets. This example sets a long cache duration for `/_next/static/*`. ```plaintext /_next/static/* Cache-Control: public,max-age=31536000,immutable ``` -------------------------------- ### OpenNext Edge Plugin Example Source: https://github.com/opennextjs/docs/blob/main/pages/aws/contribute/plugin.mdx Compile routes and middleware for the `edge` runtime using this plugin. Provide the path to the `.next` directory, the `edgeFunctionHandler.js` file, and middleware information. Set `isInCloudflare` to true if bundling for Cloudflare Workers. ```typescript openNextEdgePlugin({ // The path to the .next directory nextDir: "next", // The path to the edgeFunctionHandler.js file that we'll use to bundle the routing edgeFunctionHandlerPath: "./edgeFunctionHandler.js", // The middlewareInfo from the middleware manifest file middlewareInfo: middlewareInfo // If the app should be bundled for cloudflare workers isInCloudflare: true }) ``` -------------------------------- ### Migrate Existing Next.js App to Cloudflare Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/get-started.mdx Automate the setup for an existing Next.js app to run on Cloudflare. This command also sets up an R2 bucket for caching if enabled on your account. ```sh npx @opennextjs/cloudflare migrate ``` -------------------------------- ### Netlify TOML Custom Headers Configuration Source: https://context7.com/opennextjs/docs/llms.txt Define custom headers for specific paths in Netlify using the `netlify.toml` file. This example sets cache control for static assets. ```toml # Custom headers [[headers]] for = "/_next/static/*" [headers.values] Cache-Control = "public, max-age=31536000, immutable" ``` -------------------------------- ### OpenNext Replacement Plugin Example Source: https://github.com/opennextjs/docs/blob/main/pages/aws/contribute/plugin.mdx Use this plugin to replace or inject code within a target file. Specify the target file using a regex, define code to be replaced via `replacements`, and optionally remove code blocks using `deletes`. The `//#import` and `//#override` directives allow for structured code injection and replacement. ```typescript openNextPlugin({ // The target file to replace code in target: /plugins\/default\.js/g, // This is where the plugin will look for the code to replace replacements: [require.resolve("./plugins/default.js")], // This is to delete some code from the target file deletes: ["id1"], }) //To inject arbitrary code by using (import at top of file): //#import import data from 'data' const datum = data.datum //#endImport To replace code: //#override id1 export function overrideMe() { // I will replace the "id1" block in the target file } //#endOverride ``` -------------------------------- ### Build and Run OpenNext Locally Source: https://github.com/opennextjs/docs/blob/main/pages/aws/contribute/local_run.mdx Use these commands to build your project with OpenNext and then run the local server. The `--config-path` flag is necessary if your custom configuration file has a name other than the default. ```sh # This is to build (the config-path is needed if you use a different name than the default one) node /path/to/opennextjs-aws/packages/open-next/dist/index.js build --config-path open-next.local.config.ts # Then to run the server node .open-next/server-functions/default/index.mjs ``` -------------------------------- ### Build and Run OpenNext Locally Source: https://github.com/opennextjs/docs/blob/main/pages/aws/contribute.mdx Steps to clone the repository, build OpenNext, and run it in watch mode for local development. This allows for testing changes without frequent deployments. ```sh cd opennextjs-aws pnpm build ``` ```sh pnpm dev ``` -------------------------------- ### Server Backend Structure Source: https://github.com/opennextjs/docs/blob/main/pages/aws/inner_workings/architecture.mdx Illustrates the file structure for the Next.js standalone build output, highlighting the NextServer and dependencies. ```plain .next/ -> NextServer node_modules/ -> dependencies ``` -------------------------------- ### Drizzle ORM: D1 Example for Cloudflare Workers Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/db.mdx Example of configuring Drizzle ORM to use Cloudflare D1 database. It utilizes `getCloudflareContext` and `react`'s `cache` for efficient client creation. ```typescript import { getCloudflareContext } from "@opennextjs/cloudflare"; import { drizzle } from "drizzle-orm/d1"; import { cache } from "react"; import * as schema from "./schema/d1"; export const getDb = cache(() => { const { env } = getCloudflareContext(); return drizzle(env.MY_D1, { schema }); }); // This is the one to use for static routes (i.e. ISR/SSG) export const getDbAsync = cache(async () => { const { env } = await getCloudflareContext({ async: true }); return drizzle(env.MY_D1, { schema }); }); ``` -------------------------------- ### Sentry Server-Side Setup without Dynamic Imports Source: https://github.com/opennextjs/docs/blob/main/pages/aws/common_issues.mdx A working Sentry configuration for instrumentation.ts that avoids dynamic imports, resolving the 'cannot find module ./chunks/xxxx.js' error. This setup ensures Sentry is initialized correctly on the server. ```typescript import * as Sentry from "@sentry/nextjs"; import { initSentry } from "../sentry.server.config"; export const onRequestError = Sentry.captureRequestError; export async function register() { initSentry(process.env.NEXT_RUNTIME as "nodejs" | "edge"); } ``` ```typescript import * as Sentry from "@sentry/nextjs"; export const initSentry = (runtime: "nodejs" | "edge") => { Sentry.init({ dsn: "https://...", //...rest of your config }); }; ``` -------------------------------- ### PostgreSQL Client Initialization with Max Uses Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/db.mdx Configure the Prisma PostgreSQL adapter with `maxUses: 1` to ensure a new connection is created for each request, preventing pooling issues in Cloudflare Workers. Access the connection string via environment variables. ```typescript import { cache } from "react"; import { PrismaClient } from "@prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; export const getDb = cache(() => { const connectionString = process.env.PG_URL ?? ""; const adapter = new PrismaPg({ connectionString, maxUses: 1 }); const prisma = new PrismaClient({ adapter }); return prisma; }); ``` -------------------------------- ### Basic OpenNext Configuration File Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config.mdx This is the minimum required content for an `open-next.config.ts` file. The default configuration will be applied if this file is absent. ```typescript export default { default: {}, }; ``` -------------------------------- ### Deploy with Nx Using Configuration Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Command to deploy the application using the Nx deploy target with a predefined configuration, such as 'dev'. ```sh nx deploy next-site --configuration dev ``` -------------------------------- ### Bundle for Classic Node Server with Function Splitting Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/simple_example.mdx Configure the default server to run as a classic Node.js server and split functions for specific routes. Requires Node.js execution environment and appropriate AWS permissions. ```typescript import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; const config = { // In this case, the default server is meant to run as a classic Node server // To execute the server you need to run `node index.mjs` inside `.open-next/server-functions/default` default: { override: { wrapper: "node", converter: "node", // This is necessary to generate a simple dockerfile and for the generated output to know that it needs to deploy on docker // You can also provide a string here (i.e. the content of your Dockerfile) which will be used to create the dockerfile // You don't have to provide this if you plan on not using docker, or if you plan on using your own custom dockerfile generateDockerfile: true, }, }, // You can define multiple functions here, each with its own routes, patterns and overrides functions: { // In this case both the api route is in lambda and the rest is in node myFn: { // Patterns needs to use the glob pattern patterns: ["api/*"], routes: ["app/api/test/route", "app/api/test2/route"], }, }, } satisfies OpenNextConfig; export default config; ``` -------------------------------- ### Initialize OpenNext Cloudflare for Local Development Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/get-started.mdx Modify your Next.js configuration file to import and call `initOpenNextCloudflareForDev` for optimal integration with the OpenNext Cloudflare adapter and to use Cloudflare bindings during local development. ```typescript import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ }; export default nextConfig; import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; initOpenNextCloudflareForDev(); ``` -------------------------------- ### Nx Project Configuration for SST Deployment Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Sets up an Nx target for deploying with SST, chaining the OpenNext build. Allows passing stage arguments and defines configurations for different environments. ```json { "deploy": { "executor": "nx:run-commands", "dependsOn": ["open-next-build"], "options": { "cwd": "apps/next-site", "command": "sst deploy --stage {args.stage}", "forwardAllArgs": true }, "defaultConfiguration": "dev", "configurations": { "production": { "args": ["--stage=production"] }, "staging": { "args": ["--stage=staging"] }, "dev": { "args": ["--stage=development"] } } } } } ``` -------------------------------- ### Create Next.js App for Cloudflare Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/examples.mdx Use this command to create a new Next.js app pre-configured for Cloudflare Workers with @opennextjs/cloudflare. Ensure you have npm installed. ```sh npm create cloudflare@latest -- my-next-app --framework=next --platform=workers ``` -------------------------------- ### Configure Static Assets with run_worker_first=false Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/assets.mdx Use this configuration when static assets do not need to be served behind middleware or Next.js rewrites/headers. This is the most cost-efficient option as asset requests are not billed. ```jsonc { "name": "my-app", // ... "assets": { "directory": ".open-next/assets", "binding": "ASSETS", // Optional as false is the default value "run_worker_first": false, }, // ... } ``` -------------------------------- ### Durable Objects Build Warning Output Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/known-issues.mdx Example of the warning message displayed during the Next.js build process when internal Durable Object bindings are detected. ```text ┌─────────────────────────────────┐ │ OpenNext — Building Next.js app │ └─────────────────────────────────┘ > next build ▲ Next.js 15.2.4 ▲ [WARNING] You have defined bindings to the following internal Durable Objects: - {"name":"NEXT_CACHE_DO_QUEUE","class_name":"DOQueueHandler"} These will not work in local development, but they should work in production. If you want to develop these locally, you can define your DO in a separate Worker, with a separate configuration file. For detailed instructions, refer to the Durable Objects section here: https://developers.cloudflare.com/workers/wrangler/api#supported-bindings Creating an optimized production build ... workerd/server/server.c++:1951: warning: A DurableObjectNamespace in the config referenced the class "DOQueueHandler", but no such Durable Object class is exported from the worker. Please make sure the class name matches, it is exported, and the class extends 'DurableObject'. Attempts to call to this Durable Object class will fail at runtime, but historically this was not a startup-time error. Future versions of workerd may make this a startup-time error. ``` -------------------------------- ### Configure Wrangler.jsonc for Large Site Caching Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/caching.mdx Defines Durable Objects bindings for caching and queues in wrangler.jsonc. This setup is required for on-demand revalidation with `DOShardedTagCache`. ```jsonc { "name": "", // ... "services": [ { "binding": "WORKER_SELF_REFERENCE", "service": "", }, ], // R2 incremental cache "r2_buckets": [ { "binding": "NEXT_INC_CACHE_R2_BUCKET", "bucket_name": "", }, ], // DO Queue and DO Sharded Tag Cache "durable_objects": { "bindings": [ { "name": "NEXT_CACHE_DO_QUEUE", "class_name": "DOQueueHandler", }, // This is only required if you use On-demand revalidation { "name": "NEXT_TAG_CACHE_DO_SHARDED", "class_name": "DOShardedTagCache", }, { "name": "NEXT_CACHE_DO_PURGE", "class_name": "BucketCachePurge", }, ], }, "migrations": [ { "tag": "v1", "new_sqlite_classes": [ "DOQueueHandler", // This is only required if you use On-demand revalidation "DOShardedTagCache", "BucketCachePurge", ], }, ], } ``` -------------------------------- ### Enable OpenNext Debug Mode Source: https://github.com/opennextjs/docs/blob/main/pages/aws/common_issues.mdx Set the OPEN_NEXT_DEBUG environment variable to true to enable verbose logging, disable esbuild minifying, and add source maps. Do not use in production as it significantly increases bundle size. ```sh OPEN_NEXT_DEBUG=true npx open-next@latest build ``` -------------------------------- ### Configure Static Assets with run_worker_first=true Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/assets.mdx Use this configuration when assets must be served behind middleware or Next.js rewrites/headers. All requests will reach the Worker and incur billing. This option is necessary for skew protection. ```jsonc { "name": "my-app", // ... "assets": { "directory": ".open-next/assets", "binding": "ASSETS", "run_worker_first": true, }, // ... } ``` -------------------------------- ### Mock Global in Middleware for Development Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/custom_overrides.mdx Mock global variables in your middleware for development environments when they are not available. This example fetches the `myApi` function from a local development endpoint. ```typescript import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; // Here you need to mock the global if not present // One way to avoid issues with different implementation would be to create an api endpoint // that uses the exact same logic as the global you defined earlier, // and that is only available during development i.e. /api/dev/myApi if (!globalThis.myApi) { globalThis.myApi = async () => { return await fetch("http://localhost:3000/api/dev/myApi").then((res) => res.json()); }; } export function middleware(request: NextRequest) { // You can also send an error in the api endpoint itself // Or you could add all the dev endpoint in their own lambda // that you do not deploy in production if (request.nextUrl.pathname.startsWith("/api/dev") && process.env.NODE_ENV === "production") { return NextResponse("This route is only available in development", { status: 500, }); } // Now you can use Node.js in your middleware const { nb } = await myApi(); // ... your code here } ``` -------------------------------- ### Define a Custom Wrapper for Node.js Globals Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/custom_overrides.mdx Create a custom wrapper to define global variables accessible in your middleware. This example defines `myApi` for use with Node.js modules. ```typescript import defaultWrapper from "@opennextjs/aws/overrides/wrappers/aws-lambda.js"; //Here you can define some globals declare global { var myApi: () => Promise; } globalThis.myApi = async () => { const crypto = await import("crypto"); return { nb: crypto.randomInt(0, 100), }; }; export default defaultWrapper; ``` ```typescript import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; const config = { default: { override: { wrapper: () => import("./customWrapper").then((mod) => mod.default), }, }, } as OpenNextConfig; export default config; ``` -------------------------------- ### Configure Next.js for OpenNext in Nx Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Update `next.config.js` to include `output: 'standalone'` and `experimental.outputFileTracingRoot` for OpenNext compatibility in an Nx monorepo. ```javascript //@ts-check // eslint-disable-next-line @typescript-eslint/no-var-requires const { composePlugins, withNx } = require('@nx/next'); const { join } = require('node:path'); /** * @type {import('@nx/next/plugins/with-nx').WithNxOptions} **/ const nextConfig = { nx: { // Set this to true if you would like to use SVGR // See: https://github.com/gregberge/svgr svgr: false, }, output: 'standalone', experimental: { // this should be the path to the root of your repo, in this case it's just two levels down. needed for open-next to detect that it's a monorepo outputFileTracingRoot: join(__dirname, '../../'), }, }; const plugins = [ // Add more Next.js plugins to this list if needed. withNx, ]; module.exports = composePlugins(...plugins)(nextConfig); ``` -------------------------------- ### Invalidate CloudFront Paths including Data JSON Source: https://github.com/opennextjs/docs/blob/main/pages/aws/inner_workings/caching.mdx This example shows how to invalidate both a page path and its corresponding `_next/data` JSON file. The `BUILD_ID` is accessed via `process.env.NEXT_BUILD_ID`. ```typescript await invalidateCloudFrontPaths(["/foo", `/_next/data/${process.env.NEXT_BUILD_ID}/foo.json`]); ``` -------------------------------- ### Deploy to AWS with SST Source: https://context7.com/opennextjs/docs/llms.txt Execute this command to deploy your Next.js application to AWS using SST. ```bash npx sst deploy ``` -------------------------------- ### Configure Cloudflare Cache Purge Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/caching.mdx Integrate the cache purge component into your open-next.config.ts. This setup is necessary for on-demand revalidation when using cache components that leverage the Cache API. ```typescript import { defineCloudflareConfig } from "@opennextjs/cloudflare"; import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache"; import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache"; import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; import { purgeCache } from "@opennextjs/cloudflare/overrides/cache-purge/index"; export default defineCloudflareConfig({ incrementalCache: withRegionalCache(r2IncrementalCache, { mode: "long-lived" }), queue: doQueue, // This is only required if you use On-demand revalidation tagCache: doShardedTagCache({ baseShardSize: 12 }), // Disable this if you want to use PPR enableCacheInterception: true, // you can also use the `durableObject` option to use a durable object as a cache purge cachePurge: purgeCache({ type: "direct" }), }); ``` -------------------------------- ### Drizzle ORM: Hyperdrive Example for Cloudflare Workers Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/db.mdx Configuration for Drizzle ORM with Cloudflare Hyperdrive. It ensures a per-request client using `maxUses: 1` and `react`'s `cache`. ```typescript import { getCloudflareContext } from "@opennextjs/cloudflare"; import { drizzle } from "drizzle-orm/node-postgres"; import { cache } from "react"; import * as schema from "./schema/pg"; import { Pool } from "pg"; export const getDb = cache(() => { const { env } = getCloudflareContext(); const connectionString = env.HYPERDRIVE.connectionString; const pool = new Pool({ connectionString, // You don't want to reuse the same connection for multiple requests maxUses: 1, }); return drizzle({ client: pool, schema }); }); // This is the one to use for static routes (i.e. ISR/SSG) export const getDbAsync = cache(async () => { const { env } = await getCloudflareContext({ async: true }); const connectionString = env.HYPERDRIVE.connectionString; const pool = new Pool({ connectionString, // You don't want to reuse the same connection for multiple requests maxUses: 1, }); return drizzle({ client: pool, schema }); }); ``` -------------------------------- ### Configure Origins and Bucket Deployments Source: https://github.com/opennextjs/docs/blob/main/pages/aws/reference-implementation.mdx Sets up S3 origins and maps various origin types (Function or AppRunner) for the OpenNext application. ```typescript private createOrigins() { const { s3: s3Origin, default: defaultOrigin, imageOptimizer: imageOrigin, ...restOrigins } = this.openNextOutput.origins; const s3 = new S3Origin(this.bucket, { originPath: s3Origin.originPath, }); for (const copy of s3Origin.copy) { new BucketDeployment(this, `OpenNextBucketDeployment${copy.from}`, { sources: [Source.asset(copy.from)], destinationBucket: this.bucket, destinationKeyPrefix: copy.to, prune: false, }); } const origins = { s3: new S3Origin(this.bucket, { originPath: s3Origin.originPath, originAccessIdentity: undefined, }), default: defaultOrigin.type === "function" ? this.createFunctionOrigin("default", defaultOrigin) : this.createAppRunnerOrigin("default", defaultOrigin), imageOptimizer: imageOrigin.type === "function" ? this.createFunctionOrigin("imageOptimizer", imageOrigin) : this.createAppRunnerOrigin("imageOptimizer", imageOrigin), ...Object.entries(restOrigins).reduce( (acc, [key, value]) => { if (value.type === "function") { acc[key] = this.createFunctionOrigin(key, value); } else if (value.type === "ecs") { acc[key] = this.createAppRunnerOrigin(key, value); } return acc; }, {} as Record ), }; return origins; } ``` -------------------------------- ### Deploy Next.js Site with Nx Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Use this command to deploy your Next.js site. Specify the '-c dev' flag for development deployments or '--stage ' for custom deployment stages. ```bash nx deploy next-site -c dev # OR nx deploy next-site --stage my-stage # Custom Stage ``` -------------------------------- ### Get Current Server Worker Deployment Status Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/multi-worker.mdx Retrieves the currently deployed version ID of the server worker. This is necessary to determine the current production version before initiating a gradual deployment. ```bash wrangler deployments status --config ./path-to/server-wrangler.jsonc ``` -------------------------------- ### Netlify TOML Build Configuration Source: https://context7.com/opennextjs/docs/llms.txt Basic Netlify `netlify.toml` configuration for building a Next.js application. It specifies the build command and publish directory. ```toml # netlify.toml [build] command = "next build" publish = ".next" ``` -------------------------------- ### Create OpenNext Configuration Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Define `open-next.config.ts` in the app's root directory to specify build commands, output paths, and package JSON location for OpenNext. ```typescript import type { OpenNextConfig } from "@opennextjs/aws/types/open-next"; const config = { default: {}, buildCommand: "exit 0", // in my example we set up Nx task distribution to handle the order of building. buildOutputPath: ".", appPath: ".", packageJsonPath: "../../", // again, path to the root of your repo (where the package.json is) } satisfies OpenNextConfig; export default config; ``` -------------------------------- ### Configure Durable Object Queue for Caching Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/caching.mdx Integrate the Durable Object Queue for time-based revalidation in your OpenNext configuration. This setup requires importing specific modules and defining them in your OpenNext config. ```typescript import { defineCloudflareConfig } from "@opennextjs/cloudflare"; // ... import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; export default defineCloudflareConfig({ // ... incrementalCache: r2IncrementalCache, queue: doQueue, }); ``` -------------------------------- ### SST Configuration File Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Create `sst.config.ts` in the app's directory to configure SST for deployment, specifying the application name. ```typescript /// export default $config({ app(input) { return { name: "next-site", // use whatever your project is called here, ``` -------------------------------- ### Implement a Custom API Gateway v2 Converter Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/custom_overrides.mdx Create a custom converter to modify event objects before they are processed by OpenNext. This example adds custom headers and injects a secret key from SST Config. ```typescript import converter from "@opennextjs/aws/overrides/converters/aws-apigw-v2.js"; import type { Converter } from "@opennextjs/aws/types/overrides.js"; import { Config } from "sst/node/Config"; const mySecretKey = Config.YOUR_SECRET_KEY; export default { convertFrom: async (event) => { const result = await converter.convertFrom(event); return { ...result, headers: { ...result.headers, "inserted-in-converter": "1", "my-super-secret-key": mySecretKey, }, }; }, convertTo: async (intResult) => { const result = await converter.convertTo(intResult); return { ...result, headers: { ...result.headers, "x-converter-end": "1", }, }; }, name: "custom-apigw-v2", } as Converter; ``` ```typescript import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; const config = { default: { override: { converter: () => import("./customConverter").then((mod) => mod.default), }, }, } as OpenNextConfig; ``` -------------------------------- ### Configure pnpm Shameful Hoisting for Netlify Source: https://github.com/opennextjs/docs/blob/main/pages/netlify/index.mdx To use pnpm with Next.js on Netlify, set the PNPM_FLAGS environment variable to '--shamefully-hoist'. This ensures proper dependency management by appending the flag to the pnpm install command. ```bash PNPM_FLAGS=--shamefully-hoist ``` -------------------------------- ### OpenNext Resolve Plugin Example Source: https://github.com/opennextjs/docs/blob/main/pages/aws/contribute/plugin.mdx This plugin prevents bundling the entire library by replacing dynamic imports with specified overrides. Configure the `overrides` object to map modules to their desired runtime environments (e.g., 'node'). ```typescript openNextResolvePlugin({ overrides: { wrapper: "node", converter: "node", }, }); ``` -------------------------------- ### Hyperdrive Client Initialization Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/db.mdx Set up Prisma with Hyperdrive by using the `PrismaPg` adapter and configuring `maxUses: 1`. Access the Hyperdrive connection string from Cloudflare's environment variables. Includes both cached and async versions for different route types. ```typescript import { getCloudflareContext } from "@opennextjs/cloudflare"; // You can use cache from react to cache the client during the same request // this is not mandatory and only has an effect for server components import { cache } from "react"; import { PrismaClient } from "@prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; export const getDb = cache(() => { const { env } = getCloudflareContext(); const connectionString = env.HYPERDRIVE.connectionString; const adapter = new PrismaPg({ connectionString, maxUses: 1 }); return new PrismaClient({ adapter }); }); // This is the one to use for static routes (i.e. ISR/SSG) export const getDbAsync = async () => { const { env } = await getCloudflareContext({ async: true }); const connectionString = env.HYPERDRIVE.connectionString; const adapter = new PrismaPg({ connectionString, maxUses: 1 }); return new PrismaClient({ adapter }); }; ``` -------------------------------- ### Deploy with Nx and Custom Stage Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/nx.mdx Command to deploy the application using the Nx deploy target, allowing for custom stage specification. ```sh nx deploy next-site --stage this-is-my-stage ``` -------------------------------- ### Initialize SST in Next.js Project Source: https://context7.com/opennextjs/docs/llms.txt Use this command to initialize SST in your Next.js project for AWS deployment. ```bash npx sst@latest init ``` -------------------------------- ### Initialize Cloudflare Cache with OpenNext.js Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/caching.mdx Configure OpenNext.js to use Cloudflare's R2 for incremental caching, DoQueue for queueing, and D1 for tag caching. This setup optimizes cache management and data handling on Cloudflare Workers. ```typescript import { defineCloudflareConfig } from "@opennextjs/cloudflare"; import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache"; import { withFilter, softTagFilter } from "@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter"; export default defineCloudflareConfig({ incrementalCache: r2IncrementalCache, queue: doQueue, tagCache: withFilter({ tagCache: d1NextTagCache, filterFn: softTagFilter, }), }); ``` -------------------------------- ### Configure Cloudflare Caching with R2, DO Queue, and D1 Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/caching.mdx Configure wrangler.jsonc for R2 incremental cache, Durable Objects for the queue, and D1 for the tag cache. This setup is recommended for small sites using revalidation and on-demand revalidation. ```jsonc { "name": "", // ... "services": [ { "binding": "WORKER_SELF_REFERENCE", "service": "", }, ], // R2 incremental cache "r2_buckets": [ { "binding": "NEXT_INC_CACHE_R2_BUCKET", "bucket_name": "", }, ], // DO Queue "durable_objects": { "bindings": [ { "name": "NEXT_CACHE_DO_QUEUE", "class_name": "DOQueueHandler", }, ], }, "migrations": [ { "tag": "v1", "new_sqlite_classes": ["DOQueueHandler"], }, ], // D1 Tag Cache (Next mode) // This is only required if you use On-demand revalidation "d1_databases": [ { "binding": "NEXT_TAG_CACHE_D1", "database_id": "", "database_name": "", }, ], } ``` ```typescript import { defineCloudflareConfig } from "@opennextjs/cloudflare"; import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache"; import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; //import { withFilter, softTagFilter } from "@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter"; export default defineCloudflareConfig({ incrementalCache: r2IncrementalCache, queue: doQueue, // This is only required if you use On-demand revalidation tagCache: d1NextTagCache, //If you don't use `revalidatePath`, you can also filter internal soft tags using the `softTagFilter` // tagCache: withFilter({ // tagCache: d1NextTagCache, // filterFn: softTagFilter, // }), // Disable this if you want to use PPR enableCacheInterception: true, }); ``` -------------------------------- ### Preview Application Locally with Cloudflare Source: https://context7.com/opennextjs/docs/llms.txt Use this command to preview your Next.js application locally using the Cloudflare Workers runtime. ```bash opennextjs-cloudflare preview ``` -------------------------------- ### OpenNext CDK Reference Implementation Construct Source: https://github.com/opennextjs/docs/blob/main/pages/aws/reference-implementation.mdx This TypeScript code defines a CDK construct for deploying OpenNext. It includes setup for S3 buckets, DynamoDB tables, SQS queues, and CloudFront distributions. It reads configuration from 'open-next.output.json'. ```typescript import { Construct } from "constructs"; import { readFileSync } from "fs"; import path from "path"; import { BlockPublicAccess, Bucket } from "aws-cdk-lib/aws-s3"; import { BucketDeployment, Source } from "aws-cdk-lib/aws-s3-deployment"; import { CustomResource, Duration, Fn, RemovalPolicy, Stack } from "aws-cdk-lib/core"; import { AllowedMethods, BehaviorOptions, CacheCookieBehavior, CacheHeaderBehavior, CachePolicy, CacheQueryStringBehavior, CachedMethods, Distribution, ICachePolicy, ViewerProtocolPolicy, FunctionEventType, OriginRequestPolicy, Function as CloudfrontFunction, FunctionCode, } from "aws-cdk-lib/aws-cloudfront"; import { HttpOrigin, S3Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { Code, Function as CdkFunction, FunctionUrlAuthType, InvokeMode, Runtime, } from "aws-cdk-lib/aws-lambda"; import { TableV2 as Table, AttributeType, Billing } from "aws-cdk-lib/aws-dynamodb"; import { Service, Source as AppRunnerSource, Memory, HealthCheck, Cpu } from "@aws-cdk/aws-apprunner-alpha"; import { DockerImageAsset } from "aws-cdk-lib/aws-ecr-assets"; import { Queue } from "aws-cdk-lib/aws-sqs"; import { SqsEventSource } from "aws-cdk-lib/aws-lambda-event-sources"; import { IGrantable } from "aws-cdk-lib/aws-iam"; import { Provider } from "aws-cdk-lib/custom-resources"; import { RetentionDays } from "aws-cdk-lib/aws-logs"; type BaseFunction = { handler: string; bundle: string; }; type OpenNextFunctionOrigin = { type: "function"; streaming?: boolean; } & BaseFunction; type OpenNextECSOrigin = { type: "ecs"; bundle: string; dockerfile: string; }; type OpenNextS3Origin = { type: "s3"; originPath: string; copy: { from: string; to: string; cached: boolean; versionedSubDir?: string; }[]; }; type OpenNextOrigins = OpenNextFunctionOrigin | OpenNextECSOrigin | OpenNextS3Origin; interface OpenNextOutput { edgeFunctions: { [key: string]: BaseFunction; }; origins: { s3: OpenNextS3Origin; default: OpenNextFunctionOrigin | OpenNextECSOrigin; imageOptimizer: OpenNextFunctionOrigin | OpenNextECSOrigin; [key: string]: OpenNextOrigins; }; behaviors: { pattern: string; origin?: string; edgeFunction?: string; }[]; additionalProps?: { disableIncrementalCache?: boolean; disableTagCache?: boolean; initializationFunction?: BaseFunction; warmer?: BaseFunction; revalidationFunction?: BaseFunction; }; } interface OpenNextCdkReferenceImplementationProps { openNextPath: string; } export class OpenNextCdkReferenceImplementation extends Construct { private openNextOutput: OpenNextOutput; private bucket: Bucket; private table: Table; private queue: Queue; private staticCachePolicy: ICachePolicy; private serverCachePolicy: CachePolicy; public distribution: Distribution; constructor(scope: Construct, id: string, props: OpenNextCdkReferenceImplementationProps) { super(scope, id); this.openNextOutput = JSON.parse( readFileSync(path.join(props.openNextPath, "open-next.output.json"), "utf-8") ) as OpenNextOutput; this.bucket = new Bucket(this, "OpenNextBucket", { publicReadAccess: false, blockPublicAccess: BlockPublicAccess.BLOCK_ALL, autoDeleteObjects: true, removalPolicy: RemovalPolicy.DESTROY, enforceSSL: true, }); this.table = this.createRevalidationTable(); this.queue = this.createRevalidationQueue(); const origins = this.createOrigins(); this.serverCachePolicy = this.createServerCachePolicy(); this.staticCachePolicy = this.createStaticCachePolicy(); this.distribution = this.createDistribution(origins); } private createRevalidationTable() { const table = new Table(this, "RevalidationTable", { partitionKey: { name: "tag", type: AttributeType.STRING }, sortKey: { name: "path", type: AttributeType.STRING }, pointInTimeRecovery: true, billing: Billing.onDemand(), globalSecondaryIndexes: [ { indexName: "revalidate", partitionKey: { name: "path", type: AttributeType.STRING }, sortKey: { name: "revalidatedAt", type: AttributeType.NUMBER }, }, ], ``` -------------------------------- ### Drizzle ORM: Avoid Global Client for PostgreSQL Source: https://github.com/opennextjs/docs/blob/main/pages/cloudflare/howtos/db.mdx When using Drizzle with PostgreSQL in Cloudflare Workers, create a new client per request to prevent connection pool issues. This example shows the incorrect global client approach. ```typescript import { drizzle } from "drizzle-orm/node-postgres"; import * as schema from "./schema/pg"; import { Pool } from "pg"; const pool = new Pool({ connectionString: process.env.PG_URL, }); export const db = drizzle({ client: pool, schema }); ``` -------------------------------- ### Configure Edge Runtime Function Source: https://github.com/opennextjs/docs/blob/main/pages/aws/config/simple_example.mdx Define an edge runtime function for specific routes. This function will be deployed in the edge runtime, offering reduced cold start times. Note that only one route can be deployed per function, and middleware is not bundled. ```typescript import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; const config = { default: {}, functions: { edge: { runtime: "edge", //placement: "global", If you want your function to be deployed globally (i.e. lambda@edge) uncomment this line. Otherwise it will be deployed in the region specified in the stack routes: ["app/api/test/route"], patterns: ["api/test"], }, }, } satisfies OpenNextConfig; export default config; ``` -------------------------------- ### Cloudflare Bindings - Accessing KV, R2, D1 in Route Handlers Source: https://context7.com/opennextjs/docs/llms.txt Access Cloudflare bindings (KV, R2, D1, Durable Objects) within Next.js route handlers and server components using `getCloudflareContext`. Demonstrates putting and getting from KV, and putting to R2. ```typescript // app/api/example/route.ts import { getCloudflareContext } from "@opennextjs/cloudflare"; export async function GET(request: Request) { const { env, cf, ctx } = getCloudflareContext(); // Access KV namespace const myKv = env.MY_KV_NAMESPACE; await myKv.put("foo", "bar"); const value = await myKv.get("foo"); // Access R2 bucket const r2 = env.MY_R2_BUCKET; await r2.put("data.json", JSON.stringify({ hello: "world" })); // Access request metadata const country = cf?.country; return new Response(JSON.stringify({ value, country })); } // For SSG routes, use async mode export async function generateStaticParams() { const context = await getCloudflareContext({ async: true }); // Use context.env to access bindings during build } ```