npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nextjs-serverless-lambda-handler

v0.1.2

Published

A simple interoperability wrapper for deploying a Next.js server with AWS Lambda and API Gateway

Downloads

127

Readme

next.js serverless lambda handler

ci_on_commit deploy_on_tag

A simple interoperability wrapper for deploying a Next.js server with AWS Lambda and API Gateway

Why:

  • To make deploying a Next.js app w/ lambda serverless easy - while retaining full control over the contract.
    • e.g., you manage the domain name + routing
    • e.g., you have control over the "handler" and your custom server

How:

  • Translate between Lambda+ApiGatewayEvent and Next.js+Express+HttpServer
    • converts the ApiGatewayEvent that your lambda receives into an HttpRequest your Next.js/Express server expects
    • converts the HttpResponse your Next.js/Express returns into the payload that ApiGateway expects.

Example

install

npm install --save nextjs-serverless-lambda-handler

setup your lambda

import express from 'express';
import { IncomingMessage, ServerResponse } from 'http';
import next from 'next';

// decide whether we're running in development mode
const inDevMode = process.env.NODE_ENV === 'development'; // default to false

// setup our server dependencies
const app = next({ dev: inDevMode });
const handle = app.getRequestHandler();
const promisePrepared = app.prepare(); // define the promise in global context, so we only do it once per lambda container
const server = express();

// define the express routes that we want to use
server.get('*', async (req: IncomingMessage, res: ServerResponse) => {
  await promisePrepared; // wait for nextjs to be ready
  await handle(req, res); // use nextjs to process the req and send the res
});

// expose the handler
export const handler = createApiGatewayEventHandlerForNextJsCustomServer({ server });

deploy - with expected infrastructure (see below)

Expected Infrastructure

Please back this ApiGateway triggered lambda with a CloudFront distribution.

Our recommendation is:

  • set up the Lambda+ApiGateway using Serverless
  • set up the CloudFront distribution using Terraform

Api Gateway

Api Gateway enables lambdas to receive and respond to https requests, like any api. AWS manages spinning up and invoking your lambda on demand.

Here is an example serverless.yml config:

service: awesome-web-app-server

provider:
  name: aws
  runtime: nodejs12.x
  memorySize: 2048 # optional, in MB, default is 1024
  timeout: 27 # api gateway times out at 29 seconds, so wait up to as long as possible
  environment:
    NODE_ENV: ${opt:stage}

functions:
  ssr:
    handler: dist/ssr/handler.handler
    events:
      - http:
          method: GET
          path: '/' # catch root route
          cors: true
      - http:
          method: GET
          path: '/{any+}' # catch all other routs
          cors: true

CloudFront Distribution

A CloudFront distribution does several things for us:

  • Allows using a custom domain name for accessing this api gateway
  • Enables removing the "stage" path prefix that api gateway requires automatically
  • Cache's the responses from api gateway, based on the Cache Control max-age header that your Next.js/Express custom server returns, to reduce the number of requests that hit your lambda.

Here is an example CloudFront distribution module:

locals {
  awesome_domain_name = "www.awesomedomainname.com"
  api_gateway_host = "__random_numbers__.execute-api.__region__.amazonaws.com"
  api_gateway_stage = "__stage__"
}

locals {
  api_gateway_origin_id = "api-gateway-origin-1"
  seconds_per_day       = "${60 * 60 * 24}"
}

data "aws_acm_certificate" "domain_cert" {
  domain      = "*.awesomedomainname.com"
  types       = ["AMAZON_ISSUED"]
  most_recent = true
}

// https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-cloudfront-distribution/
resource "aws_cloudfront_distribution" "web_app_cdn" {
  origin {
    domain_name = "${local.api_gateway_host}"
    origin_id   = "${local.api_gateway_origin_id}"

    origin_path = "/${local.api_gateway_stage}" # e.g., reroute `domain.com/cool-page` to `www.awesomedomainname.com/__stage__/cool-page`

    custom_origin_config {
      http_port              = "80"
      https_port             = "443"
      origin_protocol_policy = "https-only"
      origin_ssl_protocols   = ["TLSv1.2"]
    }
  }

  # misc
  enabled         = true
  is_ipv6_enabled = true

  # which host names should we expect, if using a custom domain name
  aliases = ["${local.awesome_domain_name}]

  # settings for caching; https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#cache-behavior-arguments
  default_cache_behavior {
    allowed_methods  = ["GET", "HEAD"]
    cached_methods   = ["GET", "HEAD"]
    target_origin_id = "${local.api_gateway_origin_id}"

    forwarded_values {
      query_string = true # web apps often use query strings, so we want to forward these

      cookies {
        forward = "none" # if your app needs cookies, change this
      }
    }

    viewer_protocol_policy = "allow-all"
    min_ttl                = 0
    default_ttl            = 0 # by default, do not cache; each page defines its own cache rules. next.js does a good job optimizing this based on whether each path is static or dynamic
    max_ttl                = "${60 * 60 * 24 * 7}" # up to 7 days
  }

  restrictions {
    geo_restriction {
      restriction_type = "none" # no restrictions
    }
  }

  # must specify the https certificate if using a custom domain; not required if not using custom domain
  viewer_certificate {
    acm_certificate_arn = "${data.aws_acm_certificate.custom_domain_cert.arn}"
    ssl_support_method  = "sni-only"
  }
}

Caveats

ApiGateway mandatory url /stage prefix

The /stage prefix that aws requires on api gateway routes will not work with Next.js's routing expectations. To solve this currently, the only option is to use a CloudFront distribution which defines an origin_path that hides the /stage prefix.

The problem is that any requests that go to __your_apigateway_subdomain__.amazonaws.com root will fail. They have to go to __your_apigateway_subdomain__.amazonaws.com/__stage__.

But that means that when you load __your_apigateway_subdomain__.amazonaws.com/__stage__/cool-page and Next.js says to get resources from /_next/.../__some_file__, the browser will ask for __your_apigateway_subdomain__.amazonaws.com/_next/.../__some_file__ and not __your_apigateway_subdomain__.amazonaws.com/__stage__/_next/.../__some_file__. Without the __stage__ prefix, the request will fail

Inspiration

This project is inspired by https://github.com/awslabs/aws-serverless-express. This project really is just a simpler implementation of that - while also fixing a problem which makes that package a blocker for Next.js: content-encoding. aws-serverless-express requires you to define a content encoding per mimetype - while Next.js may gzip or may not gzip per mimetype. For example, Next.js often does not compress small .js files while compressing the large ones. This package determines whether to return a "compressed" response based on the content-encoding header returned by Next.js, rather than statically based on the mimetype like the aws package does.