Curious about CDK bundling vs what you're doing here.
Related to issue #250, I am curious if there is a reasoning why you perform an execSync here: https://github.com/jetbridge/cdk-nextjs/blob/main/src/NextjsBuild.ts#L155-L159 rather than using the built in CDK bundling that uses docker images to execute the build and produce the artifacts.
I think performing bundling this way will accomplish a couple of issues and make the overall library more "stable":
- By building in a docker image (CDK's default), we can control platform builds avoiding node constructs for being built on an M1 Mac not work in lambda because its using amd64 architecture (like is common with something like Sharp)
- It also means we can control the build process more finely, and having CDK be able to differentiate between next versions more cleanly.
You can see this in action in the built in NodeJSFunction object here: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-lambda-nodejs/lib/function.ts#L147, you can also see how bundling works here: https://github.com/aws/aws-cdk/blob/main/packages/aws-cdk-lib/aws-lambda-nodejs/lib/bundling.ts#L59
Here is a blog about how this works: https://aws.amazon.com/blogs/devops/building-apps-with-aws-cdk/
I think this would be a good addition to the library, but I think it also fundamentally changes how the library works. I am just curious if this has been discussed before.
I'm not aware of any discussion around it previously, it sounds like an intriguing idea. The point about architecture is a good one.
I would point out there may be some complications though:
- The next build step happens in the directory containing nextjs I believe. But in a monorepo it will usually refer to dependencies in the project root, so the docker container would need to encompass the whole project or do some clever volume mounting of
node_modulesand work around the dependency tracing - If we're using
node_modulesfrom the host and the arch doesn't match then some native dependencies would probably not work in the guest build - Maybe slower if docker is being run in a VM? Probably not an issue for linux CI builds though.
But if those can be overcome it sounds like a decent idea worth trying!
Good points! I have actually run into some of these based. For some context, I run exclusively mono-repos with CDK and service code defined next to each other. I have spent a lot of time looking into bundling, but not specifically bundling NextJS.
Using the docker based bundling for NodeJSLambdas (this is completely customizeable, so something to figure out here), what happens is that CDK will mount your directory into a docker container (Iirc it just sends it the Docker context). Then the docker image will perform the necessary install steps. If you set it to bundle output, the container will used the installed node_modules directory and attempt to produce a single file using esbuid. In a mono repo, this will run into what you are describing. The NodeJSFunctionBundle construct has a way to define external modules, which forces the docker container to do the install in the image and produce an output that has node_modules. This works around your concern here. Further, bundling is really customizable to your use case, so I am confident that we can figure this out.
Yea, the point would be to not use node_modules from the host, it would be to load the lock file and the package.json into the docker context and run a fresh install there with the proper architecture.
I haven't found it that much slower tbh, there is also usually a fallback that bundling does that allows fallbacks to native builds (aka don't use docker) but I haven't needed to do that.
I think these are all easy to overcome, but it would need to be a bit thought full on how it works. I run this library in a mono repo, and if I have some time the next couple of days, I can maybe whip up a PoC on how this could work if you are all interested.
Sure, give it a try! Just please read this first: https://nextjs.org/docs/app/api-reference/config/next-config-js/output
Just as a data point, I had to run my build on arm64 so that sqlite bindings work on lambda (which by default is arm64). I would assume building on x86_64 should work if changed lambda to run on x86_64.
Also the same thing for the node version, ie. needed to match the ci and lambda versions.
For anyone wondering, the config should be something like
overrides: {
nextjsServer: {
functionProps: {
// So that it uses the same as from ci
runtime: lambda.Runtime.NODEJS_22_X,
// In case you can't run your builds on arm64
architecture: lambda.Architecture.X86_64,
},
},
},