Skip to content

Latest commit

 

History

History
117 lines (75 loc) · 6.04 KB

CONTRIBUTING.md

File metadata and controls

117 lines (75 loc) · 6.04 KB

Contributing

Getting started

Note that we are using Yarn v3 as our package manager and Lerna for monorepo support.

  1. First, fork the repo to your own github account and clone it.
  2. Install dependencies: yarn install
  3. Build all packages: yarn build

Running the tests

Unit tests

I recommend testing the specific package in the monorepo that you are working with. For example:

yarn test lambda-at-edge/

In watch mode:

yarn test --watch lambda-at-edge/

Note that you may need to first build some packages e.g for serverless-components before running the tests.

Integration tests

To run local integration tests, run the following command:

yarn integration

End-to-end tests

The end-to-end tests will automatically verify a real deployment to AWS using a test app and the Cypress testing framework. Currently, we have four sets of end-to-end tests, which each test various configurations:

  • next-app: basic app
  • next-app-with-trailing-slash: app with trailingSlash = true
  • next-app-with-basepath: app with basepath set
  • next-app-dynamic-routes: app that tests catch-all and other dynamic routes

The above always test the latest minor version of Next.js. These are also duplicated with prev- prefixes, which indicate they test the previous minor version of Next.js. As of November 7th, 2020, these are 10.0.x and 9.5.x respectively.

Each test app tests various combinations of configuration. The test app may also be useful for you to manually verify your changes. When making a PR, it is recommended to at least run the end-to-end tests for the basic app, i.e next-app. However, if you have difficulty setting up the end-to-end tests, no worries! We will also run them for you as part of the PR. For forks, this is run manually by a maintainer.

  1. Ensure you have built all your changes by running yarn in the root directory of serverless-next.js repository.
  2. Change directory to the test app, e.g the basic one is next-app.
  3. Run yarn install to ensure dependencies such as Cypress are installed.
  4. Run AWS_ACCESS_KEY_ID=xxx AWS_SECRET_ACCESS_KEY=xxx serverless (or npx serverless) to deploy your changes to your AWS account.
  5. Save the output CloudFront distribution URL somewhere.
  6. Run the Cypress tests using the following command, replacing CYPRESS_BASE_URL with your CloudFront distribution URL, and CYPRESS_NEXT_BUILD_ID with the Next.js build ID:
CYPRESS_NEXT_BUILD_ID=123 CYPRESS_BASE_URL=https://dxxxxxxxxxxxx.cloudfront.net yarn e2e

This will run the tests in headless mode using the Electron browser, which is based on Chromium. You can also run it in Chrome by adding arguments --browser chrome. For other options, refer to the Cypress documentation.

Deploying to AWS and testing your changes

In general, the above end-to-end tests should be sufficient. But if you want to use your own app, follow the below guide.

First, create your own test serverless component app and in the serverless.yml point the component field to your fork:

# serverless.yml
nextApp:
  component: "/path/to/your/fork/serverless-next.js/packages/serverless-components/nextjs-component"
  inputs: ...

Then from the app simply run serverless or npx serverless if you don't have the serverless cli installed. For debug logging, pass in --debug.

For interactive debugging of the deployment you may launch serverless through node like node --inspect node_modules/serverless/bin/serverless.js. From there you may attach and debug as any other Node.js app.

Note: If you are working with a TypeScript package make sure you build it (yarn build) before deploying ;)

Updating handler code

If you are updating the runtime handler code, please be mindful of increasing cold start times and/or handler size, especially when adding new dependencies or doing complex operations. Note that JS require time has the most impact on cold start times. While code size is also important, it has little effect on cold start times, because Lambda seems to cache the code pretty efficiently - and this can be mitigated by minifying the Next.js build.

Note that Node.js seems to have a minimum cold start time of ~150 ms, even on an hello-world handler. So the idea is to try to optimize everything else as much as possible.

For example, importing the built-in AWS SDK JS v2 at the top of default-handler.ts (outside of the handler) via the below:

import AWS from "aws-sdk";

or even just the S3 client:

import S3 from "aws-sdk/clients/s3";

could incur 100 ms or more cold start times on every handler invocation, even when it's not actually used. Also, although the aws-sdk (AWS SDK JS v2) is built into AWS Lambda's Node.js runtime, it is not modularized and will require a bunch of unused code. Even if importing just the S3 client, it also takes close to 100 ms because it imports code for all S3 operations, even if you use only one. In traditional server-based environments, we do not have to worry about this as it is a one-time cost, but since Lambda is a serverless environment, containers will get re-initialized and this becomes a performance problem.

See issue here for more information: #580

Instead, consider dynamically importing only when needed. For example, we use the AWS SDK JS v3 client which for S3:

const { S3Client } = await import("@aws-sdk/client-s3/S3Client");
const { PutObjectCommand } = await import(
  "@aws-sdk/client-s3/commands/PutObjectCommand"
);

We have configured Rollup to be able to bundle dynamic imports into default-handler or api-handler.

Note: in this example, for @aws-sdk (V3), we import from as deep as possible so that Rollup.js will have a minimal bundle size.

For code size, you can use tools like BundlePhobia to approximate the cost of adding a new dependency.