jetbridge / cdk-nextjs Goto Github PK
View Code? Open in Web Editor NEWDeploy a NextJS application using AWS CDK
Home Page: https://constructs.dev/packages/cdk-nextjs-standalone
License: Apache License 2.0
Deploy a NextJS application using AWS CDK
Home Page: https://constructs.dev/packages/cdk-nextjs-standalone
License: Apache License 2.0
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.
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'"
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.
I believe we can conditionally create this resource to speed up deployment when there's nothing to replace.
In my use case, I'm using Systems Manager to fetch env variables, which are already resolved, thus the NextjsS3EnvRewriter just bails after it reads the empty next-env.json
.
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?
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?
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.
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
Kind regards
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,
}
}
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 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.
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
The standalone folder does appear when running next build
from the root of the next project, however
next
: 13.1.1
cdk-nextjs-standalone
: ^2.0.4
aws-cdk-lib
: ^2.50.0
constructs
: ^10.0.5
aws-cdk
: ^2.50.0
esbuild
: ^0.15.18
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.
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"
}
}
}
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.
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.
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
nextjsPath
being something like path.join('..', 'frontend')
is not as intended.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.
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.
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.
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,
},
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)
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.
Right now defaults.distribution
and defaults.assetDeployment
are any
This is a limitation in jsii we need to figure out a workaround for
I want the type to be e.g. Partial<DistributionProps>
but we can't use Partial<>
in jsii
See this thread on the CDK slack for more details: https://cdk-dev.slack.com/archives/C01C8R4K2E6/p1665107002345799
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
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
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,
});
Awesome project here! (❤️ I've been looking for a while for a successor to https://github.com/serverless-nextjs/serverless-next.js)
Small request — It would be nice be able to use Node.js 18.x
as the Lambda Runtime
Line 7 in 9f17d6a
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.
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).
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?
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.
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.
TBD, but should be within this diff: v3.1.0...v3.2.0
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?
Error: ENOENT: no such file or directory, scandir '[...]/webapp/public'
One of the build step assumes there is a public folder before checking if folder exists.
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:
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:
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.
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.
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: '..'
})
For the WebServerHandler
to find the required server.js
file and serve the NextJS site.
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"
]
}
When I replaced my pages/index.tsx
w/ app/page.tsx
and deploy the app, I get a 404 - Page not found error.
Might be relevant: Vercel Discussion
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
cdk-nextjs/src/NextjsDistribution.ts
Line 525 in 8887b40
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
https://github.com/jetbridge/cdk-nextjs/blob/main/src/NextjsDistribution.ts#L21
const DEFAULT_STATIC_MAX_AGE = Duration.days(30);
should be:
const DEFAULT_STATIC_MAX_AGE = Duration.days(30).toSeconds();
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.
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.
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
.
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...
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)
Bucket
, NextjsBuild
, NextjsLayer
, NextJsLambda
, ImageOptimizationLambda
and NextjsDistribution
.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.
The SST construct NextjsSite uses serverless-nextjs to build Nextjs, which uses this: https://github.com/serverless-nextjs/serverless-next.js/blob/master/packages/libs/lambda-at-edge/src/image-handler.ts
I wonder if we can just copy that to the assets
folder, deploy that lambda, and somehow route /_next/image
requests to the optimizer to resize images.
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.
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,
});
...
}
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:
Create new AWS Account
To do this I just created a brand new account in my organization.
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"
}
}
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"
}
}
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.
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
});
}
}
cdk synth CdkStack —profile nextjs-cdk-test-deployer
...
...
Error: stacks which use EdgeFunctions must have an explicitly set region
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” },
});
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"
...
...
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.
...
...
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
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
Note that the favicon.ico
request also resulted in a 500
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
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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.