Giter VIP home page Giter VIP logo

sladg / nextjs-lambda Goto Github PK

View Code? Open in Web Editor NEW
166.0 4.0 19.0 10.37 MB

Lambda deployments for Nextjs12 & Nextjs13 (standalone). Easy CLI commands to get your standalone Next output to run in AWS Lambda (not @Edge)! Uses CDK in behind and produces code zips importable to Terraform, Serverless, Azure, etc.

License: MIT License

TypeScript 84.47% JavaScript 15.53%
image nextjs serverless aws-lambda lambda next12 aws-cdk next13 aws-cdk-construct aws-cdk-typescript

nextjs-lambda's Introduction

NextJS Lambda Utils

This is a project allowing to deploy Next applications (standalone options turned on) to AWS Lambda without hassle.

This is an alternative to existing Lambda@Edge implementation (see) as it has too many limitations (primarily inability to use env vars) and deployments take too long.

This library uses Cloudfront, S3, ApiGateway and Lambdas to deploy easily in seconds (hotswap supported).

Multiple domains can be specified within CLI and Routing as well as Certificates are handled automatically.

TL;DR

  • In your NextJS project, set output to standalone.
  • Run next build (will generate standalone next folder).
  • Run npx --package @sladg/nextjs-lambda cli pack (will create ZIPs).
  • Run npx --package @sladg/nextjs-lambda cli deploy (will deploy to AWS).
  • Profit ๐ŸŽ‰
next.config.js

const path = require('path')

module.exports = {
	compress: false,
	output: 'standalone',
	experimental: {
		esmExternals: false, // optional
		externalDir: true, // optional
		outputFileTracingRoot: path.join(__dirname, '../../'), // monorepo option
	}
}
  • Render frontfacing pages in Lambda,
  • Render API routes in Lambda,
  • Image optimization,
  • NextJS headers (next.config.js),
  • GetStaticPaths,
  • next-intl (i18n),
  • Middleware,
  • GetServerSideProps,
  • GetStaticProps,
  • NextJS rewrites (next.config.js),
  • Monorepo support,
  • Bundle Sharp together with image optimizer so Next uses it. Custom python optimizer used,
  • Custom babel configuration Not possible with Next standalone output,
  • ISR and fallbacks
  • Streaming

Usage

We need to create 2 lambdas in order to use NextJS. First one is handling pages/api rendering, second is solving image optimization.

This division makes it easier to control resources and specify sizes and timeouts as those operations are completely different.

Loading of assets and static content is handled via Cloudfront and S3 origin, so there is no need for specifying this behaviour in Lambda or handling it anyhow.

Server handler

This is a Lambda entrypoint to handle non-asset requests. We need a way to start Next in lambda-friendly way and translate ApiGateway event into typical HTTP event. This is handled by server-handler, which sits alongside of next's server.js in standalone output.

Image handler

Lambda consumes ApiGateway requests, so we need to create ApiGw proxy (v2) that will trigger Lambda.

Lambda is designed to serve _next/image* route in NextJS stack and replaces the default handler so we can optimize caching and memory limits for page renders and image optimization.

Optimizer used: imaginex

Environment variables

If using CDK, you can easily pass environment variables to Lambda. If .env file is present during build time, this will get picked up and passed to Lambda as file.

If env variables with prefix NEXT_ are present during deployment time, those will get picked up and passed as environment variables to Lambda.

The priority for resolving env variables is as follows (stopping when found):

  • process.env
  • .env.$(NODE_ENV).local
  • .env.local (Not checked when NODE_ENV is test.)
  • .env.$(NODE_ENV)
  • .env

Frontend environment variables are automatically resolved during build time! You will not be able to set NEXT_PUBLIC_ variables during deployment / runtime.

Via CDK

See NextStandaloneStack construct in lib/cdk/app.ts. Or just use cli deploy command so you don't have to manage CDK yourself. See CLI help command for all congiruation, notably, it's possible to set Timeout and Memory for lambda from CLI. It is advised to always use custom --stackName in deploy command as it will affect names of all resources and will help you distinguish between different environments/applications.

If you want to use it programatically, see this guide.

Packaging

In order to succefully deploy, you firstly need to include output: 'standalone' in your next.config.js setup. Secondly, any compression should be turned off as AWS is taking care of that. See compress: false in your config. Make sure to use NextJS in version 12 or above so this is properly supported.

Once output is set, you can go on and use your next build command as you normally would. To package everything, make sure to be in your project root folder and next folder .next and public exist. Packaging is done via NPM CLI command of @slack/nextjs-lambda pack.

It will create next.out/ folder with 3 zip packages. One zip Lambda's code, one is dependencies layer and one is assets layer.

  • code zip: include all files generated by next that are required to run on Lambda behind ApiGateway. Original handler as well as new server handler are included. Use handler.handler for custom one or server.handler for original one.
  • dependencies layer: all transpilied node_modules. Next includes only used files, dramatically reducing overall size.
  • assets layer: your public folder together with generated assets. Keep in mind that to public refer file, you need to include it in public/assets/ folder, not just in public. This limitation dramatically simplifies whole setup. This zip file is uploaded to S3, it's not included in Lambda code.

Server handler

Custom wrapper around NextServer to allow for passing ApiGateway events into Next server.

Cloudfront paths used:

  • default
  • _next/data/*

Static assets

Next uses multiple directories to determine which file should be served. By default next provides us with list of routes for API/images/assets/pages. To simplify the process as much as possible, we are tapping into resulting paths.

We are packaging those assets to simulate output structure and we are using S3 behind CloudFront to serve those files. Also, Image Handler is tapping into S3 to provide images, so correct folder structure is crucial.

Cloudfront paths used:

  • _next/*
  • assets/*

Keep in minda, Cloudfront does not allow for multiple regex patterns in single origin, so using extensions to distinguish image/server handlers is not doable.

nextjs-lambda's People

Contributors

bounds avatar davulrich avatar dependabot[bot] avatar fabiob avatar john-tipper avatar lou-alfaro avatar sladg 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

nextjs-lambda's Issues

`config.json` 's body is just `nextConfig`, not valid JSON.

So I am getting an error when uploading this to AWS Lambda:

/var/task/config.json: Unexpected token e in JSON at position 1

When you look in the generated config.json the entire body is as follows

nextConfig

so it is NOT valid json.

I did some digging in the code and found this area here where it seems to try and pull the contents of that config.json

const nextConfig = findInFile(generatedNextServerPath, nextServerConfigRegex)

using this regex:
https://github.com/sladg/nextjs-lambda/blob/1b278aef890e86f17ddf81998913ea88b8391b37/lib/consts.ts#LL1C14-L1C35

So when I do the same search locally in my generated files I find this bit of code:

  const nextServer = new NextServer({
    hostname,
    port: currentPort,
    dir: path.join(__dirname),
    dev: false,
    customServer: false,
    conf: nextConfig,
  })

Notice the second to last line there. It seems to just be pulling the string nextConfig rather than the value of it.

Here is my package.json

{
  "name": "xyz-www",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@apollo/client": "^3.7.13",
    "@types/node": "18.16.3",
    "@types/react": "18.2.0",
    "@types/react-dom": "18.2.1",
    "autoprefixer": "10.4.14",
    "bootstrap": "^5.3.0-alpha3",
    "eslint": "8.39.0",
    "eslint-config-next": "13.3.4",
    "filerobot-image-editor": "^4.4.0",
    "next": "13.3.4",
    "postcss": "8.4.23",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-filerobot-image-editor": "^4.4.0",
    "tailwindcss": "3.3.2",
    "typescript": "5.0.4"
  },
  "devDependencies": {
  }
}

Any suggestions?

Allow image lambda to fetch remote images

Consider following scenario:

  • URL for images is stored in database,
  • backend is using private S3 bucket to store data,
  • when request is made, backend signs image URL,
  • frontend displays image url (ERROR 500 currently).

next.config.js allows us to specify domains allowed.

Image lambda should check if URL is absolute or relative. If relative, use internal S3 bucket, if absolute, fetch remote image.

Hotswap CLI flag breaks first deploy

Added in commit fd1e1da

Hotswap results in first time deployments breaking

stderr: Stack Deployments Failed: Error: No stack named 'StandaloneNextjsStack-Temporary'

Process failed with error: Error: Command failed: /Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/bin/cdk deploy --app "node /Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/@sladg/nextjs-lambda/dist/cdk/app.js" --require-approval never --ci --hotswap

 โŒ  StandaloneNextjsStack-Temporary failed: Error: No stack named 'StandaloneNextjsStack-Temporary'
    at CloudFormationStack.assertExists (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:329:591559)
    at CloudFormationStack.get stackId [as stackId] (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:329:590784)
    at tryHotswapDeployment (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:329:598568)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async deployStack (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:335:651)
    at async deployStack2 (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:342:144797)
    at async /Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:342:130251
    at async run (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:342:128257)

 โŒ Deployment failed: Error: Stack Deployments Failed: Error: No stack named 'StandaloneNextjsStack-Temporary'
    at deployStacks (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:342:130558)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async CdkToolkit.deploy (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:342:146846)
    at async exec4 (/Users/dave/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/index.js:397:51795)
Stack Deployments Failed: Error: No stack named 'StandaloneNextjsStack-Temporary'

When using pnpm in monorepo, cannot find next-sever.js in the main lambda

Issue:
Cannot find next-sever.js in the main lambda
Remarks:
Works OK for yarn monorepo
Stack:

  • [email protected]
  • [email protected]
  • [email protected]
  • sladg/[email protected]
    Error:
    { "errorType": "Runtime.ImportModuleError", "errorMessage": "Error: Cannot find module 'next/dist/server/next-server'\nRequire stack:\n- /var/task/handler.js\n- /var/runtime/index.mjs", "stack": [ "Runtime.ImportModuleError: Error: Cannot find module 'next/dist/server/next-server'", "Require stack:", "- /var/task/handler.js", "- /var/runtime/index.mjs", " at _loadUserApp (file:///var/runtime/index.mjs:951:17)", " at async Object.UserFunction.js.module.exports.load (file:///var/runtime/index.mjs:976:21)", " at async start (file:///var/runtime/index.mjs:1137:23)", " at async file:///var/runtime/index.mjs:1143:1" ] }

Followed guids:
_Monorepos
In case you are using monorepo, there are few more requirements. Firstly, you need to setup outputFileTracing in your next.config.js see: vercel/next.js#36386 (comment).

Secondly, you need to setup hoistingLimits: workspace. We need node_modules to actually contain all the dependencies in order for NextJS to pick them up for standalone build.

Tested with [email protected] and [email protected]_

Update Changelog

Hi,

Could you update the changelog file to reflect the last updates?

Thanks for your work!

Handlers do not include dependencies.

Rollup build does not include dependencies as esbuild does.
Using node-resolve plugin with commonjs plugins in rollup does not work properly, switch to esbuild is probably wise move.

Allow for use of `www`

Generally speaking, using www has almost no down-sights compared to using root domain (apex), which has technical limitations such as CNAMEs.

After reading through RFCs on this topic, the preferred way is to have apex redirect to www and use www everywhere in case other subdomain is used.

Currently, it's not possible to easily follow this behaviour. You can specify www as subdomain, however, there is no mechanism to deal with redirection from apex out-of-box.

Deployment fails at ImageOptimization Handler

Hi, when I run npx --package ... deploy

Failed resources:
StandaloneNextjsStack-Temporary | 2:28:51 PM | UPDATE_FAILED        | AWS::Lambda::Function                           | ImageOptimizationNextJs (ImageOptimizationNextJsB167CF66) Resource handler returned message: "Lambda function StandaloneNextjsStack-Tem-ImageOptimizationNextJsB-MAFjARSAA8QV could not be found" (RequestToken: bd6daa8c-9bf9-6640-0e13-9dc06fb19e9c, HandlerErrorCode: NotFound)

stderr: 
 โŒ  StandaloneNextjsStack-Temporary failed: Error: The stack named StandaloneNextjsStack-Temporary failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Lambda function StandaloneNextjsStack-Tem-ImageOptimizationNextJsB-MAFjARSAA8QV could not be found" (RequestToken: bd6daa8c-9bf9-6640-0e13-9dc06fb19e9c, HandlerErrorCode: NotFound)
    at FullCloudFormationDeployment.monitorDeployment (/Users/user/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/api/deploy-stack.ts:505:13)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at deployStack2 (/Users/user/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/cdk-toolkit.ts:264:24)
    at /Users/user/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/deploy.ts:39:11
    at run (/Users/user/.npm/_npx/3b877e98bdaa260f/node_modules/p-queue/dist/index.js:163:29)

stderr: 
 โŒ Deployment failed: Error: Stack Deployments Failed: Error: The stack named StandaloneNextjsStack-Temporary failed to deploy: UPDATE_ROLLBACK_COMPLETE: Resource handler returned message: "Lambda function StandaloneNextjsStack-Tem-ImageOptimizationNextJsB-MAFjARSAA8QV could not be found" (RequestToken: bd6daa8c-9bf9-6640-0e13-9dc06fb19e9c, HandlerErrorCode: NotFound)
    at deployStacks (/Users/user/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/deploy.ts:61:11)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at CdkToolkit.deploy (/Users/user/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/cdk-toolkit.ts:338:7)
    at initCommandLine (/Users/user/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/cli.ts:364:12)


Dependency upgrade

Upgrade imaginex package to 0.17.0.

Improved codebase, cleaned-up, added support for height.

Specified target is invalid. Provided: "standalone" should be one of server, serverless, experimental-serverless-trace

Thanks for this project. I'm having trouble running next build with target: 'standalone'. next.config.js is:

module.exports = {
  async redirects() {
    return [
      {
        source: '/town/:slug',
        destination: '/towns/:slug',
        permanent: true,
      },
    ]
  },
  output: 'standalone',
  target: 'standalone',
  compress: false,
}

When I run next built I get the following output:

Error: Specified target is invalid. Provided: "standalone" should be one of server, serverless, experimental-serverless-trace

Running npx --package @sladg/nextjs-lambda next-utils pack without this results in Process failed with error: Error: Folder: /Users/<project root>/.next/standalone does not exist!

From the next docs it seems we can have output: 'standalone',? But this doesn't seem to work either, or where am I going wrong? Thanks.

Rename CLI command to `cli`

This is consistency improvement to avoid confusion between packages and stacks.

Requires BREAKING CHANGE as command call with change.
README to be updated, package.json definition to be updated.

Getting the response body returned with Base64 encoded with `compress: false`

Hey I am getting a Base64 encoded response back. Here is my next.config.js

const path = require('path')

module.exports = {
  compress: false,
  output: 'standalone',
  experimental: {
    esmExternals: false, // optional
    externalDir: true, // optional
    outputFileTracingRoot: path.join(__dirname, '../../'), // monorepo option
  }
}

Here is the package.json

{
  "name": "drawnby-www",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@apollo/client": "^3.7.13",
    "@types/node": "18.16.3",
    "@types/react": "18.2.0",
    "@types/react-dom": "18.2.1",
    "autoprefixer": "10.4.14",
    "bootstrap": "^5.3.0-alpha3",
    "eslint": "8.39.0",
    "eslint-config-next": "13.3.4",
    "filerobot-image-editor": "^4.4.0",
    "next": "^12.3.4",
    "postcss": "8.4.23",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-filerobot-image-editor": "^4.4.0",
    "tailwindcss": "3.3.2",
    "typescript": "5.0.4"
  }
}

Is there anything special I need to do on the APIGateway side for this to work?
I tried playing with the following integration properties:

  passthrough_behavior    = "WHEN_NO_MATCH"
  content_handling        = "CONVERT_TO_TEXT"

but it did not seem to help.

Let me know if I am missing something. Thanks!

[FR] Implement ISR

I have seen ISR is on the way, is there any ETA on when it will be implemented?

Support for Cloudfront Alternate Domain Names

Adding an option like --alternateDomainNames where domain names will be fed in a comma separated manner, or reading multiple entries of the option.

As far as i know there is no way of doing this.

Improve CLI documentation

Provide complete npx commands for CI/CD.
We need to include --package flag so npx know what to run, such as npx --package @sladg/nextjs-lambda next-utils pack

Add remove command

I want to have possibility to easily remove CloudFormation stack in programatic way.
It's not always possible to use Console and aws-cli is not straight forward enough for cleaning up.

This would be helpful for testing deployments as well as I can quickly deploy and remove the stack.

EOL announcement

After testing of open-next and SST I will be deprecating this package in foreseeable future in favour of SST and Open-Next.

Reasons are as following:

  • open-next took basic idea I had with this package and build complete support for developing Next,
  • connection with SST gives developers amazing experience when running and trying things locally - something I'm not planning to include or try to duplicate,
  • SST supports newer syntaxes and generally better keeps up with Next's changes thank to bigger community,
  • open-next builds on CDK and allows deployments to non-Edge lambdas, which go hand-in-hand with ideology of mine and this project,
  • Extra features such as ISR are supported.

Currently only one concern in mind:

  • Complexity of open-next and possibility to lot of things to brake (mapping of experimental flags, need to pass Next's env flags to build, Sharp bundling and generally using JS for image optimization - Python implementation we use now). However, with vivid community this is something, we can overcome.

One future issue I came by with current package as well as open-next

  • bundling of node_modules for Lambda's environment. Binary-specific dependencies get's bundled locally on host OS and might not be compatible with Lambda's runtime. This is industry-wide problem solvable by Docker and pipelines, however, not compatible with SST's dev experience.

Cheers!
Jan

Cloudwatch alerts

How do you guys configure cloudwatch alerts without Cloudformation config drift? Right now my plan is to fork the repo and add things to NextStandaloneStack, but is there something you do instead? I'm trying to get alerts when there are HTTP errors over a certain threshold, for both Next routes and image optimisation.

How to obtain {sharp,next,image}_handler.zip

First, thank you for the provided example/*, it is very helpful on helping me integrating this project.
I have no idea how to acquire these zips, could you document more on how to obtain these zips?

CDK Additional Information

Hey @sladg great library and this isn't really an issue as such, it's just more of an information gathering question.

I ran the commands that were mentioned and the app was definitely deployed but I'm not really familiar with CDK and that's kind of where my question lies.

Initially, I ran into an issue with the deploy command not being able to find the environment variables so that led me to installing cdk and running cdk bootstrap and that solved the issue as I was able to point it to the correct profile, account and region.

So my question is, is there a way I can configure AWS related attributes like the name of the Cloudfront resource or associating a domain with the Cloudfront distribution. This might be a CDK related question but I just wanted to drop it here to confirm that. I can indeed see CDK related config in the cdk.out folder and my guess is that editing the data there should help with this.

Open Next Community

Hi!

I'm not related to Open Next but I think your help in this joint project of several collaborators who implemented NextJS Serverless would be amazing and help centralize and maintain all the work.

I don't know if you've heard of the project since it's very recent, so just in case I'll leave it here.

Thank you very much for your work!

https://github.com/serverless-stack/open-next

Forward the Host header to the origin

On the application layer we need to access the Host header in the request object, however it's not equal to any of alternative domain names.

As of now, header behaviour is hardcoded as CacheHeaderBehavior.allowList('accept', 'accept-language', 'content-language', 'content-type', 'user-agent', 'authorization').

Adding host to this list breaks the API Gateway with 403 error, unless API Gateway is configured with custom domain names.

Error: Public folder assets must be nested in public/assets folder

Hello! I was using the npx --package @sladg/nextjs-lambda next-utils pack command and I keep getting the error: Process failed with error: Error: Public folder assets must be nested in public/assets folder.

My next config looks like this:

const nextConfig = {
	reactStrictMode: true,
	swcMinify: true,
	output: "standalone",
	compress: false,
};

I am using the command in the root directory and have my public folder structured like this: public/assets/files.svg, etc.

is there something im not setting up correctly?

Much appreciated!

Specify Profile for CDK to use

Hoping there would be a deploy flag for choosing an AWS profile from ~/.aws/credentials

Bit of a pain to modify [profile.default]to get this going.

Maybe there's a better way?

Add region as CLI flag

For safety while deploying, it's reasonable to pass region as part of the command instead of relying on env vars on given operating system.

This CLI parameter should be passed to CDK command and used for deployment.

wrong version from `--version`

The bundle for the latest version still has the previous version in it.

$ npx --package @sladg/nextjs-lambda cli --version                                                         1640ms
Need to install the following packages:
  @sladg/[email protected]
Ok to proceed? (y) y
7.0.3

See:

20657   โ”‚ // package.json
20658   โ”‚ var package_default = {
20659   โ”‚   name: "@sladg/nextjs-lambda",
20660   โ”‚   version: "7.0.3",

Packaging doesn't include index.html?

Hi. I am a beginner to AWS. I am using your CLI to deploy my Next.js app (13) to Lambda. The deployment process was successful and generated multiple URLs, but the S3 bucket the CLI created during packaging doesn't have index.html. My question is: how can I access to my app without index.html via Static website hosting in S3 Bucket settings? @sladg

StandaloneNextjsStack-Temporary.assetsBucketUrl doesn't work.

Screenshot 2023-04-13 at 10 50 11

Respect CDK version

Move CDK into peer dependency and allow for CDK dependencies to not be used / to have version defined in upstream.

Figure out passing of env vars into CDK when used as CLI

I want to be able to pass environment variables during deployment, at the same time, I don't want to manage CDK myself as that's unnecessary overhead.

We need a convention on how to pass env vars to Lambda, without exposing all env vars as some of them might be private / unnecessary for app run.

CLI options are parsed as booleans

Looks like commander default to options being treated as booleans. To pass in paths I think the CLI options need to be updated to something like '--standaloneFolder <path>'

Bundle image handler with Sharp

Existing implementation was error prone, creating unnecessary complexity and layers.
There needs to be easier way how to bundle image handler and include Sharp for Lambda as well.

Currently using Squoosh for image handling, which is not recommended for production use with Vercel.

Respect Next version

Move Next into peer dependency and build layer manually via npx. Parse next version from parent (allow parameter) to ensure compatibility with our implementation.

This means that we either pre-build all possible Next layers into AWS and use ARNs or we build during deployment time, which means slowing down whole process, but does not require external resources.

[FR] Allow to bypass Route53 DNS record.

Related to #79.

Add a flag that will disable automatic Route53 record creation, so it is possible to use other DNS servers, or fallback to just adding the custom domain name to Cloudfront.

Allow use of Serverless Framework instead of CDK

Serverless Framework has a deployment approach of a serverless.yml file defining a single API Gateway and multiple lambdas. This is different to the current CDK implementation where each lambda is associated with a separate APIG and each APIG is associated with a separate CloudFront origin.

If you have 2 lambdas associated with a single APIG, then each lambda needs to be given a different path, e.g. the server-handler lambda being associated with /server/* paths and the image-handler lambda being associated with /image/* paths. CloudFront allows for originPaths where different origins will use a different prefix when making requests to the origins. Thus it is possible to user Serverless Framework to route traffic to different origins, where each origin uses the SAME shared APIG but with different prefixes.

PROBLEM 1: The server-handler lambda uses the full path of the request passed to it from the APIG to determine what Nextjs resources to serve (the image-handler lambda does not, it just uses the query string passed to it and doesn't care about the path). If the server-handler lambda has a /server prefix passed to it then the routes in Nextjs are wrong.

SOLUTION: The serverless-http library used in this project has support for defining a basePath configuration parameter (details here), which will strip the basePath off the supplied path before passing the value on to the consumer. e.g. a request path of /server/_next/data* with basePath: '/server' would be converted to /_next/data* before being passed to Nextjs.

PROBLEM 2: There is currently no means of injecting a configuration value into the options that are supplied to serverless-http, see here. Additionally, the current version of serverless-http (3.0.2) has a bug such that the Typescript type definition of the Options object passed to slsHttp does not support basePath. I have had a PR for serverless-http merged (dougmoscrop/serverless-http#246) which will add this support, but I am waiting for a release of 3.0.3.

SOLUTION: I would like to add a PR to allow for the basePath to be optionally specified by an env variable, here. e.g.:

basePath: process.env.NEXTJS_LAMBDA_BASE_PATH

If no variable is specified then basePath is not set and there is no behaviour change. If the variable is set then the base path is stripped before being passed to Nextjs. This will allow for both Serverless Framework and CDK to be used as a deployment tool.

Unable to have an API route that returns binary data

I want to have an API route that returns binary data - by way of example, let's say that there's an API route that generates a dynamic image /api/image/generate and where the content is returned by something looking like this:

export default async (req: NextApiRequest, res: NextApiResponse) => {

...
  // buffer contains the image of type defined by mime, is of type Buffer
  res.setHeader('Content-Type', mime);
  res.setHeader('Content-Length', buffer.length);
  res.status(200).send(buffer);

}

If I have run Nextjs locally, this works fine. I can enter the API route directly into the browser and the image will render. However, if I run this in nextjs-lambda, I get an error. Specifically, the content length of the actual data returned is very much larger than the actual image size. I have an image of size 224214 and the content length reported by the server lambda is 403034 and the content is corrupted.

The issue is that the API Gateway (HTTP API) that sits in front of the lambda is not recognising the fact that the data being returned is binary and is mangling it (https://aws.amazon.com/blogs/compute/handling-binary-data-using-amazon-api-gateway-http-apis/). This library explicitly sets the response from the lambda to be non-binary: https://github.com/sladg/nextjs-lambda/blob/master/lib/standalone/server-handler.ts#L39

I don't see what the harm would be to remove that config flag: when we look at the way the serverless-http response from the Nextjs lambda is formatted, if the binary option is not specified then the response will not be treated as binary unless the underlying lambda returns a binary header or the Content-Type is set to be a binary type (by matching a type defined in process.env.BINARY_CONTENT_TYPES). Specifically, by not setting that flag we would hit this if statement.

Could we please consider removing that line?

This would allow for setting BINARY_CONTENT_TYPES=image/png,image/jpg,image/jpeg or similar and then such content would be returned to API Gateway in a way that APIG can handle it appropriately.

How to use the CDK

Would you be willing to provide usage instructions for importing and using the CDK constructs that you've made here so I can easily import them and add them to my own CDK code?

Context

I have other infra that I deploy to AWS (databases, APIs) and I'd want to deploy a NextJS app to Lambda as well.

Additional thoughts

I would like if there was a construct, say NextJSLambda, that I could import and deploy in my own stack similar to this.

import { NextJSLambda } from 'sladg/nextjs-lambda';

interface MyNextJSStack {
  config: Config;
  zoneId: string;
}

export class MyNextJSStack extends Construct {
  readonly nextJSApp;
  constructor(scope: Construct, id: string, props: MyNextJSStackProps) {
    super(scope, id);

    // Next
    this.nextJSApp = new NextJSLambda(this, `${id}Lambda`, {
      description: `${id} infra created by NextJSLambda construct`,
      runtime: Runtime.NODEJS_16_X,
      memory: 1024,
      timeout: Duration.seconds(30),
      withLogging: true,
      name: {
        apiLambda: `${id}Api`,
        imageLambda: `${id}Image`,
      },
      domain: {
        certificate,
        domainNames: [props.config.domainName],
        hostedZone: zone,
      },
      s3Props: {
        bucketName: `${props.config.prefixKebabCase}next-bucket`,
      },
    });
  }
}

What do you think?

Building Assets Failed: Error: StandaloneNextjsStack-Temporary: SSM parameter /cdk-bootstrap/hnb659fds/version not found.

Steps to reproduce
next build
npx --package @sladg/nextjs-lambda next-utils pack
npx --package @sladg/nextjs-lambda next-utils deploy
I get

[paul@rb demo-app]$ npx --package @sladg/nextjs-lambda next-utils deploy
Our config is:  {
  stackName: 'StandaloneNextjsStack-Temporary',
  appPath: '/home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/@sladg/nextjs-lambda/dist/cdk-app.js'
}
stdout: 
โœจ  Synthesis time: 1.63s


stdout: StandaloneNextjsStack-Temporary: building assets...


stdout: current credentials could not be used to assume 'arn:aws:iam::661723878467:role/cdk-hnb659fds-deploy-role-661723878467-us-east-1', but are for the right account. Proceeding anyway.

stderr: 
 โŒ Building assets failed: Error: Building Assets Failed: Error: StandaloneNextjsStack-Temporary: SSM parameter /cdk-bootstrap/hnb659fds/version not found. Has the environment been bootstrapped? Please run 'cdk bootstrap' (see https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)
    at buildAllStackAssets (/home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/build.ts:21:11)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at CdkToolkit.deploy (/home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/cdk-toolkit.ts:175:7)
    at initCommandLine (/home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/cli.ts:357:12)

stdout: 

stderr: Building Assets Failed: Error: StandaloneNextjsStack-Temporary: SSM parameter /cdk-bootstrap/hnb659fds/version not found. Has the environment been bootstrapped? Please run 'cdk bootstrap' (see https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)

Process failed with error: Error: Command failed: STACK_NAME=StandaloneNextjsStack-Temporary /home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/bin/cdk deploy --app "node /home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/@sladg/nextjs-lambda/dist/cdk-app.js" --require-approval never --ci

 โŒ Building assets failed: Error: Building Assets Failed: Error: StandaloneNextjsStack-Temporary: SSM parameter /cdk-bootstrap/hnb659fds/version not found. Has the environment been bootstrapped? Please run 'cdk bootstrap' (see https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)
    at buildAllStackAssets (/home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/build.ts:21:11)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at CdkToolkit.deploy (/home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/cdk-toolkit.ts:175:7)
    at initCommandLine (/home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/lib/cli.ts:357:12)
Building Assets Failed: Error: StandaloneNextjsStack-Temporary: SSM parameter /cdk-bootstrap/hnb659fds/version not found. Has the environment been bootstrapped? Please run 'cdk bootstrap' (see https://docs.aws.amazon.com/cdk/latest/guide/bootstrapping.html)

    at ChildProcess.exithandler (node:child_process:400:12)
    at ChildProcess.emit (node:events:513:28)
    at maybeClose (node:internal/child_process:1093:16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:302:5) {
  code: 1,
  killed: false,
  signal: null,
  cmd: 'STACK_NAME=StandaloneNextjsStack-Temporary /home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/aws-cdk/bin/cdk deploy --app "node /home/paul/.npm/_npx/3b877e98bdaa260f/node_modules/@sladg/nextjs-lambda/dist/cdk-app.js" --require-approval never --ci'
}

Is something missing in README.md?
Do I really need to run cdk bootstrap manually?
Thanks.

Consider sharing resources across stacks

AWS is limiting number of:

  • cache policies,
  • cloudfronts.

Those are typical resources limitations experiences. Cloudfront is not really solvable as each one is unique. However, cache policies are same across stacks so those could be shared (aka. create once and import to stacks).

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.