Git Product home page Git Product logo

serverless-next.js's Introduction

Serverless Next.js Component

logo

serverless GitHub contributors Financial Contributors on Open Collective npm latest npm alpha Build Status End-to-end Tests CircleCI Build Status Codacy Badge codecov Tested Next.js versions Cypress.io Platforms Tested Node.js versions

A zero configuration Next.js 10/11 serverless component for AWS Lambda@Edge aiming for full feature parity.

Please review features for a list of currently supported features.

Contents

⚠️ This README reflects the latest changes on the master branch. It may or may not yet be published to the latest (stable) or alpha release in npm. Please go to Releases, find the correct @sls-next/serverless-component version you are using, and open the README for that release for more accurate information. If a feature is listed in this README but not working, please first try upgrading to the most recent alpha release in npm.

⚠ this is currently using Serverless Components Beta (not GA version) as the project was started before GA. We are currently reworking how deployments will work in the future and exploring better IaC solutions such as CDK, CDK for Terraform, etc. and will make an announcement before end of the year on any updates.

Motivation

Since Next.js 8.0, serverless mode was introduced which provides a new low level API which projects like this can use to deploy onto different cloud providers. However, Next.js doesn't provide the full serverless routing logic, hence why this project is needed to fill the gap. The long-term vision is to allow you to self-host with various clouds, starting with AWS.

This project is a better version of the serverless plugin which focuses on addressing core issues like next 9 support, better development experience, the 200 CloudFormation resource limit and performance.

Design principles

  1. Zero configuration by default

There is little to no configuration needed. You can extend defaults based on your application needs.

  1. Feature parity with Next.js

Users of this component should be able to use Next.js development tooling, aka next dev. It is the component's job to deploy your application ensuring parity with all of next's features we know and love. We try to emulate all or most of the routing and server-side logic from Next.js and optimize it for a serverless environment. Below you can find a list of the features that are currently supported.

  1. Fast deployments / no CloudFormation resource limits.

With a simplified architecture and no use of CloudFormation, there are no limits to how many pages you can have in your application, plus deployment times are very fast! with the exception of CloudFront propagation times of course.

Features

Since we emulate the Next.js routing logic, unfortunately we aren't always at full parity. The following shows all supported features or planned features. If the checkbox is ticked, it means that the feature is supported. Otherwise, it is likely not supported yet or currently in planning or implementation stage. Please refer to an item's description for specific details.

Note that some features may only be on the latest alpha version. If a feature is listed as supported but not working on the latest tag, it most likely is in the alpha tag. If you can, please help us test the latest alpha changes and submit a bug report if you find any issues. Thank you!

Is there a feature that you want but is not yet supported? Please open a new issue to let us know!

Getting started

First, ensure you have Node.js 12+ installed on the deploying machine as all code is now transpiled to ES2019. Add your next application to the serverless.yml:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}" # it is recommended you pin the latest stable version of serverless-next.js

🚫 If you specify @sls-next/serverless-component in your serverless.yml file, do not add @sls-next/serverless-component to your package.json file, it is not used and only the version in serverless.yml file is used, which Serverless pulls from npm by itself. If you do not specify the version, it will use the latest tag, which refers to the latest stable version here (i.e not alpha versions).

You can also point it to a local installation, for example if you want to version using package.json.

In this case, configure the following:

# serverless.yml

myNextApplication:
  component: "./node_modules/@sls-next/serverless-component"

Then set your AWS credentials as environment variables:

AWS_ACCESS_KEY_ID=accesskey
AWS_SECRET_ACCESS_KEY=sshhh

And simply deploy:

$ serverless

If you have issues deploying due to new serverless version, please try to pin to specific version e.g 2.72.2. See #2320 (comment)

[ALPHA - may be buggy] You may also deploy using npx @sls-next/serverless-patched (or serverless-patched if you installed it locally), which is a patched version of serverless that fixes a couple of issues by patching the underlying @serverless/cli: (1) Continuous "Deploying" messages being printed in non-interactive terminals (e.g CI output) that make it hard to debug, and (2) Handles silent Next.js build failures.

It's also recommended to add --debug flag to get more useful logs of what's happening behind the scenes.

🚫 Don't attempt to deploy by running serverless deploy, use only serverless

Custom domain name

In most cases you wouldn't want to use CloudFront's distribution domain to access your application. Instead, you can specify a custom domain name.

You can use any domain name but you must be using AWS Route53 for your DNS hosting. To migrate DNS records from an existing domain follow the instructions here. The requirements to use a custom domain name:

  • Route53 must include a hosted zone for your domain (e.g. mydomain.com) with a set of nameservers.
  • You must update the nameservers listed with your domain name registrar (e.g. namecheap, godaddy, etc.) with those provided for your new hosted zone.

The serverless Next.js component will automatically generate an SSL certificate and create a new record to point to your CloudFront distribution.

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    domain: "example.com" # sub-domain defaults to www
    domainMinimumProtocolVersion: "TLSv1.2_2018" # can be omitted, defaults to "TLSv1.2_2018"

You can also configure a subdomain:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    domain: ["sub", "example.com"] # [ sub-domain, domain ]

Custom CloudFront configuration

To specify your own CloudFront inputs, just add any aws-cloudfront inputs under cloudfront:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    cloudfront:
      # if you want to use an existing cloudfront distribution, provide it here
      distributionId: XYZEXAMPLE #optional
      # this is the default cache behaviour of the cloudfront distribution
      # the origin-request edge lambda associated to this cache behaviour does the pages server side rendering
      defaults:
        forward:
          headers:
            [
              CloudFront-Is-Desktop-Viewer,
              CloudFront-Is-Mobile-Viewer,
              CloudFront-Is-Tablet-Viewer
            ]
      # this is the cache behaviour for next.js api pages
      api:
        minTTL: 10
        maxTTL: 10
        defaultTTL: 10
      # you can set other cache behaviours like "defaults" above that can handle server side rendering
      # but more specific for a subset of your next.js pages
      /blog/*:
        minTTL: 1000
        maxTTL: 1000
        defaultTTL: 1000
        forward:
          cookies: "all"
          queryString: false
      /about:
        minTTL: 3000
        maxTTL: 3000
        defaultTTL: 3000
      # you can add custom origins to the cloudfront distribution
      origins:
        - url: /static
          pathPatterns:
            /wp-content/*:
              minTTL: 10
              maxTTL: 10
              defaultTTL: 10
        - url: https://old-static.com
          pathPatterns:
            /old-static/*:
              minTTL: 10
              maxTTL: 10
              defaultTTL: 10
        - url: http://old-api.com
          protocolPolicy: http-only
          pathPatterns:
            /old-api/*:
              minTTL: 10
              maxTTL: 10
              defaultTTL: 10
      aliases: ["foo.example.com", "bar.example.com"]
      priceClass: "PriceClass_100"
      # You can add custom error responses
      errorPages:
        - code: 503
          path: "/503.html"
          minTTL: 5 # optional, minimum ttl the error is cached (default 10)
          responseCode: 500 # optional, alters the response code
      comment: "a comment" # optional, describes your distribution
      webACLId: "arn:aws:wafv2:us-east-1:123456789012:global/webacl/ExampleWebACL/473e64fd-f30b-4765-81a0-62ad96dd167a" # ARN of WAF
      restrictions:
        geoRestriction:
          restrictionType: "blacklist" # valid values are whitelist/blacklist/none. Set to "none" and omit items to disable restrictions
          items: ["AA"] # ISO 3166 alpha-2 country codes
      certificate:
        cloudFrontDefaultCertificate: false # specify false and one of IAM/ACM certificates, or specify true and omit IAM/ACM inputs for default certificate
        acmCertificateArn: "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
        iamCertificateId: "iam-certificate-id" # specify either ACM or IAM certificate, not both
        sslSupportMethod: "sni-only" # can be omitted, defaults to "sni-only"
        minimumProtocolVersion: "TLSv1.2_2019" # can be omitted, defaults to "TLSv1.2_2019"
      originAccessIdentityId: XYZEXAMPLE #optional
      paths: ["/*"] # which paths should be invalidated on deploy, default matches everything, empty array skips invalidation completely
      waitBeforeInvalidate: true # by default true, it waits for the CloudFront distribution to have completed before invalidating, to avoid possibly caching old page
      tags: # Add any tags you want
        tag1: val1
        tag2: val2

This is particularly useful for caching any of your Next.js pages at CloudFront's edge locations. See this for an example application with custom cache configuration. You can also update an existing cloudfront distribution using custom cloudfront inputs.

Static pages caching

Statically rendered pages (i.e. HTML pages that are uploaded to S3) have the following Cache-Control set:

cache-control: public, max-age=0, s-maxage=2678400, must-revalidate

s-maxage allows Cloudfront to cache the pages at the edge locations for 31 days. max-age=0 in combination with must-revalidate ensure browsers never cache the static pages. This allows Cloudfront to be in full control of caching TTLs. On every deployment an invalidation/* is created to ensure users get fresh content.

Public directory caching

By default, common image formats(gif|jpe?g|jp2|tiff|png|webp|bmp|svg|ico) under /public or /static folders have a one-year Cache-Control policy applied(public, max-age=31536000, must-revalidate). You may customize either the Cache-Control header value and the regex of which files to test, with publicDirectoryCache:

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    publicDirectoryCache:
      value: public, max-age=604800
      test: /\.(gif|jpe?g|png|txt|xml)$/i

value must be a valid Cache-Control policy and test must be a valid regex of the types of files you wish to test. If you don't want browsers to cache assets from the public directory, you can disable this:

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    publicDirectoryCache: false

AWS Permissions

By default the Lambda@Edge functions run using AWSLambdaBasicExecutionRole which only allows uploading logs to CloudWatch. If you need permissions beyond this, like for example access to DynamoDB or any other AWS resource you will need your own custom policy or role arn:

Specify policy:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    policy: "arn:aws:iam::123456789012:policy/MyCustomPolicy"

Specify role:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    roleArn: "arn:aws:iam::123456789012:role/MyCustomLambdaRole"

Make sure you add CloudWatch log permissions to your custom policy or role. Minimum policy JSON example:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Resource": "*",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ]
    },
    {
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::s3-deployment-bucket-name/*",
      "Action": ["s3:GetObject", "s3:PutObject"]
    }
  ]
}

Role should include trust relationship with lambda.amazonaws.com and edgelambda.amazonaws.com.

NOTE: Specify bucketName and give permissions to access that bucket via policy or roleArn so default and API lambdas can access static resources.

AWS Permissions for deployment

The exhaustive list of AWS actions required for a deployment:

  "acm:DescribeCertificate", // only for custom domains
  "acm:ListCertificates",    // only for custom domains
  "acm:RequestCertificate",  // only for custom domains
  "cloudfront:CreateCloudFrontOriginAccessIdentity",
  "cloudfront:CreateDistribution",
  "cloudfront:CreateInvalidation",
  "cloudfront:GetDistribution",
  "cloudfront:GetDistributionConfig",
  "cloudfront:ListCloudFrontOriginAccessIdentities",
  "cloudfront:ListDistributions",
  "cloudfront:ListDistributionsByLambdaFunction",
  "cloudfront:ListDistributionsByWebACLId",
  "cloudfront:ListFieldLevelEncryptionConfigs",
  "cloudfront:ListFieldLevelEncryptionProfiles",
  "cloudfront:ListInvalidations",
  "cloudfront:ListPublicKeys",
  "cloudfront:ListStreamingDistributions",
  "cloudfront:UpdateDistribution",
  "cloudfront:TagResource",         // for adding tags
  "cloudfront:UntagResource",       // for adding tags
  "cloudfront:ListTagsForResource", // for adding tags
  "iam:AttachRolePolicy",
  "iam:CreateRole",
  "iam:CreateServiceLinkedRole",
  "iam:GetRole",
  "iam:PutRolePolicy",
  "iam:PassRole",
  "lambda:CreateFunction",
  "lambda:EnableReplication",
  "lambda:DeleteFunction",            // only for custom domains
  "lambda:GetFunction",
  "lambda:GetFunctionConfiguration",
  "lambda:PublishVersion",
  "lambda:UpdateFunctionCode",
  "lambda:UpdateFunctionConfiguration",
  "lambda:ListTags",                  // for tagging lambdas
  "lambda:TagResource",               // for tagging lambdas
  "lambda:UntagResource",             // for tagging lambdas
  "route53:ChangeResourceRecordSets", // only for custom domains
  "route53:ListHostedZonesByName",
  "route53:ListResourceRecordSets",   // only for custom domains
  "s3:CreateBucket",
  "s3:GetAccelerateConfiguration",
  "s3:GetObject",                     // only if persisting state to S3 for CI/CD
  "s3:ListBucket",
  "s3:PutAccelerateConfiguration",
  "s3:PutBucketPolicy",
  "s3:PutObject",
  "s3:PutBucketTagging",              // for tagging buckets
  "s3:GetBucketTagging",              // for tagging buckets
  "lambda:ListEventSourceMappings",
  "lambda:CreateEventSourceMapping",
  "iam:UpdateAssumeRolePolicy",
  "iam:DeleteRolePolicy",
  "sqs:CreateQueue", // SQS permissions only needed if you use Incremental Static Regeneration. Corresponding SQS.SendMessage permission needed in the Lambda role
  "sqs:DeleteQueue",
  "sqs:GetQueueAttributes",
  "sqs:SetQueueAttributes"

Lambda At Edge Configuration

The default, api, and image (for Next.js Image Optimization) edge lambdas will be assigned 512mb of memory by default. This value can be altered by assigning a number to the memory input

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    memory: 1024

Values for default, api, and image lambdas can be separately defined by assigning memory to an object like so:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    memory:
      defaultLambda: 1024
      apiLambda: 2048
      imageLambda: 2048

The same pattern can be followed for specifying the Node.js runtime (nodejs14.x by default):

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    runtime:
      defaultLambda: "nodejs14.x"
      apiLambda: "nodejs14.x"
      imageLambda: "nodejs14.x" # Note that the sharp image library is built for Lambda Node.js 14.x, although it will likely work fine on other runtimes

Similarly, the timeout by default is 10 seconds. To customise you can:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    timeout:
      defaultLambda: 20
      apiLambda: 15
      imageLambda: 15

Note the maximum timeout allowed for Lambda@Edge is 30 seconds. See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html

You can also set a custom name for default, api, and image lambdas - if not the default is set by the aws-lambda serverless component to the resource id:

# serverless.yml

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    name:
      defaultLambda: fooDefaultLambda
      apiLambda: fooApiLambda
      imageLambda: fooImageLambda

There is a fourth regeneration lambda, which can be configured similarly and is used for Incremental Static Regeneration. However, it does not use Lamda@Edge and can, for example, have a longer timeout setting.

Architecture

architecture

Four Cache Behaviours are created in CloudFront.

The first two _next/* and static/* forward the requests to S3.

The third is associated to a lambda function which is responsible for handling three types of requests.

  1. Server side rendered page. Any page that defines getInitialProps method will be rendered at this level and the response is returned immediately to the user.

  2. Statically optimised page. Requests to pages that were pre-compiled by next to HTML are forwarded to S3.

  3. Public resources. Requests to root level resources like /robots.txt, /favicon.ico, /manifest.json, etc. These are forwarded to S3.

The reason why 2. and 3. have to go through Lambda@Edge first is because the routes don't conform to a pattern like _next/* or static/*. Also, one cache behaviour per route is a bad idea because CloudFront only allows 25 per distribution.

The fourth cache behaviour handles next API requests api/*.

Inputs

Name Type Default Value Description
deploy boolean true When set to false, it will not deploy the app to the provider (e.g AWS).
domain Array null For example ['admin', 'portal.com']
domainRedirects object {} Adds domain-level redirects at the edge using a 308 redirect. Specify an object of domain name -> redirect destination with protocol. For example, www.example.com: https://example.com. See here for more information.
bucketName string null Custom bucket name where static assets are stored. By default is autogenerated.
bucketRegion string null Region where you want to host your s3 bucket. Make sure this is geographically closer to the majority of your end users to reduce latency when CloudFront proxies a request to S3.
bucketTags object undefined Custom bucket tags to set for your bucket. If undefined, the component will not update any tags. If set to an empty object, it will remove all tags.
nextConfigDir string ./ Directory where your application next.config.js file is. This input is useful when the serverless.yml is not in the same directory as the next app.
Note: nextConfigDir should be set if next.config.js distDir is used
nextStaticDir string ./ If your static or public directory is not a direct child of nextConfigDir this is needed
description string *lambda-type*@Edge for Next CloudFront distribution The description that will be used for both lambdas. Note that "(API)" will be appended to the API lambda description.
policy string|object arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole The arn or inline policy that will be assigned to both lambdas.
roleArn string|object null The arn of role that will be assigned to both lambdas.
runtime string|object nodejs14.x When assigned a value, both the default and api lambdas will be assigned the runtime defined in the value. When assigned to an object, values for the default and api lambdas can be separately defined
memory number|object 512 When assigned a number, both the default and api lambdas will be assigned memory of that value. When assigned to an object, values for the default and api lambdas can be separately defined
tags object undefined Tags to assign to a Lambda. If undefined, the component will not update any tags. If set to an empty object, it will remove all tags.
timeout number|object 10 Same as above
handler string index.handler When assigned a value, overrides the default function handler to allow for configuration. Copies handler.js in route into the Lambda folders. Your handler MUST still call the default-handler afterwards or your function won't work with Next.JS
name object / Names for all lambdas can be explicitly defined
build boolean|object true When true builds and deploys app, when false assume the app has been built and uses the .next .serverless_nextjs directories in nextConfigDir to deploy. If an object is passed build allows for overriding what script gets called and with what arguments.
build.cmd string node_modules/.bin/next Build command, you may pass a no-op command (e.g true or : in Unix-based systems) which will then skip the Next.js build
build.args Array|string ['build'] Arguments to pass to the build
build.cwd string ./ Override the current working directory
build.enabled boolean true Same as passing build:false but from within the config
build.env object {} Add additional environment variables to the script
build.postBuildCommands Array [] Any commands to run post-build and pre-deploy. For example, you can run any custom code on the .serverless_nextjs directory e.g you can copy additional files into the Lambda: see #767 (comment) for an example for next-18n. Only applies during execution of the serverless command.
build.cleanupDotNext boolean true Whether to clean up .next directory before running the build step
build.assetIgnorePatterns string[] [] Glob patterns to ignore when discovering files to copy from _next/static, public, static directories.
build.useV2Handler boolean false Experimental Set this to true to use V2 handlers which starts to use genericized handlers. Note: this has the functionality of separateApiLambda and disableOriginResponseHandler so it should not be used together. Also, it is not completely optimized yet in terms of code size, but should still be performant. In the future, we will likely use this mode by default.
cloudfront object {} Inputs to be passed to aws-cloudfront
certificateArn string `` Specific certificate ARN to use for CloudFront distribution. Helpful if you have a wildcard SSL cert you wish to use. This currently works only in tandem with thedomaininput. Please check custom CloudFront configuration for how to specifycertificatewithout needing to use thedomaininput (note that doing so will override any certificate due to the domain input).
domainType string "both" Can be one of:"apex"- apex domain only, don't create a www subdomain."www" - www domain only, don't create an apex subdomain."both"- create both www and apex domains when either one is provided.
domainMinimumProtocolVersion string "TLSv1.2_2018" Can be one of: "SSLv3", "TLSv1", "TLSv1.1_2016", "TLSv1.2_2018", "TLSv1.2_2019", "TLSv1.2_2021" or "TLSv1_2016". See reference.
publicDirectoryCache boolean|object true Customize thepublic/staticfolder asset caching policy. Assigning an object withvalueand/ortestlets you customize the caching policy and the types of files being cached. Assigning false disables caching
useServerlessTraceTarget boolean false Use the experimental-serverless-trace target to build your next app. This is the same build target that Vercel Now uses. See this RFC for details. Note: while using this, you may need to setNODE_ENVvariable toproduction.
minifyHandlers boolean false Use pre-built minified handlers to reduce code size. Does not minify custom handlers.
enableHTTPCompression boolean false When set totruethe Lambda@Edge functions for SSR and API requests will use Gzip to compress the response. Note that you shouldn't need to enable this because CloudFront will compress responses for you out of the box.
authentication object undefined Authentication object for use with basic authentication (available from 1.19.0-alpha.3). It only supports a single username/password combination for now and is inlined in plaintext in the Lambda handler. You must also forward theAuthorizationheader for CloudFront behaviors, e.gdefaults, api/*, and \_next/data/\_. Note: this is meant as a simple means of protecting an environment such as a development/test site, it is not recommended for production use.
authentication.username string undefined Username for basic authentication.
enableS3Acceleration boolean true Whether to enable S3 transfer acceleration. This may be useful to disable as some AWS regions do not support it. See reference.
removeOldLambdaVersions boolean false Basic support for removing old Lambda versions after deploying to ensure. If set to true, every time you deploy it will automatically removes up to ~50 old versions (starting from oldest) of all Lambdas that are not deployed/replicated. If you require more complex strategies, it is recommended to write your own script to remove old versions.

Custom inputs can be configured like this:

myNextApp:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    bucketName: my-bucket

CDK Construct

(experimental) - more work required to bring this construct up to speed and also to reuse some of the serverless logic. As a result the construct is likely to adapt/change accordingly.

Documentation can be found here.

FAQ

How production ready is this?

As we are emulating Next.js routing logic nearly from scratch to optimize it for a serverless environment, there may be some incomplete or missing features (as mentioned earlier). However, we feel that we've covered the majority of features and have added good unit and end-to-end test coverage to ensure stability (e.g across 10+ end-to-end test suites). Several people are using this to power their startup, personal websites, etc.

Cloud provider limitations also apply - for example on AWS Lambda@Edge, there are cold starts, code size limits, 1 MB response size limit, etc to name a few. You are of course also tied to a single platform for now (AWS Lambda@Edge; more coming soon!).

We are also continuing to improve the deployment process by considering better infrastructure-as-code solutions in the near future (CDK, CDK Terraform, Pulumi, etc.).

My component doesn't deploy

Make sure your serverless.yml uses the serverless-components (beta) format. serverless components differ from the original serverless framework, even though they are both accessible via the same CLI.

Do

# serverless.yml
myNextApp:
  component: "@sls-next/serverless-component@{version_here}"

myTable:
  component: serverless/aws-dynamodb
  inputs:
    name: Customers
# other components

🚫 Don't

# serverless.yml
provider:
  name: aws
  runtime: nodejs10.x
  region: eu-west-1

myNextApp:
  component: "@sls-next/serverless-component@{version_here}"

Resources: ...

Note how the correct yaml doesn't declare a provider, Resources, etc.

For deploying, don't run serverless deploy. Simply run serverless and that deploys your components declared in the serverless.yml file.

For more information about serverless components go here.

The Lambda@Edge code size is too large

The API handler and default handler packages are deployed separately, but each has a limit of 50 MB zipped or 250 MB uncompressed per AWS - see here and here. By design, there is currently only one Lambda@Edge for all page routes and one Lambda@Edge for all API routes. This could lead to code size issues especially if you have many API routes, SSR pages, etc.

If you are encountering code size issues, please try the following:

  • Optimize your code size: reduce # dependencies in your SSR pages and API routes, have fewer SSR pages (i.e don't use getInitialProps() or getServerSideProps()).

  • Minify the handler code itself by using the minifyHandlers input. This will reduce handler size from ~500 kB to ~200 kB.

  • Minify/minimize your server-side code using Terser by adding the following Webpack configuration to your next.config.js. It uses NEXT_MINIMIZE environment variable to tell it to minimize the SSR code. Note that this will increase build times, and minify the code so it could be harder to debug CloudWatch errors.

First, add terser-webpack-plugin to your dependencies. Then update next.config.js:

const TerserPlugin = require("terser-webpack-plugin");
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
  if (isServer && !dev && process.env.NEXT_MINIMIZE === "true") {
    config.optimization = {
      minimize: true,
      minimizer: [
        new TerserPlugin({
          parallel: true,
          cache: true,
          terserOptions: {
            output: { comments: false },
            mangle: true,
            compress: true
          },
          extractComments: false
        })
      ]
    };
  }

  return config;
};

Note that if you do not use any API routes, all JS files used only for prerendering static pages are automatically removed from the bundle. However, if you use API routes, we do not remove them as they may be used for preview mode. There's no official/non-hacky way to identify and remove these JS files not used in preview mode even when API routes are used. But we can add a new input to manually exclude them, if needed.

  • Use the useServerlessTraceTarget option in serverless.yml. This will cause Next.js to not bundle dependencies into each page (instead creating lightweight pages) and then serverless-next.js will reference a single set of dependencies in node_modules.

Serverless deployment takes a long time and times out with a message like "TimeoutError: Connection timed out after 120000ms"

This is likely either because of a Lambda@Edge code size issue (see above for potential solutions. Related GitHub Issue) or if you have a slow network upload speed and/or are trying to deploy a large Lambda package.

In the second case, the aws-sdk npm package used has a default timeout of 120 seconds. Right now this is not configurable, but we may support longer timeouts in the near future (similar to serverless/serverless#937, which only applies to Serverless Framework, not Serverless Components).

When accessing the Host header in my SSR pages or APIs, I get an S3 domain instead of the CloudFront distribution or my domain name

By default, CloudFront sets the Host header to the S3 origin host name. You need to forward the Host header to the origin. See the example below for forwarding it for your api/* cache behavior:

myNextApplication:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    cloudfront:
      api/*:
        forward:
          headers: [Host]

Should I use the serverless-plugin or this component?

Users are encouraged to use this component instead of the serverless-plugin. This component was built and designed using lessons learned from the serverless plugin.

How do I interact with other AWS Services within my app?

See examples/dynamodb-crud for an example Todo application that interacts with DynamoDB. You can find a full list of examples here

[CI/CD] Multi-stage deployments / A new CloudFront distribution is created on every CI build. I wasn't expecting that

Unfortunately, because of the way Serverless Components works (at least the beta version), the deployment state needs to be synced outside of the main serverless command. So you can try the following solutions:

  1. You need to commit your application state in source control. That is the files under the .serverless directory. Although this is not recommended as it doesn't work well for multiple stages.
  2. Alternatively you could use S3 to store the .serverless files, see an example here, here (uses multiple serverless.yml files), or here (GitHub Actions-based, uses multiple serverless.yml files).
  3. You can also use the distributionId CloudFront input to specify an existing CloudFront distribution to deploy to.

In the future, we will look to improve this by integrating proper stage management into the component itself.

Why is this still using Serverless Components Beta? Is there a plan to upgrade to GA?

This project was started by the original author when Serverless Components was in beta, and unfortunately GA components was released shortly afterwards. But this grew larger and larger, with several sub-components imported into this monorepo as they weren't maintained by Serverless. And then it became difficult to upgrade.

There was a plan to upgrade to GA components, but it was put on hold for a few reasons:

  • Since there is only one active maintainer, and it's been hard enough keeping up with Next.js parity and fixing bugs
  • Upon analysis of Serverless Components GA, it seems like there may be more drawbacks than benefits: now your code/temporary credentials might have to be built on Serverless infra and thus risks vendor lock-in (whereas beta components doesn't - it primarily provided reusable components but everything happened locally). In addition, it's not as configurable and robust as proper infrastructure-as-code (IaC) solutions, and a lot of components (especially non-AWS resources) are not well maintained. Finally, the current deployment logic is quite fragile and custom-written and requires lots of maintenance to keep up with new cloud provider features.

We are currently looking into proper IaC solutions (such as CDK for Terraform, CDK, Pulumi, etc.) to address this and to ease the burden of maintaining complex deployment logic, so that we can focus on the developer experience and feature parity with Next.js.

Are there plans to expand to other platforms?

Yes! The main blocker was that the Next.js routing logic used to be highly coupled with Lambda@Edge/CloudFront logic. However, we have genericized most of the core logic (into the @sls-next/core package) so that it can be reused in other platforms, simply by creating a wrapping handler, implementing some platform-specific client (e.g to retrieve pages, trigger static regeneration, etc.), and creating a deployer. If you were observant, you'll have noticed a new package currently in the works for Lambda deployments via API Gateway: https://github.com/serverless-nextjs/serverless-next.js/tree/master/packages/libs/lambda. Other platforms like Azure and Google Cloud should hopefully follow soon.

My lambda is deployed to us-east-1. How can I deploy it to another region?

Serverless Next.js is regionless. By design, serverless-next.js applications will be deployed across the globe to every CloudFront edge location. The lambda might look like is only deployed to us-east-1 but behind the scenes, it is replicated to every other region.

I require passing additional information into my build

See the sample below for an advanced build setup that includes passing additional arguments and environment variables to the build.

# serverless.yml
myDatabase:
  component: MY_DATABASE_COMPONENT
myNextApp:
  component: "@sls-next/serverless-component@{version_here}"
  inputs:
    build:
      args: ["build", "custom/path/to/pages"]
      env:
        DATABASE_URL: ${myDatabase.databaseUrl}

Concatenating environment variables doesn't seem to work

If you try to concatenate an environment variable with another string, like ${env.VARIABLE}-blah, Serverless framework seems to resolve it to only ${env.VARIABLE}.

It seems to be a bug in Serverless Components - it may be due to not using the latest GA version, where it might have been fixed (this is still on Components Beta, unfortunately). For now, the workaround is:

  1. Don't concatenate but specify only environment variable, though this means duplicating environment variables.
  2. Follow #530 (comment) and set a serverless.yml variable first, then concatenate:
stage: ${env.STAGE}
my-app:
  component: "@sls-next/[email protected]"
  inputs:
    domain:
      - "${stage}-front-end"
      - mydomain.com

I was expecting for automatic subdomain redirection when using the domainType: www/apex input

You can use the new domainRedirects input, along with forwarding Host header and domainType: both, to redirect requests to the correct domain. See example configuration below that redirects www.example.com requests to https://example.com.

next-app:
  component: "../../serverless-components/nextjs-component"
  inputs:
    cloudfront:
      defaults:
        forward:
          headers: [Host]
    domain: ["example.com"]
    domainType: "both"
    domainRedirects:
      www.example.com: https://example.com

All of this happens within the Lambda@Edge origin request handlers. Please note that this will not allow you to redirect requests at _next/static/* or /static/*, since those cache behaviors do not have a Lambda@Edge handler attached to them.

Otherwise, you can also use the manual workaround using an S3 bucket outlined here. In summary, you will have to create a new S3 bucket and set it up with static website hosting to redirect requests to your supported subdomain type (ex. "www.example.com" or "example.com"). To be able to support HTTPS redirects, you'll need to set up a CloudFront distribution with the S3 redirect bucket as the origin. Finally, you'll need to create an "A" record in Route 53 with your newly created CloudFront distribution as the alias target.

My environment variables set in build.env don't show up in my app

To allow your app to access the defined environment variables, you need to expose them via the next.config.js as outlined here.

Given a serverless.yml like this

myApp:
  inputs:
    build:
      env:
        API_HOST: "http://example.com"

your next.config.js should look like that:

module.exports = {
  env: {
    API_HOST: process.env.API_HOST
  }
};

502 Error when hitting CloudFront distribution

You may see an error like below:

502 ERROR
The request could not be satisfied.
The Lambda function returned invalid JSON: The JSON output is not parsable. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Generated by cloudfront (CloudFront)

It commonly occurs when the size of the response is too large. Lambda@Edge currently does have a limitation of 1 MB returned by the origin request handler. See: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-generating-http-responses-in-requests.html#lambda-generating-http-responses-errors. Unfortunately, there may not be a workaround other than trying to ensure your responses are smaller.

Automatic locale detection

Ensure you forward Accept-Language header via CloudFront configuration, otherewise it is not able to determine which languages the user understands and/or prefers. By default it is forwarded but if you override with your own configuration, you should add it back.

Third party integrations

If you are using third party libraries (only next-i18next for now) and use default paths, some files may need to be copied to the Lambda directories. This component will try to detect default files and copy them for you. However, if you have custom config, you may need to write your own postBuildCommands to copy files, etc.

Refer to the README for more information and caveats.

Reporting Issues

You can open a new issue here. If posting a problem, please follow the debugging wiki first for some useful tips, and try to include as much information to reproduce the issue.

If you are reporting a security vulnerability, please follow the security policy instead.

Please note that there is only one core maintainer right now (@dphang), and a handful of community contributors, who all contribute to this library in their free time. So we hope you understand that our response is best-effort and may take several days, or more. So we hope you could at least help debug the issue or provide as much context. In case an issue hasn't been looked at for a long time (> a few weeks) or it seems like a major issue, feel free to mention a maintainer and we will try to prioritize it.

Contributing

We would love if you can help contribute, whether it's coding, triaging or debugging issues, helping with documentation, or financial support! Please see the contributing guide.

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Made with contributors-img.

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

serverless-next.js's People

Contributors

a14m avatar andrewgadziksonos avatar ari-luokkala avatar barrysteyn avatar cmaerz avatar danielcondemarin avatar deadleg avatar dependabot[bot] avatar dphang avatar dpowell avatar gabrola avatar iiroj avatar ingoclaro avatar j3tto avatar jlaramie avatar josephluck avatar jvarho avatar kensuketakahara avatar kirkness avatar lone-cloud avatar mekwall avatar mertyildiran avatar peniakoff avatar qubica avatar renovate[bot] avatar sarkurd avatar simonireilly avatar stan-sack avatar thchia avatar thiagozf 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  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

serverless-next.js's Issues

Google Cloud deployments

Opening this issue to gauge how much interest there is in supporting google in the plugin. More details will be added if / when development is started.

Example should be target: severless

Next Config should be target:serverless no?

Also, npm run dev doesn't work really right now. May want to remove that script for now to climate confusion. However... What are you doing for a dev server? Right now, I'm maintaining two different servers essentially.

  1. Express for local dev
  2. lamba/severless for anything but loca.

ENOENT: no such file or directory, open 'sls-next-build/_error.compat.js'

I get the following error when I try use this plugin: ENOENT: no such file or directory, open 'sls-next-build/_error.compat.js'

Here is a stack trace:

Error: ENOENT: no such file or directory, open 'sls-next-build/_error.compat.js'
    at PluginManager.invoke (/.config/yarn/global/node_modules/serverless/lib/classes/PluginManager.js:408:22)
    at PluginManager.spawn (/.config/yarn/global/node_modules/serverless/lib/classes/PluginManager.js:426:17)
    at Deploy.BbPromise.bind.then (/.config/yarn/global/node_modules/serverless/lib/plugins/deploy/deploy.js:117:50)
From previous event:
    at Object.before:deploy:deploy [as hook] (/.config/yarn/global/node_modules/serverless/lib/plugins/deploy/deploy.js:107:10)
    at BbPromise.reduce (/.config/yarn/global/node_modules/serverless/lib/classes/PluginManager.js:408:55)
From previous event:
    at PluginManager.invoke (/.config/yarn/global/node_modules/serverless/lib/classes/PluginManager.js:408:22)
    at PluginManager.run (/.config/yarn/global/node_modules/serverless/lib/classes/PluginManager.js:439:17)
    at variables.populateService.then (/.config/yarn/global/node_modules/serverless/lib/Serverless.js:106:33)
    at runCallback (timers.js:705:18)
    at tryOnImmediate (timers.js:676:5)
    at processImmediate (timers.js:658:5)
    at process.topLevelDomainCallback (domain.js:121:23)
From previous event:
    at Serverless.run (/.config/yarn/global/node_modules/serverless/lib/Serverless.js:93:6)
    at serverless.init.then (/.config/yarn/global/node_modules/serverless/bin/serverless:43:28)
    at /.config/yarn/global/node_modules/graceful-fs/graceful-fs.js:99:16
    at /.config/yarn/global/node_modules/graceful-fs/graceful-fs.js:43:10
    at FSReqWrap.oncomplete (fs.js:141:20)
From previous event:
    at initializeErrorReporter.then (/.config/yarn/global/node_modules/serverless/bin/serverless:43:6)
    at runCallback (timers.js:705:18)
    at tryOnImmediate (timers.js:676:5)
    at processImmediate (timers.js:658:5)
    at process.topLevelDomainCallback (domain.js:121:23)
From previous event:
    at /.config/yarn/global/node_modules/serverless/bin/serverless:28:46
    at Object.<anonymous> (/.config/yarn/global/node_modules/serverless/bin/serverless:67:4)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)

make nextConfigDir optional

I'd propose to use ./ as default for nextConfigDir and make the plugin work ootb without providing any configuration (unless you need it).

Cannot use deploymentBucket in serverless.yaml

I want to keep my serverless deployments in one bucket, as supported by the serverless.yaml docs

When I add

  deploymentBucket:
    name: myBucketName

I get the following error and stack trace:

Type Error ---------------------------------------------
 
  Cannot read property 'Resources' of undefined
 
     For debugging logs, run again after setting the "SLS_DEBUG=*" environment variable.
 
  Stack Trace --------------------------------------------
 
TypeError: Cannot read property 'Resources' of undefined
    at addS3BucketToResources (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless-nextjs-plugin/lib/addS3BucketToResources.js:24:6)
From previous event:
    at PluginManager.invoke (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/classes/PluginManager.js:408:22)
    at PluginManager.spawn (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/classes/PluginManager.js:426:17)
    at Deploy.BbPromise.bind.then (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/plugins/deploy/deploy.js:117:50)
From previous event:
    at Object.before:deploy:deploy [as hook] (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/plugins/deploy/deploy.js:107:10)
    at BbPromise.reduce (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/classes/PluginManager.js:408:55)
From previous event:
    at PluginManager.invoke (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/classes/PluginManager.js:408:22)
    at PluginManager.run (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/classes/PluginManager.js:439:17)
    at variables.populateService.then (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/Serverless.js:109:33)
    at processImmediate (timers.js:638:19)
    at process.topLevelDomainCallback (domain.js:136:23)
From previous event:
    at Serverless.run (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/lib/Serverless.js:96:6)
    at serverless.init.then (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/bin/serverless:43:28)
    at /Users/me/Work/Repos/myApp.front-end/node_modules/graceful-fs/graceful-fs.js:111:16
    at /Users/me/Work/Repos/myApp.front-end/node_modules/graceful-fs/graceful-fs.js:45:10
    at FSReqCallback.args [as oncomplete] (fs.js:145:20)
From previous event:
    at initializeErrorReporter.then (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/bin/serverless:43:6)
    at processImmediate (timers.js:638:19)
    at process.topLevelDomainCallback (domain.js:136:23)
From previous event:
    at /Users/me/Work/Repos/myApp.front-end/node_modules/serverless/bin/serverless:28:46
    at Object.<anonymous> (/Users/me/Work/Repos/myApp.front-end/node_modules/serverless/bin/serverless:67:4)
    at Module._compile (internal/modules/cjs/loader.js:799:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:810:10)
    at Module.load (internal/modules/cjs/loader.js:666:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:606:12)
    at Function.Module._load (internal/modules/cjs/loader.js:598:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:862:12)
    at internal/main/run_main_module.js:21:11

The unhappy code lives in addS3BucketToResources.js: in serverless-nextjs-plugin/lib.

It's not clear to me what's happening, but somehow cf becomes undefined on this line:

cf.Resources.NextStaticAssetsS3Bucket.Properties.BucketName = bucketName;

I'd appreciate any insight, advice, or solutions!

Not loading /_next path

I got this integrating onto my site, but have replicated it in the example app.

Steps to reproduce

Using Serverless v1.40.0

  • Clone this repo
  • $ cd examples/basic-next-serverless-app/
  • $ yarn
  • $ npx serverless offline
  • See lots of messages in the console such as Serverless: GET /_next/on-demand-entries-ping (λ: notFoundErrorPage)

Screenshot 2019-04-03 at 12 50 25

And lots of 403 status codes in the network tab for anything on the /_next path.

Screenshot 2019-04-03 at 12 53 34

I'm not sure what to do, but this means none of my JS or CSS will load.

static `getInitialProps` triggers page flash

When debugging locally, using getInitialProps to provide initial props to a page, the client side app seems to not get anything out of it. When reloading a page, the desired props will be passed to render, then shortly after the page will render as if no props were set.

A console error will be output in the browser : "Warning: Text content did not match."

Configure multiple URLs to be served by the same page (file)

Hi, I am new to this plugin and to the serverless ecosystem, so maybe this is not the best place to ask this question.

My use case is that I have a page that serves different URLs. Let's call the page content.js which is the real file. How can I setup serverless.yml in order to handle different URLs? For example

  • blog/{url} => rendered by content.js
  • meetings/{url} => rendered by content.js
  • info/{url} => rendered by content.js

Amazing plugin btw!!! Thanks!

Number or resources > 200

Finally deploying my project en AWS, I've got this :

The CloudFormation template is invalid: Template format error: Number of resources, 226, is greater than maximum allowed, 200

I dont know this error. FWI, I've got 37 pages.

Local development / interoperability with now.json routes?

Wondering if it would be possible to generate Serverless routes from those in now.json or vice-versa?

Related, do you know how I could map /a and /b to the same page handler (but not /c)?

In now.json:

{
  "version": 2,
  "builds": [{ "src": "next.config.js", "use": "@now/next" }],
  "routes": [
    {
      "src": "/(a|b)",
      "dest": "/page?type=$1"
    },
    {
      "src": "/c",
      "dest": "/another?type=$1"
    }
  ]
}

I.e. looking to achieve the same within the plugin config.

Maybe this is a Serverless question but struggling to find answers from their documentation.

Update example next.config.js

example have to get serverless target

const withCSS = require("@zeit/next-css");
module.exports = withCSS({
  target: "serverless",
  assetPrefix: "https://s3.amazonaws.com/{myBucket}"
});

here is error stack

Error: Serverless Nextjs: Target 'server' is invalid. Set 'serverless' as the target
    at createError (/Users/dj/project/nextjs/serverless-nextjs-plugin/examples/basic-next-serverless-app/node_modules/serverless-nextjs-plugin/utils/createError.js:2:10)
    at module.exports.nextConfigDir (/Users/dj/project/nextjs/serverless-nextjs-plugin/examples/basic-next-serverless-app/node_modules/serverless-nextjs-plugin/lib/parseNextConfiguration.js:20:11)
    at ServerlessNextJsPlugin.get configuration [as configuration] (/Users/dj/project/nextjs/serverless-nextjs-plugin/examples/basic-next-serverless-app/node_modules/serverless-nextjs-plugin/index.js:47:12)
    at nextBuild.then (/Users/dj/project/nextjs/serverless-nextjs-plugin/examples/basic-next-serverless-app/node_modules/serverless-nextjs-plugin/index.js:57:44)
From previous event:
    at PluginManager.invoke (/Users/dj/n/lib/node_modules/serverless/lib/classes/PluginManager.js:407:22)
    at PluginManager.spawn (/Users/dj/n/lib/node_modules/serverless/lib/classes/PluginManager.js:425:17)
    at Deploy.BbPromise.bind.then (/Users/dj/n/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:117:50)
From previous event:
    at Object.before:deploy:deploy [as hook] (/Users/dj/n/lib/node_modules/serverless/lib/plugins/deploy/deploy.js:107:10)
    at BbPromise.reduce (/Users/dj/n/lib/node_modules/serverless/lib/classes/PluginManager.js:407:55)
From previous event:
    at PluginManager.invoke (/Users/dj/n/lib/node_modules/serverless/lib/classes/PluginManager.js:407:22)
    at PluginManager.run (/Users/dj/n/lib/node_modules/serverless/lib/classes/PluginManager.js:438:17)
    at variables.populateService.then.then (/Users/dj/n/lib/node_modules/serverless/lib/Serverless.js:114:33)
    at processImmediate (timers.js:637:19)
    at process.topLevelDomainCallback (domain.js:126:23)
From previous event:
    at Serverless.run (/Users/dj/n/lib/node_modules/serverless/lib/Serverless.js:101:6)
    at serverless.init.then (/Users/dj/n/lib/node_modules/serverless/bin/serverless:43:28)
    at /Users/dj/n/lib/node_modules/serverless/node_modules/graceful-fs/graceful-fs.js:111:16
    at /Users/dj/n/lib/node_modules/serverless/node_modules/graceful-fs/graceful-fs.js:45:10
    at FSReqCallback.args [as oncomplete] (fs.js:145:20)
From previous event:
    at initializeErrorReporter.then (/Users/dj/n/lib/node_modules/serverless/bin/serverless:43:6)
    at processImmediate (timers.js:637:19)
    at process.topLevelDomainCallback (domain.js:126:23)
From previous event:
    at /Users/dj/n/lib/node_modules/serverless/bin/serverless:28:46
    at Object.<anonymous> (/Users/dj/n/lib/node_modules/serverless/bin/serverless:67:4)
    at Module._compile (internal/modules/cjs/loader.js:738:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:749:10)
    at Module.load (internal/modules/cjs/loader.js:630:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:570:12)
    at Function.Module._load (internal/modules/cjs/loader.js:562:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:801:12)
    at internal/main/run_main_module.js:21:11

Hosting static assets : using custom bucket

I'm using this :

plugins:
  - serverless-nextjs-plugin

custom:
  serverless-nextjs:
    nextConfigDir: ./
    assetsBucketName: assets.cuistotducoin.com

with (in next.config.js)

  assetPrefix: 'https://assets.cuistotducoin.com',

Looking at the bucket on AWS, its empty ! I deleted the stack and let serverless re-create it, but no change.

You can look the repo here : https://github.com/CuistotduCoin/front

Console SLS :

Serverless Nextjs: assets bucket secure URL: https://assets.cuistotducoin.com.s3.amazonaws.com
Serverless Nextjs: assets bucket website URL: http://assets.cuistotducoin.com.s3-website-eu-west-1.amazonaws.com

Development experience for app that uses API Gateway authorisation?

My question is mainly centred around the development experience of building a serverless app using the Serverless Framework and Next.JS 8 for AWS, but with API Gateway authorisation.

Assume I'm building a page to login and one that requires you to be logged in:

pages/login.js
pages/profile.js // requires log in

So when running Serverless with https://github.com/dherault/serverless-offline I can replicate API Gateway locally, including features such as having an authorizer allowing me to deny access to the profile page, and custom gateway responses to customise the UnAuthorised experience. No code necessary in the lambda.

serverless-offline will spin up a server in development that replicates my API Gateway configuration.

However...

If my authorisation logic is in API Gateway config for Serverless, and not within the Next.js app itself, then surely that means the developer experience running the Next.js server won't apply the any authorisation right?

Therefore what is the best developer experience setup to an app that has these requirements?

If Next.js only builds the lambdas at build time, how can I wire them up to the serverless server in development? Ideally I should be using the serverless server, and not Next.js's, in development because it'll have the authorisation and will be closer to what my production setup will be when deployed... right?

Any help on this would be appreciated as I'm really keen to start using Next.js 8 with serverless!

Build CLI then deploy

When running serverless deploy all your next pages will be automatically compiled, packaged and deployed.

I'm trying to use your plugin with my current project : https://github.com/CuistotduCoin/front
I'm using Typescript, and I need to build before deploying. Can you divide your current deploy phase into two phases : build (like next build but custom) then deploy ?

Alternative : (three steps)

  • Use custom build from next (With requirements for 2nd step (dir, ect))
  • Rewrite Handlers
  • Deploy

Thanks for the work.

EDIT : I just saw sls package. I didnt knew that.

feat: gzip s3

Hi!
Would a PR be merged which compresses some assets by mime type (*/text, css, svg) before uploading to s3? The upload config then has two options: The encoding- and the content-type-header. Thats all. To configure this, I suggest a flag in serverless.yaml with a bool to enable this (what should be the default?) and a list to override the mime types.

What do you think?

Build time for CircleCI

I'm using Circle CI for deploying my site.
Right now, "serverless package" is very long compared to "next build".

With config.optimization.minimize = false on next.config.js (to minimize next part)
Next build : Done in 37.92s.
serverless package : Done in 761.15s.

Its maybe related to #11

Files in buckets on Windows

For local testing, I'm using windows.
The files in the bucket for the assets seems broken :
image

I will try with CircleCI, but it seems that Windows env. doesnt like the filenames.

NextStaticAssetsS3Bucket - assets already exists.

Error in Circle CI :

erverless: Creating Stack...
Serverless: Checking Stack create progress...
......
Serverless: Operation failed!
Serverless: View the full error output: https://eu-west-1.console.aws.amazon.com/cloudformation/home?region=eu-west-1#/stack/detail?stackId=arn%3Aaws%3Acloudformation%3Aeu-west-1%3A899832958734%3Astack%2Fserverless-ssr-production%2F403c64d0-4afa-11e9-aece-061c3112010a
 
  Serverless Error ---------------------------------------
 
  An error occurred: NextStaticAssetsS3Bucket - assets already exists.
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com
 
  Your Environment Information -----------------------------
     OS:                     linux
     Node Version:           8.10.0
     Serverless Version:     1.39.1

image

I deleted the stack, and the bucket doesnt exist before deployment.

Allow pages names to have a length greater than 64.

Hi,
Thanks again for this super cool library.
Currently when I create a page with the name /co2-emissions-per-capita it maps to a Lambda function with the name : tsp-data-portal-v2-client-production-co2-emissions-per-capitaPage which is longer than 64 characters so sls won't upload it :

An error occurred: Co2DashemissionsDashperDashcapitaPageLambdaFunction - 1 validation error detected: Value 'tsp-data-portal-v2-client-production-co2-emissions-per-capitaPage' at 'functionName' failed to satisfy constraint: Member must have length less than or equal to 64 (Service: AWSLambdaInternal; Status Code: 400; Error Code: InvalidParameterValueException; Request ID: 96b996e3-6ce0-11e9-bdb8-e754b5f7535f).

For the moment I can just rename my service with a shorter name so that it leaves space for page naming.
Maybe in the future, it will create problems for others.

The solution I see is creating a hash of the page name but it won't be human readable anymore...

Cheers

Is it possible to have other handlers/functions as well?

Is it possible to serve nextjs as it's layed out in the example, but to have additional handlers, say nested under /api/ url structure so that I can have my entire app + api live in one project?

I am new to this, but I expected something like this to work:

serverless.yml

service: basic-next-serverless-app

provider:
  name: aws
  runtime: nodejs8.10
  region: us-west-2

plugins:
  - serverless-nextjs-plugin
  - serverless-offline

custom:
  serverless-nextjs:
    nextConfigDir: "./"

package:
  exclude:
    - ./**

functions:
  app:
    handler: index.handler
    events:
      - http: 'ANY /api/'
      - http: 'ANY /api/{proxy+}'

index.js

const _ = require('lodash');
const serverless = require('serverless-http');
const express = require('express');

app.get('/api/a-thing', async (req, res) => {
  res.json({ hey: 1 });
});

const serverlessApp = serverless(app);

export const handler = async (event, context) => {
  return await serverlessApp(event, context);
};

But instead I just get my react 404 page.

Serverless deploy fails due to an existing s3 bucket

After the initial deploy, subsequent deploys fail since the assets S3 bucket already exists.

Is there any extra configuration required in my Serverless .yml file to make Cloud Formation aware of the bucket state?

Azure deployments

Opening this issue to gauge how much interest there is in supporting azure in the plugin. More details will be added if / when development is started.

Deployment issues with s3 buckets

Trying to deploy to a s3 bucket that doesn't exist yet

next config

 config.target = 'serverless'
    config.assetPrefix = `https://serverless-cowsay-v3-${stage}.s3-us-east-1.amazonaws.com`
    console.log(config.assetPrefix)
    return config

It reports that it found a bucket

https://serverless-cowsay-v3-dev.s3-us-east-1.amazonaws.com
Serverless Nextjs: Found bucket "serverless-cowsay-v3-dev"

Then it errors out because the bucket does not exist. No bucket is created in s3

Serverless: Uploading service cowsayify.zip file to S3 (4.09 MB)...
Serverless: [AWS s3 200 0.938s 0 retries] putObject({ Body: <Buffer 50 4b 03 04 14 00 08 00 08 00 00 00 21 00 00 00 00 00 00 00 00 00 00 00 00 00 18 00 00 00 73 6c 73 2d 6e 65 78 74 2d 62 75 69 6c 64 2f 5f 65 72 72 6f ... >,
  Bucket: 'cowsayify-dev-serverlessdeploymentbucket-f5qg4i30jpdt',
  Key: 'serverless/cowsayify/dev/1555102192807-2019-04-12T20:49:52.807Z/cowsayify.zip',
  ContentType: 'application/zip',
  Metadata: { filesha256: 'chsg3nZggrgUZUClb2nQ40pe7GDK+L7juFnjj5VHQVY=' } })
https://serverless-cowsay-v3-dev.s3-us-east-1.amazonaws.com
Serverless Nextjs: Uploading static assets to serverless-cowsay-v3-dev ...
Serverless: [AWS s3 404 0.244s 0 retries] putObject({ Body: <Buffer 7b 22 76 65 72 73 69 6f 6e 22 3a 33 2c 22 66 69 6c 65 22 3a 22 73 74 61 74 69 63 2f 63 68 75 6e 6b 73 2f 30 2e 6a 73 22 2c 22 73 6f 75 72 63 65 73 22 ... >,
  ACL: 'public-read',
  Bucket: 'serverless-cowsay-v3-dev',
  Key: '_next/static/chunks/0.js.map',
  ContentType: 'application/json' })
Serverless: [AWS s3 404 0.253s 0 retries] putObject({ Body: <Buffer 28 77 69 6e 64 6f 77 5b 22 77 65 62 70 61 63 6b 4a 73 6f 6e 70 22 5d 20 3d 20 77 69 6e 64 6f 77 5b 22 77 65 62 70 61 63 6b 4a 73 6f 6e 70 22 5d 20 7c ... >,
  ACL: 'public-read',
  Bucket: 'serverless-cowsay-v3-dev',
  Key: '_next/static/chunks/0.js',
  ContentType: 'application/javascript' })
Serverless Nextjs: ServerlessError: The specified bucket does not exist

If I create the bucket first, it complains that the bucket already exists!

Questions - Developer Experience with this plugin

I have a few questions regarding the stability and production-ready usage of this plugin. I'm concerned overall about the Developer eXperience, basically the ability to debug, develop locally, how does the local env matches the production env, but also about performances and cold starts.

I've been using Next since v5, and I'm currently running v5 in production, powered by Express, which powers Next.js. I've handled cold start by using AWS Heath Checks and calling a dedicated route to warm the whole thing. Cold start take around 3-6sec, depending on the allocated RAM.

The new "serverless" approach is fundamentally different, as each page lives in its own lambda, and therefore warming all the endpoints gets complicated. But maybe it's not needed anymore because each page may render much faster than my 3-6 sec using v5. (I know Next.js has had multiple performances improvements since v5, but don't know much about it)

With my current app, I also have endpoints that aren't next-related and are handled by Express (basically, an API). I guess the new correct approach would be to have a dedicated lambda to handle those. I see two approaches:

  1. I could either have an /api endpoint powering and express server as I did before (but not related to next this time, as it'd live in another lambda)
  2. I could also use one lambda per API endpoint, but don't quite see the benefits of doing that

Also, I'm wondering how does HMR work when working locally. My current Next 5 setup is tricky. I basically have the serverless-offline that lives on :3000, without HMR. And I have the Next app that lives on :3001, with HMR. I basically dev on 3000/3001 based on whether I do front-end or back-end stuff, but that's far from being perfect and I'm really looking forward to improve that.

I guess those points are my main concerns about switching/upgrading. I'd appreciate any feedback regarding those :)

And, thanks for this plugin, that's a really great idea @danielcondemarin ! I believe it'll help bring Next to another level, because let's be honest: AWS Lambda is so cost-effective and Next is such a great React framework, it really makes sens to put those two together using Serverless and it's a wonderful way to have a ready-to-deploy app in a matter of minutes.

Question: How can I use serverless variables inside nextconfig

For example, I want to name the assetPrefix with my stage variable:

// next.config.js
const stage= // How would I set this?
module.exports = {
  assetPrefix: `https://s3.amazonaws.com/your-bucket-name_${stage}`
};

The question is: How would I import the stage variable into my next config

S3 Upload does not work

In my next.js.config I have

module.exports = withSASS({
    ...
    assetPrefix: 'https://s3.amazonaws.com/staging.app.de',
    ...
});

But on deployment, the bucket cannot be created:

Serverless Nextjs: Found bucket "staging.app.de" in assetPrefix! Will provision!
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service app.zip file to S3 (921.87 KB)...
Serverless Nextjs: Uploading static assets to staging.app.de ...
 
  Serverless Error ---------------------------------------
 
  Access Denied
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com
 
  Your Environment Information -----------------------------
     OS:                     darwin
     Node Version:           8.10.0
     Serverless Version:     1.38.0

I tried some combinations: Without preexisting bucket, after creating it manually and after setting it to public. Any ideas?

EDIT:
My serverless.yml has the following plugins:

plugins:
  - serverless-dynamodb-local
  - serverless-domain-manager
  - serverless-apigw-binary
  - serverless-apigwy-binary
  - serverless-nextjs-plugin

Custom handler isn't build

When you add a custom handler like this :

const compat = require("serverless-nextjs-plugin/aws-lambda-compat");
const Raven = require("raven");
const RavenLambdaWrapper = require("serverless-sentry-lib")

module.exports = page => {
    return RavenLambdaWrapper.handler(Raven, (event, context, callback) => {
        compat(page)(event, context, callback);
    })
};

After build phase, the handler isnt transform as pages are. I think this should be the custom behavior.

Alternative : add the custom packages in the final artifact, but I think first option is the best.

Feature: Improved page and static resources Routing

Motivation

Currently there are no routing capabilities for static assets. For example, you can't have /robots.txt, /browserconfig.xml or maybe a service worker /sw.js.
Also, although the custom routing for pages works and is flexible it can be quite verbose to declare:

custom:
  serverless-nextjs:
    nextConfigDir: ./
    pageConfig:
      post:
        events:
          - http:
              path: post/{slug}
              request:
                parameters:
                  paths:
                    slug: true 

That is just for a custom post page route: post/{slug}.

Proposal

1 - Upload static/ directory contents to S3 assets bucket

The concept of a static folder is familiar already to next developers. Is an easy way to get static content available to the application. The plugin already manages an S3 bucket where the next build assets are uploaded, so the static/ dir contents will also be uploaded to the same bucket.

2 - Add support for a new routes plugin configuration array:

# serverless.yml
custom:
  serverless-nextjs:
    nextConfigDir: ./
    routes:
       -
          # post page routing
          # https://123.execute-api.us-east-1.amazonaws.com/dev/post/123
          src: pages/post.js
          path: post/{slug}
          request:
            parameters:
              paths:
                slug: true
       -
          # https://123.execute-api.us-east-1.amazonaws.com/dev/robots.txt
          src: static/robots.txt
          path: /robots.txt
       -
          # https://123.execute-api.us-east-1.amazonaws.com/dev/browserconfig.xml
          src: static/browserconfig.xml
          path: /browserconfig.xml

3 - Make api gateway act as a proxy to the S3 bucket for any static route requests such as /robots.txt, /browserconfig.xml etc.

Do note that you can only setup static routes for files inside the static/ directory.

Related: #29, #14

Support pages with similar names

Given I have two nested pages

  • pages/one/foo.js
  • pages/two/foo.js

And I want to provide different custom page routing configurations for these

Then I expect to write my custom pageConfig like this:

custom:
  serverless-nextjs:
    nextConfigDir: ./
    pageConfig:
      one/foo: #...
      two/foo: #...

But only the page file is used as a name, so I can not provide different configs for these.

I'd propose to use the relative page path as identifier in the custom config.

I'd be happy to provide a PR. But this change would be a braking change with the current API. Please let me know how to proceed.

Support for custom source directory

I am using TypeScript and want to put all pages and components in a ./src directory. This works fine with Next.js by simply adding src as the last argument to any next cli command (such as next build src) but I can't figure out how to get it to work with this plugin.

Question about multiple Lambda code bundles and cold boot times

At the moment this plugin creates a single code bundle with all of the handler functions as separate files within e.g. my-app.zip with homepage.js and product.js in the zip.

However, I thought one of the main reasons that Next.js created a Lambda per function was so that Lambda code bundle size could be kept to a minimum thus improving cold boot times? I.e. might have expected my-app-homepage.zip, my-app-product.zip etc. instead?

Route path returns 404 for nested folder with an index.js file.

First of all, thanks for making this awesome serverless plugin!
I guess this is more of a question since I may be doing something wrong, but my pages directory looks like the following:

pages
└───home
|   | home.js
|   | home.scss
|   | index.js
└───about
|   | about.js
|   | about.scss
|   | index.js

In each of the subdirectory's index.js file, I export the corresponding page.
When deploying, 4 functions are created:

  • /home
  • /home/home
  • /about
  • /about/about

The single nested routes return a 404, while the double nested routes return the correct page.
Is this route structure intended? I ask because when running not in serverless mode, nextjs correctly builds only the single nested routes (/home, /about).

Read /pages subdirs

I think the lib/getNextPagesFromBuildDir.js does not read the subdirs for pages. Maybe its a good idea to do so, because next suppurts this.

Basic assets for index

robots.txt, manifest.json, or browserconfig.xml needs to be at the root of the site (index page).
How do we serve these files ?

No file matches include / exclude patterns

I'm getting a No file matches include / exclude patterns error on the example app while trying to deploy from Windows. Ideas?

C:\dev2019\serverless-nextjs-plugin\examples\basic-next-serverless-app>serverless deploy
Serverless Nextjs: Started building next app ...
Creating an optimized production build ...

Compiled successfully.

 ┌ /
 ├ /_app
 ├ /_document
 ├ /_error
 ├ /about
 └ /post

Serverless Nextjs: Copying next pages to tmp build folder
Serverless Nextjs: Found 4 next page(s)
Serverless Nextjs: Creating compat handler for page: about.js
Serverless Nextjs: Creating compat handler for page: index.js
Serverless Nextjs: Creating compat handler for page: post.js
Serverless Nextjs: Creating compat handler for page: _error.js
Serverless: Packaging service...
Serverless: Excluding development dependencies...

  Serverless Error ---------------------------------------

  No file matches include / exclude patterns

  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
     Issues:        forum.serverless.com

  Your Environment Information -----------------------------
     OS:                     win32
     Node Version:           10.13.0
     Serverless Version:     1.37.1

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.