Giter Site home page Giter Site logo

jetbridge / cdk-nextjs Goto Github PK

View Code? Open in Web Editor NEW
238.0 238.0 37.0 8.97 MB

Deploy a NextJS application using AWS CDK

Home Page: https://constructs.dev/packages/cdk-nextjs-standalone

License: Apache License 2.0

TypeScript 100.00%
aws cdk cloudfront lambda next nextjs serverless

cdk-nextjs's People

Contributors

albertsabate avatar ansgars avatar bestickley avatar durgaprasad-budhwani avatar jadenv avatar keenankaufman avatar kevin-mitchell avatar khuezy avatar lagyu avatar okarlsson avatar onhate avatar patrickufer avatar revmischa avatar xinha-sh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

cdk-nextjs's Issues

Reduce repetitive open-next commands to speed up deployment

When I run cdk deploy within a CDK app using Nextjs construct with multiple stacks, open-next is called multiple times to build the site which slows down deployment. This is because to run open-next the spawn.sync here to run which runs each time the synthesis code runs. Instead, could we use the Asset construct which is smarter in that is uses caching. More info here. Example code here. I'm interested in submitting this PR.

.next folder missing from standalone.zip

Any ideas why .next folder is missing from standalone.zip?

+ zip -ryq1 /tmp/nextjs-cdk-build-cJGhSY/standalone/standalone.zip node_modules package.json server.cjs server.cjs.map server.js server.js.map
Bundling asset oam-web/Web/ImgOptFn/Code/Stage...

  packages/web/cdk.out/bundling-temp-132480d4ff30450d18988a5dd5e22b14df829e19d7adb24b82ca76b24a6077a3/index.js  1.3mb ⚠️      

⚡ Done in 2651ms

I am assuming that leads to this error:

"ENOENT: no such file or directory, open '/var/task/.next/required-server-files.json'"

NextJS warn outputFileTracingRoot

I'll need to reinvestigate how we're setting the tracing outputs. Currently, the function to determine it returns a relative path.
eg: nextjsPath: './path' returns ".."
NextJS warns and suggests to use an absolute path.

projectRoot seems to be ignored

I have been deploying my app successfully for the past few weeks, but today it stopped working. I am using a monorepo. My next folder is in apps/next and my cdk folder is an apps/cdk.

This is the config. I have added nextjsPath and projectRoot.

new Nextjs(this, getId('web-app'), {
      nextjsPath: '../next',
      projectRoot: '../..',
      environment: environment,
      defaults: {
        distribution: {
          customDomain: {
            domainName: props.webAppFqdn,
            alternateNames: props.alternateNames,
            certificate: props.wildcardCertificate,
          },
        },
      },
    });

The error I am getting when hitting the lambda is:

{
   "errorType": "Error",
   "errorMessage": "ENOENT: no such file or directory, open '/var/task/apps/next/node_modules/next/package.json'",
   "code": "ENOENT",
   "errno": -2,
   "syscall": "open",
   "path": "/var/task/apps/next/node_modules/next/package.json",
   "stack": [
       "Error: ENOENT: no such file or directory, open '/var/task/apps/next/node_modules/next/package.json'",
       "    at Object.openSync (node:fs:601:3)",
       "    at Object.readFileSync (node:fs:469:35)",
       "    at xe (file:///var/task/apps/next/index.mjs:4:11322)",
       "    at ue (file:///var/task/apps/next/index.mjs:4:10572)",
       "    at file:///var/task/apps/next/index.mjs:4:9489",
       "    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)"
   ]
}

Which is right, I am using a monorepo, so the 'node_modules' folder is in the root, not in the apps/next folder.

Any ideas why this is no longer working?

Caching of static pages

After some testing I noticed that pages are not being cached, so each time they're accessed they trigger the ServerHandler lambda, which makes requests slower. Couldn't we cache static pages just like any other resources and serve them from CloudFront cache?

Cloudfront 403 Issues for static files while deploying

Hello,

Thanks for the construct. Really has helped us deploying Next to AWS.

We have been running into some issues with Cloudfront. While the deployment is in progress and the assets are being replaced in the S3 bucket (usually about a minute), if a user requests any of the static assets during this time they receive a 403 that will be cached. The user then will not be able to access the application until they do a hard refresh on their browser.

I am wondering if anyone has seen this behavior before and if they have found something to help with it?

Thank you.

Error: stacks which use EdgeFunctions must have an explicitly set region

Hi,

When I run cdk bootstrap,it builds the next.js app and then I'm getting the error: "Error: stacks which use EdgeFunctions must have an explicitly set region". The AWS profile is set up locally.

import * as cdk from 'aws-cdk-lib'
import { Construct } from 'constructs'
import path from 'path'
import { Nextjs } from 'cdk-nextjs-standalone'

class NextjsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props)

    new Nextjs(this, 'Nextjs', {
      nextjsPath: path.resolve('../web/')
    })
  }
}

export default NextjsStack
  1. What am I missing?
  2. Is it possible to switch to the standard lambda at all?

Kind regards

WAF

What would be the best way to add a WAF to the distribution?

If I write this I won't work

const siteN = new Nextjs(this, 'Web', {
nextjsPath: './wwwroot/demonxt2', // relative path to nextjs project root
defaults: {
distribution: {
webAclId: webacl.attrArn,
}
}

AWS CDK Deployment Error with BucketDeployment

AWS CDK Deployment Error with BucketDeployment

When building large files, sometimes the deployment fails with the following error message:

Message returned: Command '['/opt/awscli/aws', 's3', 'cp', 's3://cdk-hnb659fds-assets--/a2466ec462ec002bde2a58f1ec547b9488499a9c37024449a3538c46c9878364.zip', '/tmp/tmpsqrwsitn/471574d4-1e48-4de6-bfea-e0ac6c1ee822']' died with <Signals.SIGKILL: 9>.

This issue is related to aws-cdk BucketDeployment. More information about this issue can be found in this GitHub thread.

According to the thread, the solution is to modify the files at line and add the flag useEfs: true to enable EFS storage.

destinationKeyPrefix: 'efs/',
useEfs: true, //This is the flag to enable efs storage.

Using EFS storage can resolve this issue as it provides a more reliable and scalable way of handling large files. It's recommended to use EFS storage for large files in AWS CDK deployments.

Lambda@Edge geo data empty

Lambda@Edge is functioning but the request.geo object is empty in my middleware. The headerBehaviors uses cf.OriginRequestHeaderBehavior.all(), which should include CloudFront-Viewer-Country, etc...

Need to investigate why geo is empty.

Build error with .next/standalone on Next 13.1.1

Describe Behavior

When building the next project with cdk-nextjs-standalone, it does not output a .next/standalone folder, despite the output set to standalone in next.config.js

Additional Information

The standalone folder does appear when running next build from the root of the next project, however

Versions used:

Dependencies

next: 13.1.1
cdk-nextjs-standalone: ^2.0.4
aws-cdk-lib: ^2.50.0
constructs: ^10.0.5

devDependencies

aws-cdk: ^2.50.0
esbuild: ^0.15.18

npm audit fix does not work due to packages being listed as bundled

Not sure why these are listed as bundled dependencies. Could it be changed, or dependencies updated? Atm I can't patch xml2js vulnerability.

npm WARN audit fix [email protected] node_modules/cdk-nextjs-standalone/node_modules/xml2js
npm WARN audit fix [email protected] is a bundled dependency of
npm WARN audit fix [email protected] [email protected] at node_modules/cdk-nextjs-standalone
npm WARN audit fix [email protected] It cannot be fixed automatically.
npm WARN audit fix [email protected] Check for updates to the cdk-nextjs-standalone package.
npm WARN audit fix [email protected] node_modules/cdk-nextjs-standalone/node_modules/aws-sdk
npm WARN audit fix [email protected] is a bundled dependency of
npm WARN audit fix [email protected] [email protected] at node_modules/cdk-nextjs-standalone
npm WARN audit fix [email protected] It cannot be fixed automatically.
npm WARN audit fix [email protected] Check for updates to the cdk-nextjs-standalone package.

404 Not Found for every js and css asset

image

I use swc

This Stack is called from a CodePipeline stage:

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import { Nextjs } from 'cdk-nextjs-standalone';

import { AWS_DEFAULT_REGION } from './config';

export class MyStack extends Stack {
    constructor(
        scope: Construct,
        id: string,
        props?: StackProps
    ) {
        super(scope, id, {
            ...props,
            env: {
                ...props?.env,
                /**
                 * Force deployment to global region because of Lambda Edge function
                 */
                region: process.env?.REGION ?? AWS_DEFAULT_REGION, // AWS_DEFAULT_REGION is definitely set
            },
        });

        const nextjs = new Nextjs(this, `nextjs-test`, {
            nextjsPath: '.',
        });
    }
}

package.json

{
    "name": "myProject",
    "private": true,
    "scripts": {
        "dev": "next dev",
        "build": "next build && next export",
        "start": "next start",
        "test": "jest --watch",
    },
    "dependencies": {
        "next": "13.1.6",
        "react": "^18.1.0",
        "react-dom": "^18.1.0",
        "ts-node": "^10.9.1"
    },
    "devDependencies": {
        "@testing-library/jest-dom": "5.16.4",
        "@testing-library/react": "13.2.0",
        "@testing-library/user-event": "14.2.0",
        "@types/react": "18.0.9",
        "@types/testing-library__jest-dom": "5.14.5",
        "aws-cdk": "^2.65.0",
        "aws-cdk-lib": "^2.65.0",
        "cdk-nextjs-standalone": "^2.1.0",
        "constructs": "^10.1.249",
        "esbuild": "^0.17.10",
        "jest": "28.1.0",
        "jest-environment-jsdom": "28.1.0",
        "source-map-support": "^0.5.9",
        "typescript": "4.6.4"
    }
}

next.config.js

/**
 * @type {import('next').NextConfig}
 */
const nextConfig = {
    swcMinify: true,
    images: {
        unoptimized: true,
    },
    output: 'standalone',
};

export default nextConfig;

tsconfig.json

{
    "compilerOptions": {
        "target": "ES6",
        "lib": [
            "dom",
            "dom.iterable",
            "esnext",
            "es2016",
            "es2017.object",
            "es2017.string"
        ],
        "allowJs": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "noImplicitThis": true,
        "skipLibCheck": true,
        "alwaysStrict": true,
        "strict": true,
        "forceConsistentCasingInFileNames": true,
        "noEmit": true,
        "esModuleInterop": true,
        "moduleResolution": "node",
        "resolveJsonModule": true,
        "isolatedModules": true,
        "jsx": "preserve",
        "incremental": true,
        "typeRoots": ["./node_modules/@types"],
        "baseUrl": ".",
        "paths": {
            "@/components/*": ["components/*"],
            "@/pages/*": ["pages/*"],
            "@/styles/*": ["styles/*"]
        },
        "module": "esnext"
    },
    "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types.d.ts"],
    "exclude": ["node_modules", "cdk.out"],
    "ts-node": {
        "compilerOptions": {
            "module": "CommonJS",
            "target": "ES6"
        }
    }
}

Headers missing from static assets

When I push my app out via this CDK module, there does not appear to be an easy way to have headers like "strict-transport-security" set for static assets. Is there a recommended process for this?

An example would be the css files _next/static/css/7cd2ca589f13dfff.css

This is necessary for CSP headers and others as well.

Improve Security Of Public Lambda Function URL

Currently this construct deploys Lambda Function URLs with AuthType of NONE. This allows anyone to call the function if they know the Lambda URL. A more secure approach would be to put the Lambda behind an API Gateway which verifies a secret header set by CloudFront and have the CloudFront behavior call the API GW. This would ensure Lambdas can only be called through CloudFront. Is there any interest from the maintainer of this project/community for such a feature? I'd possibly be interested in contributing.

Monorepo Support

I'm currently experiencing issues while running everything in a monrepo setup, where the infrastructure code is not withing the nextjs project to be packaged. Imagine something like this:

- apps
  - frontend
  - api
  - infrastructure
    - index.ts (containing the CDK app with the frontend & api stack)

upon the build step several problems occur due to

  • incorrectly concatenated paths (I assume the usage of nextjsPath being something like path.join('..', 'frontend') is not as intended
  • the subpath created in the .next output folder not being correctly handled (something like the following is created with outputFileTracingRoot set)
- frontend
  - .next
    - standalone
      - apps
        - my-app
          - .next
          - node_modules
          - package.json
          - server.js
      - node_modules
      - package.json

Would it be in the scope to support this? - If so it might be interesting to add a new parameter to the configuration monorepoRoot which would help with generating the correct subpath, and additionally with some fixes around nextjsPath this could be a viable thing to do.

I played around for a bit with that idea, but than go stuck with the lambda bundling/copying. Would be glad to also help out here.

`required-server-files.json: No such file or directory`

I'm encountering an error in the beforeBundling hook of the ImageOptimizationLambda class. The CDK app for my project is located in a directory named cdk. The Next app is located in a directory named web-frontend. These two directories both exist directly below the project root directory, which is named next-lambda.

.
├── cdk
│   ├── bin
│   │   └── cdk.ts
│   ├── lib
│   │   └── web-app-stack.ts
│   ├── cdk.json
│   └── package.json
├── web-frontend
│   ├── .next
│   ├── next.config.js
│   ├── ...
│   ├── ...
│   └── package.json
// cdk/lib/web-app-stack.ts

import { Construct } from 'constructs';
import { Stack, StackProps } from 'aws-cdk-lib';
import { Nextjs } from 'cdk-nextjs-standalone';

export class WebAppStack extends Stack {
    constructor(scope: Construct, id: string, props?: StackProps) {
        super(scope, id, props);

        new Nextjs(this, 'Web', {
            nextjsPath: '../web-frontend'
        });
    }
}

When I execute cdk deploy, the asset bundling stage fails with the error cp: cannot stat ‘/Users/Alistair/Sites/next-lambda/web-frontend/.next/web-frontend/.next/required-server-files.json’: No such file or directory. The actual locations of required-server-files.json are web-frontend/.next and web-frontend/.next/standalone/.next.

I've tried moving the Next application into the cdk directory and changing the value of the nextjsPath prop to ./web-frontend. Using this directory structure, the error reads cp: cannot stat ‘/Users/Alistair/Sites/next-lambda/cdk/web-frontend/.next/standalone/web-frontend/.next/required-server-files.json’: No such file or directory. In this case, the required-server-files.json does exist at the location specified in the error message, but seemingly could not be found when the code ran.

Allow Different Types of Compute Other Than Lambda

Currently, it is assumed that lambda will be used as the source of compute for the NextjsDistribution construct. Instead of passing serverFunction and imageOptFunction directly into this construct, what if it was abstracted away and serverHttpOrigin and imageOptOrigin were passed into the construct such that other types of compute could be defined by user and passed in?

I likely won't have time to implement this, but said I would create this issue in #108. Wanted to create the issue in case someone else wanted to implement.

QUESTION: How to add additional behaviors to the generate distribution?

Hi, can you help me with one example on how to add additional behaviors?, is that possible?

I tried something like this, but it doesn't work :(

const nextjs = new Nextjs(scope, 'Web', {
    projectRoot: '..',
    nextjsPath: '../webapp', // relative path to nextjs project root

    defaults: {
      assetDeployment: {
        bucket,
      },
      distribution: {
        customDomain: {
          domainName: `${
            env !== Environment.prod ? `${env}.` : ''
          }${domainName}`,
          ...(env === Environment.prod
            ? { domainAlias: `www.${domainName}` }
            : {}),
          hostedZone,
          certificate,
        },
        cdk: {
          distribution: {
            additionalBehaviors: { // spellchecker: disable-line
              '/graphql': {
                origin: new HttpOrigin(appSyncHostName),
                allowedMethods: AllowedMethods.ALLOW_ALL,
                viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
                cachePolicy: appSyncCachePolicy,
                originRequestPolicy: appSyncOriginRequestPolicy
              }
            }
          }
        }
      },
    },

I did noticed that despite I override the default bucket an empty bucket is generated?, might be the case?:

 assetDeployment: {
        bucket,
      },

Custom Build Command

Hello there im currently looking at moving away from the now-unsupported sls-next cdk construct, and came accross this implementation. it all looks very promising, however I noticed that there is no current way to have a custom build command. only npm run build .

we use an Nx monorepo (version 14.8.6 + nextjs 12.3.1) with multiple nextjs applications, so having a single npm run build is not really usable for us. (we also use yarn so that's a something else too)

an alternative would be to be able to point this in the direction of an already built application (this is what we do currently, and allow nx to figure out if it needs building)

Allow another prefix for Edge functions

Each edge function stack is prefixed with "Nextjs-" by default.

If one want to deploy more than one NextJS app, then this behavior might lead to unforeseen issues.

Access denied

Following the docs, we receive an cloud formation deployment error

Received response status [FAILED] from custom resource. Message returned: Access Denied Logs: /aws/lambda/cmWebsiteDev-WebAssetDeploymentRewriteOnEventHandl-D5vXeHccathS at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/services/s3.js:710:35) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:686:14) at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10) at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12) at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10 at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9) at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:688:12) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18) (RequestId: cf2684d9-8475-476d-900e-56d9b6b554d8)

EDIT: Looks like the policy for the RewriteOnEventHandler in NextjsAssetsDeployment.ts has insufficient permissions (?)

SECOND EDIT:
For debugging purposes I increased the permissions for the RewriteOnEventHandler. It still fails, but this due to a non existing s3 key

Received response status [FAILED] from custom resource. Message returned: The specified key does not exist.
Logs: /aws/lambda/cmWebsiteDev-WebAssetDeploymentRewriteOnEventHandl-0l0jlup8xw3J

Npm install of cdk-nextjs-standalone fails if esbuild is not set specifically to 0.15.13

Tried to install via:

npm i cdk-nextjs-standalone

command sh -c -- node install.js
npm ERR! cdk-nextjs-standalone/node_modules/esbuild/install.js:94
npm ERR!     throw new Error(Expected ${JSON.stringify("0.15.13")} but got ${JSON.stringify(stdout)});
npm ERR!           ^
npm ERR!
npm ERR! Error: Expected "0.15.13" but got "0.14.54"
npm ERR!     at validateBinaryVersion (node_modules/cdk-nextjs-standalone/node_modules/esbuild/install.js:94:11)
npm ERR!     at node_modules/cdk-nextjs-standalone/node_modules/esbuild/install.js:243:5

npm ERR! A complete log of this run can be found in:
npm ERR!     .npm/_logs/2022-11-25T10_03_13_696Z-debug-0.log 

Workaround:
npm i -D [email protected] cdk-nextjs-standalone

Monorepo support

Does CNS support monorepos? It currently executes npm run build in the nextjs app folder, but for monorepos such as turbo, we'd want to execute the build at the root of the package so it properly symlinks all the packages.

Adding this works:

    const buildResult = spawn.sync('npm', ['run', 'build'], {
      cwd: this.props.isMonorepo ? '.' : nextjsPath, // if monorepo, build at the root
      stdio: this.props.quiet ? 'ignore' : 'inherit',
      env: buildEnv,
    });

Default AssetDeployment Slow

The default NextJsAssetsDeployment uses 128MB of memory. That results in (in my experience) a 46 sec. upload for a 3MB site. Could we increase the default memory to maybe 1024MB to speed it up? I'd think that's a good default. I'd be ok with higher.

Also, after doing a little research, it looks like first the BucketDeployment construct is used to download the s3 files from the CDK assets bucket and then upload them into the Nextjs static site bucket. Then NextjsS3EnvRewriter downloads the files from the static site bucket (again), replaces environment variables, and then re-uploads to static site bucket. Is that correct?

What if instead there was 1 custom resource that downloaded files from CDK assets bucket, replaced environment variables, and then uploaded to static site bucket? I could take a shot at this in the next couple weeks. I would utilize this.

Allow choice of package manager

In NextJSBuild, it would be convenient to choose between "yarn" or "npm".
The NextjsBuildProps could be extended with an additional option of a packageManager using "npm" as default (to not break existing builds).

ENOENT: no such file or directory, open '/var/task/app/.next/required-server-files.json

I'm getting an error on the ServerHandler lambda function whenever I try to access the CF url:

{
    "errorType": "Error",
    "errorMessage": "ENOENT: no such file or directory, open '/var/task/app/.next/required-server-files.json'",
    "code": "ENOENT",
    "errno": -2,
    "syscall": "open",
    "path": "/var/task/app/.next/required-server-files.json",
    "stack": [
        "Error: ENOENT: no such file or directory, open '/var/task/app/.next/required-server-files.json'",
        "    at Object.openSync (node:fs:585:3)",
        "    at Object.readFileSync (node:fs:453:35)",
        "    at Object.<anonymous> (/var/task/app/server.cjs:813:35)",
        "    at Module._compile (node:internal/modules/cjs/loader:1105:14)",
        "    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)",
        "    at Module.load (node:internal/modules/cjs/loader:981:32)",
        "    at Function.Module._load (node:internal/modules/cjs/loader:822:12)",
        "    at Module.require (node:internal/modules/cjs/loader:1005:19)",
        "    at require (node:internal/modules/cjs/helpers:102:18)",
        "    at _tryRequireFile (file:///var/runtime/index.mjs:869:37)"
    ]
}

The nextjs App works locally (next dev and next build) and I'm accessing the root url I get from the cloudfront distribution. Am I missing something else?

`v3.2.0` causes `/public` assets to return 503

Problem

When deploying my Next.js app with v3.2.0 my previously-working references of /public folder assets (ie. /image/kevin.webp, not to be confused with Next-Image usage) began returning a generic CloudFront 503.

Similarly, not-found routes returned the same CloudFront 503.

Steps I took to remediate

Downgrade from v3.2.0 to v3.1.0 and redeploy with AWS CDK.

/public images began working again.
❌ Not-found routes began returning a generic S3 403, XML page.

Root cause

TBD, but should be within this diff: v3.1.0...v3.2.0

Add staticResponseHeadersPolicy to NextjsProps

I would like to reuse the same policy for different cloudfront distributions. It is possible for the cachePolicies and the originRequestPolicies. So could staticResponseHeadersPolicy be added to the NextjsProps to be reused?

Refreshing Page on Route Returns Data Instead of HTML

Hi, thank you for this incredible open source CDK construct!

I've run into an issue where I've deployed my Next.js site successfully and it mostly works, except when I refresh the page route on a specific route. Here is what happens:

I load the website by accessing it here: https://d1p0a0h0rn07pc.cloudfront.net which works great. See screenshot below for what I see:

image

You'll notice that the URL in the screenshot is https://d1p0a0h0rn07pc.cloudfront.net/batches because my app/page.tsx looks like:

export default function Root() {
  const router = useRouter();
  useEffect(() => {
    router.push("/batches");
  }, [router]);
}

Now, the issue occurs when I refresh the page on https://d1p0a0h0rn07pc.cloudfront.net/batches, I get this:

image

I'm not sure if this is an issue with this construct or I'm doing something wrong. I guess I could do redirect on server, but thought maybe this is an edge case this construct would want to fix.

Folder structure layout:
image

[AWS-Lambda] Error: Cannot find module 'next/dist/server/next-server'

Describe Issue

I can see the CloudFormation output creates the CloudFront, Lambda, and S3 resources in AWS. However, visiting the CloudFront URL throws an internal server error. When I look at the CloudWatch logs, it says there is an error finding server.js for the WebServerHandler lambda.

My Setup

I am invoking the cdk-nextjs-standalone construct like this:

const myNextJSApp = new Nextjs(this, 'OneSalesWeb', {
  nextjsPath: '../demo-cdk-web',
  projectRoot: '../demo-cdk-web'
})

I have also tried invoking like this, with the same result

const myNextJSApp = new Nextjs(this, 'OneSalesWeb', {
  nextjsPath: '../demo-cdk-web',
  projectRoot: '..'
})

Expected Behavior

For the WebServerHandler to find the required server.js file and serve the NextJS site.

Actual Behavior

A runtime error is thrown. See the below CloudWatch output for more details.

2023-01-11T19:26:26.158Z	undefined	ERROR	Uncaught Exception 	{
    "errorType": "Runtime.ImportModuleError",
    "errorMessage": "Error: Cannot find module 'next/dist/server/next-server'\nRequire stack:\n- /var/task/server.js\n- /var/runtime/index.mjs",
    "stack": [
        "Runtime.ImportModuleError: Error: Cannot find module 'next/dist/server/next-server'",
        "Require stack:",
        "- /var/task/server.js",
        "- /var/runtime/index.mjs",
        "    at _loadUserApp (file:///var/runtime/index.mjs:1000:17)",
        "    at async Object.UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:1035:21)",
        "    at async start (file:///var/runtime/index.mjs:1200:23)",
        "    at async file:///var/runtime/index.mjs:1206:1"
    ]
}

Unable to add alternate domain names to distribution

Adding alternate names to the NextJs distribution does not seem to work

const nextJs = new Nextjs(this, 'NextLambdaEdge', {
      // ...

      defaults: {
        distribution: {
          customDomain: {
            domainName: props.reactDomainName,
            hostedZone: HostedZone.fromHostedZoneAttributes(this, 'HostedZone', <HostedZoneAttributes>{
              hostedZoneId: props.hostedZoneId,
              zoneName: props.zoneName,
            }),
            certificate: Certificate.fromCertificateArn(this, 'Certificate', props.certificateArn),
            alternateNames: props.reactAlternateNames, // this doesn't work
          },
          // ....
        },
      },
    });

It seems like it only takes the domainName property into effect

private buildDistributionDomainNames(): string[] {

Does not resolve the 404 page

Cloudfront returns ERROR 403: Forbidden instead of the actual 404 page (as in local).

May be some misconfiguration on my side?, my deployment is pretty simple:

export function buildWebDistribution(
  scope: Construct,
  env: Environment,
  domainName: string,
  hostedZone: IHostedZone,
  certificate: ICertificate
) {
  const bucket = buildMainWebS3Bucket(scope, env);
  const webDomainName = `${
    env !== Environment.prod ? `${env}.` : ''
  }${domainName}`;
  const nextjs = new Nextjs(scope, 'Web', {
    projectRoot: '..',
    nextjsPath: '../webapp', // relative path to nextjs project root

    // See: https://github.com/jetbridge/cdk-nextjs/blob/main/src/Nextjs.ts#L108
    imageOptimizationBucket: bucket,
    defaults: {
      assetDeployment: {
        bucket,
      },
      distribution: {
        customDomain: {
          domainName: webDomainName,
          ...(env === Environment.prod
            ? { domainAlias: `www.${domainName}` }
            : {}),
          hostedZone,
          certificate,
        },
      },
    },
  });

  return nextjs;
}

Verified in v3.0.0 and 2.1.4

warn - You are using a non-standard "NODE_ENV" value

warn  - You are using a non-standard "NODE_ENV" value in your environment. This creates inconsistencies in the project and is strongly advised against. Read more: https://nextjs.org/docs/messages/non-standard-node-env

I don't know why this is popping up during build. It should really only ever be set to development or production and I haven't been able to determine what next thinks the value is or why.

Use aws-sdk v3

As I understand, aws-sdk is bloated and doesn't allow for tree shaking.
Consider using v3: @aws-sdk/client-s3.

It looks like this library is only using S3.
Nvm, my fork was on an older commit.

Unable to do deployment through Windows with WSL

When trying to deploy a NextjsDistribution to AWS through a Windows environment, some commands doesn't from WSL doesn't work properly because of paths or syntax.
That happens when bundling the ImageOptimizationLambda and when building NextjsBuild.

Expected Behavior

Run cdk deploy, bundle and deploy the NextjsDistribution.

├ Running "yarn build" in <path>
$ next build
info  - Linting and checking validity of types
info  - Compiled successfully
info  - Collecting page data
info  - Generating static pages (3/3)
info  - Finalizing page optimization

Route (pages)                              Size     First Load JS
┌ ○ /                                      443 B          73.7 kB
├   /_app                                  0 B            73.3 kB
└ ○ /404                                   182 B          73.4 kB
+ First Load JS shared by all              73.3 kB
  ├ chunks/framework-2c79e2a64abdb08b.js   45.2 kB
  ├ chunks/main-0ecb9ccfcb6c9b24.js        27 kB
  ├ chunks/pages/_app-2855696d374eec2d.js  287 B
  └ chunks/webpack-8fa1640cc84ba8fe.js     750 B

○  (Static)  automatically rendered as static HTML (uses no initial props)

info  - Creating an optimized production build ..+ cd /mnt/<path>/standalone
+ zip -ryq1 /mnt/c/Users/<user>/AppData/Local/Temp/standalone-IinlJm/standalone.zip .
Bundling asset DEV-Web-Admin-MAIN/<Stack_Name>-Web-Admin-ImageOpsLambda-DEV/Code/Stage...
D:\<path>\required-server-files.json
        1 file(s) copied.
warning package.json: No license field
$ D:\<path>\node_modules\.bin\esbuild --bundle D:\<path>\node_modules\cdk-nextjs-standalone\assets\lambda\ImageOptimization\index.ts --target=node16 --platform=node --outfile=D:\<path>\cdk.out\bundling-temp-11162cfe73d40f0248812214cb3d7a6342de11bdaadacd770b227a07922345df\index.js --minify

  cdk.out\bundling-temp-11162cfe73d40f0248812214cb3d7a6342de11bdaadacd770b227a07922345df\index.js  1.3mb

[Warning at /DEV-Web-Admin-MAIN/<Stack_Name>-Web-Admin-Distribution-DEV/DefaultOriginRequestEdgeFn/Fn] AWS Lambda has changed their authorization strategy, which may cause client invocations using the 'Qualifier' parameter of the lambda function to fail with Access Denied errors.
If you are using a lambda Version or Alias, make sure to call 'grantInvoke' or 'addPermission' on the Version or Alias, not the underlying Function
See: https://github.com/aws/aws-cdk/issues/19273

✨  Synthesis time: 21.61s

DEV-Certificate-MAIN (DEV-Certificate-ADMINWEB): building assets...

Current Behavior

Whenever bundling with Windows and WSL, deployment will throw an error with the .zip compression on NextjsBuild.ts at line 259 because paths syntax is different.
Then, fixing it, ImageOptimizationLambda.ts will fail at line 80 because cp is not recognize as a valid command (it is copy at windows environment)

Steps to Reproduce

  1. Create a stack with all necessary constructs: Bucket, NextjsBuild, NextjsLayer, NextJsLambda, ImageOptimizationLambda and NextjsDistribution.

Context (Environment)

I am trying to do test deployments on my machine before creating a CI/CD to handle the deployment on AWS side, I was able to do the testing by manually changing those lines in node_modules, but would be cool to support it natively from the library.

How does cdk-nextjs compare with SST

I've seen multiple comments from one of the main contributors @khuezy recommending the SST library with open-next. In my eyes that is a competing library that shares the same responsiblities as cdk-nextjs, so it makes me wonder if you're planning to deprecate this one in favour of SST. Or do you foresee any situation where using cdk-nextjs is preferrable?

I'm about to start the migration from Vercel to AWS, and would appreciate any guidance in deciding which library to choose.

HostedZone look up is incorrect when a string is passed in.

The else/if block needs to be swapped.
For most use cases, the user just wants to pass in hostedZone: 'example.com' instead of the object:

{ hostedName: 'example.com', hostedZoneId: 'user have to manually retrieve this'}
protected lookupHostedZone(): route53.IHostedZone | undefined {
   ...
    } else if (customDomain.hostedZone) {
      hostedZone = customDomain.hostedZone;
    } else if (typeof customDomain.hostedZone === 'string') {
      hostedZone = route53.HostedZone.fromLookup(this, 'HostedZone', {
        domainName: customDomain.hostedZone,
      });
   ...
  }

Requests to S3 are malformed / missing bucket name in WebServerHandler Lambda

Taken at face value, it looks like this parameter is missing from a configuration value somewhere in the Next.js app, deployed Lambda, etc. I'm not sure where this comes from yet, or who / what is responsible for supplying it, but it seems like it should be required. Also, it seems at least possible this is just a red herring for something bigger / different, but I'm creating this issue as a starting point.

Here are the steps I took to reproduce this error:

  1. Create new AWS Account
    To do this I just created a brand new account in my organization.

  2. Start a brand new NextJS project
    npx create-next-app@latest

Note: here are the resulting dep versions:

{
  "name": "nextjs-cdk-test",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@types/node": "20.3.1",
    "@types/react": "18.2.13",
    "@types/react-dom": "18.2.6",
    "autoprefixer": "10.4.14",
    "eslint": "8.43.0",
    "eslint-config-next": "13.4.6",
    "next": "13.4.6",
    "postcss": "8.4.24",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tailwindcss": "3.3.2",
    "typescript": "5.1.3"
  }
}
  1. Start new CDK project
    npx cdk init sample-app —language typescript

Note: here are the resulting dep versions:

{
  "name": "cdk",
  "version": "0.1.0",
  "bin": {
    "cdk": "bin/cdk.js"
  },
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "test": "jest",
    "cdk": "cdk"
  },
  "devDependencies": {
    "@types/jest": "^29.5.1",
    "@types/node": "20.1.7",
    "aws-cdk": "2.84.0",
    "cdk-nextjs-standalone": "^3.2.0",
    "esbuild": "^0.17.16",
    "jest": "^29.5.0",
    "ts-jest": "^29.1.0",
    "ts-node": "^10.9.1",
    "typescript": "~5.0.4"
  },
  "dependencies": {
    "aws-cdk-lib": "2.84.0",
    "constructs": "^10.0.0"
  }
}
  1. Move the Next.js app into a sub-directory into the CDK directory called “web”

Screenshot 2023-06-21 at 12 36 38 PM

  1. In root directory, bootstrap CDK
    cdk bootstrap aws://XX4312/us-east-2 —profile nextjs-cdk-test-deployer

SUCCESS!!

 ~/S/c/t/**cdk**  …  cdk bootstrap aws://XX4312/us-east-2 —profile nextjs-cdk-test-deployer                                                                                                   1181ms  Wed Jun 21 12:33:46 2023
 ⏳  Bootstrapping environment aws://XX4312/us-east-2…
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Using default execution policy of ‘arn:aws:iam::aws:policy/AdministratorAccess’. Pass ‘—cloudformation-execution-policies’ to customize.
**CDKToolkit**: creating CloudFormation changeset…
 ✅  Environment aws://XX4312/us-east-2 bootstrapped.
  1. Follow directions on quickstart

https://github.com/jetbridge/cdk-nextjs

In short, I added the dependencies listed

npm install -D [email protected] cdk-nextjs-standalone

Then modified the sample-app CDK stack as such:

import { Stack, StackProps } from “aws-cdk-lib”;
import { Nextjs } from “cdk-nextjs-standalone”;
import { Construct } from “constructs”;

export class CdkStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    new Nextjs(this, “Web”, {
      nextjsPath: “./web”, // relative path to nextjs project root
    });
  }
}
  1. Running synth results in failure because no region specified

cdk synth CdkStack —profile nextjs-cdk-test-deployer

...
...
Error: stacks which use EdgeFunctions must have an explicitly set region
  1. Update default cdk stack config with region fixes EdgeFunction issue

I just modified the default (otherwise unmodified) bin/cdk.ts file produced from the cdk init sample-app .. to include a region (and I also added my account number:

#!/usr/bin/env node
import * as cdk from “aws-cdk-lib”;
import { CdkStack } from “../lib/cdk-stack”;

const app = new cdk.App();
new CdkStack(app, “CdkStack”, {
  env: { account: “XX4312”, region: “us-east-2” },
});
  1. Cdk synth worked now

cdk synth CdkStack —profile nextjs-cdk-test-deployer

┌ Building Next.js app ▼ ...
├ Running "npx --yes open-next build" in ./web
Next.js v13.4.6
OpenNext v2.0.0

┌─────────────────────────────────┐
│ OpenNext — Building Next.js app │
└─────────────────────────────────┘


> [email protected] build
> next build

- info Creating an optimized production build  
- info Compiled successfully
- info Linting and checking validity of types  
- info Collecting page data  
- info Generating static pages (4/4)
- info Finalizing page optimization  

Route (app)                                Size     First Load JS
┌ ○ /                                      0 B                0 B
└ ○ /favicon.ico                           0 B                0 B
+ First Load JS shared by all              77.6 kB
  ├ chunks/769-8ab68f842512379f.js         25.2 kB
  ├ chunks/bce60fc1-49ee79ad31766ac6.js    50.5 kB
  ├ chunks/main-app-c2f0e612a52199d9.js    216 B
  └ chunks/webpack-737972036df45a17.js     1.64 kB

Route (pages)                              Size     First Load JS
─ ○ /404                                   182 B          74.7 kB
+ First Load JS shared by all              74.5 kB
  ├ chunks/framework-8883d1e9be70c3da.js   45 kB
  ├ chunks/main-542a92a81d12d254.js        27.7 kB
  ├ chunks/pages/_app-998b8fceeadee23e.js  195 B
  └ chunks/webpack-737972036df45a17.js     1.64 kB

○  (Static)  automatically rendered as static HTML (uses no initial props)


┌──────────────────────────────┐
│ OpenNext — Generating bundle │
└──────────────────────────────┘

Bundling static assets...
Bundling cache assets...
Bundling server function...
Bundling revalidation function...
Bundling image optimization function...
Bundling warmer function...
+ cd /Users/kevin/Sites/cdk-nextjs-sandbox/test-2/cdk/web/.open-next/server-function
+ zip -ryq1 /var/folders/1b/0m12cbyn1td6lf9_25xr4v5w0000gn/T/nextjs-cdk-build-gUn9Sj/standalone/serverFn.zip .
+ cd /var/folders/1b/0m12cbyn1td6lf9_25xr4v5w0000gn/T/nextjs-cdk-build-gUn9Sj/static
+ zip -ryq1 /var/folders/1b/0m12cbyn1td6lf9_25xr4v5w0000gn/T/nextjs-cdk-build-gUn9Sj/static/assets/assets.zip .
└ Finished preparing NextJS app for deployment
Resources:
  WebBucketE51352C0:
    Type: AWS::S3::Bucket
    Properties:
      Tags:
        - Key: aws-cdk:auto-delete-objects
          Value: "true"
...
...
  1. Trying to deploy resulted in bootstrap error (even though I already bootstrapped?)

I’m not sure why, but when I ran cdk deploy I got an error, even though I already ran cdk bootstrap — profile … - running cdk bootstrap again

cdk bootstrap —profile nextjs-cdk-test-deployer did show that one region was bootstrapped, but not another. So perhaps there is some sort of multi-region (something?) that is required by the Next.js cdk setup? Note below “no changes” for one region but not the other.

...
...
CDKToolkit: creating CloudFormation changeset...

 ✨ hotswap deployment skipped - no changes were detected (use --force to override)

 ✅  Environment aws://XX4312/us-east-2 bootstrapped (no changes).
 ✅  Environment aws://XX4312/us-east-1 bootstrapped.
...
...
  1. Cdk deploy

cdk deploy CdkStack —profile nextjs-cdk-test-deployer

This “worked”, deploy kicked off (prompted to confirm security changes and such, but kicked off as expected).

Do you wish to deploy these changes (y/n)? y
**CdkStack**: deploying… [1/2]
**CdkStack**: creating CloudFormation changeset…

 ✅  CdkStack

✨  Deployment time: 570.86s

Stack ARN:
arn:aws:cloudformation:us-east-2:XX4312:stack/CdkStack/2ea9c7c0-1054-11ee-b947-06023923a9b7

✨  Total time: 581.29s
  1. Login to management console to view domain for CloudFront

I’m not aware of any other way to get the cloud front distribution domain name, so I just logged into the AWS console to get the domain, in my case

Screenshot 2023-06-21 at 1 06 13 PM

  1. Visiting domain in a browser results in Next.js’s 500 error page

Screenshot 2023-06-21 at 1 07 40 PM

Note that the favicon.ico request also resulted in a 500

  1. Visiting the Lambda console, I see multiple Lambdas were created

Screenshot 2023-06-21 at 1 10 03 PM

  1. CloudWatch logs for the “WebServerHandler” show repeated errors

E.g.

2023-06-21T17:04:38.390Z	75882801-8e7c-4100-9c33-6101a6aec3f9	ERROR	{
  clientName: 'S3Client',
  commandName: 'ListObjectsV2Command',
  input: { Bucket: undefined, Prefix: 'ggXh0qs_tsiubcOgG_DEu/index' },
  error: Error: No value provided for input HTTP label: Bucket.
      at _V (/var/task/cache.cjs:15:9782)
      at ase (/var/task/cache.cjs:31:99926)
      at async /var/task/cache.cjs:2:382,
  metadata: undefined
}

The other Lambda logs didn’t seem to have anything that looked like errors

Use `Nextjs` outside of us-east-1?

When I try to use Nextjs in a region other than us-east-1, I get the error:

Error: Resolution error: Resolution error: Resolution error: Resolution error: Cannot use resource 'ss-stickb/ui/NextSite/ServerHandler' in a cross-environment fashion, the resource's physical name must be explicit set or use PhysicalName.GENERATE_IF_NEEDED.

This is because this construct uses cloudfront.experimental.EdgeFunction construct which creates a separate stack in us-east-1. I want to deploy to eu-north-1 and so I ran into this error. Has anyone else run into this? How can I get around this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.