nuxt-lambda

0.5.8 • Public • Published

Nuxt command to create a Nuxt.js lambda (beta)

npm version npm downloads License

⚠️ Optimized build by default with limited Nuxt.js functionality

You can enable the full Nuxt.js handler by setting lambda.handler: 'full' in your nuxt.config

This command will by default create a lambda for Nuxt.js SSR that is optimized to run serverless. That means most non-essential Nuxt.js SSR features have been stripped out.

  • No connect server (it uses @nuxt/vue-renderer directly instead of going through @nuxt/server)
  • No support for runtime modules (only buildModules)
  • No support for server middlewares (although we might be able to add support for this at a later time)
  • No support for serving static files (there is optional support for the connect handler)
  • No support for hooks

Possible tunables

  • turn off compression render.compressor: false
  • turn off etags render.etag: false

How to use

Install

$ yarn add nuxt-lambda

Configure (optional)

See Options below

Build & test

See Commands

Commands

nuxt-lambda

The command to build a lambda. See -h for all available options (not all are supported).

It will by default create a .lambda dir in your rootDir containing the intermediates and a dist folder with the zip file you need to upload

$ yarn nuxt-lambda <rootDir> [-c nuxt.config.js]

// eg: yarn nuxt-lambda src
  • --handler

Same as options.lambda.handler, see Options. Used to quickly override the handler you want to use

  • --static

Same as options.lambda.static, see Options. Used to quickly override whether to enable support for serving static files or not

  • --no-optimize

Not supported yet

  • --no-nuxt

Build a lambda without re-building your Nuxt.js project.

  • --no-lambda

Not supported yet

  • --no-zip

Not supported yet

test-lambda

The command to test a lambda. See -h for available options.

Accepts either a path to a nuxt rootDir or a path to a packed lambda zip file.

$ yarn test-lambda <rootDir?> <url path>

// eg: yarn test-lambda src /about
// eg: yarn test-lambda /about
  • -p, --persistent

Normally when running test-lambda the lambda is unzipped in a temporary directory which is cleaned-up when the lambda has finished. Passing the persistent option unpacks the lambda in the options.lambda.distDir folder instead only when it doesnt exists yet

  • --debug

If your lambda was built with debug: true in your nuxt.config, use this switch to toggle verbose logging

Available Handlers

connect (default)

This is an optimized handler that uses connect & serverless-http to process requests without starting a full blown server.

minimal (experimental, proof of concept)

This is an extremely optimized handler, it uses abstracted implementations for most dependencies which cannot be tree-shaked but have a reasonable effect on cold-boot / execution times.

Dont use this handler yet in production until we have implemented more tests

  • no serverless-http, uses a lightweight custom implementation
  • fs-extra is stubbed with a lightweight custom implementation
  • a custom compression middleware is used

Preliminary savings seem to be around ~10ms compared to the connect handler

full

Just start a full-blown Nuxt.js server instance with all Nuxt.js features and dependencies. Not optimized at all, provided as fallback so you can still benefit from the packaging this command provides

Debugging

For the optimized handlers, make sure to set debug: true in your nuxt.config. Then re-build your lambda & run the test-lambda command with --debug

Debugging is implemented by returning a Proxy for the res and req variables which logs any get, set or call command. When you have compression enabled, it also prints debug logs about compression.

Also when debug: true the lambda wont be minimized, so you can check the file ${options.lambda.buildDir}/${options.lambda.name}.js to debug the webpack build

Options

You can add a lambda section in your nuxt.config with the following properties

  • name (default: nuxt)

The name of your lambda, ie the zip will be named <name>.zip. Dont change this for now if you want to run the test-lambda command

  • handler (default: connect)

Either full, connect or minimal. See available handlers

  • buildDir (default: .lambda)

The name of the folder where the intermediates for the lambda build are saved (dont use nuxt's buildDir, config is created before nuxt build which removes the nuxt builddir). If relative then relative to rootDir

  • distDir (default: dist)

The name of the folder where the zipped lambda will be saved. If relative then relative to rootDir

  • spa (default: false)

This is intended to be an override when you are using universal mode but have some single pages running as SPA. In essence it will not optimize the lambda by removing SPA-only features

  • static (default: false)

If true then full support for serving static files from nuxt.options.dir.static will also be added to the lambda.

For the full handler this means the serve-static middleware can resolve all static files For the connect handler the serve-static middleware is added to handle static file loading (but only when static: true) For the minimal handler this option only adds the files, so you probably shouldnt use this (there is no safeguard though)

  • zisi (default: undefined)

Options passed to zisi. See @netlify/zip-it-and-ship-it

  • excludeClientFiles (default: undefined)

Used to exclude certain assets from Nuxt's dist/client directory to be included in the Nuxt lambda zip file. Useful when you are combining your lambda with a static file deployment and are using image, video etc assets in your application. Those assets dont need to be included in the lambda zip file when you also deploy all your client files statically already.

Can either be an array of paths or a function. If an array then each file from the .nuxt/dist/client folder is only added when they don't start with the given path. We only support startsWith for now because this feature should be used in conjunction with Nuxt's build.filenames property to move certain file types to a specific subfolder

If a function is provided it receives the relative path to the client folder as first argument, and the full (absolute) filename as second argument

Example

// nuxt.config.js
  build: {
    filenames: {
      img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[contenthash:7].[ext]',
    }
  },
  lambda: {
    excludeClientFiles: ['img/'],
    // or
    excludeClientFiles: clientAssetPath => !clientAssetPath.startsWith('img/'),
  }
  • excludeFiles (default: undefined)

Similar to excludeClientFiles but can be used to also filter server files. Recieves the file path related from .nuxt.

Example

// nuxt.config.js
  build: {
    filenames: {
      img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[contenthash:7].[ext]',
    }
  },
  lambda: {
    excludeFiles: ['dist/client/img/'], // same as excludeClientFiles example above
    // or
    excludeFiles: filePath => filePath.endsWith('.map'), // ignore all sourcemaps
  }
  • webpack (default: null)

Any additional webpack config that is needed for your lambda build. At the moment it doesnt make really sense to touch this

Benchmarks

Please check Benchmarks

Remarks about / page

This page uses the uuid plugin, which means its expected that checksums for the response are never the same

Remarks about /about page

This page does a request to a 3rd pary API, differences in execution time are likely caused by variance in contacting this API

Remarks about /redirect page

serverless-http up to v2.3.1 contains a bug which causes Nuxt.js redirections to fail unless a previous header has been set

Remarks about /nuxt-icon.png file

This file should only return a 200 for the full and connect handlers and the with-static config. The checksums wont be the same due to differences in last-modified times

Running the benchmarks

The benchmark command is located in ./benchmark/run.js

arguments

  • --results <result.json> (optional)

Point to a previous created results file instead of running a new benchmark. Useful for markdown tweaking.

  • --runs <num>

If you want to change the default number of runs (which is 3)

Rationale for using the optimized handler by default

A lambda should be very good at running a single task, ie have a single responsibility. Nuxt will by default start a connect server which provides a similar abstraction layer as lambdas provide. The connect server is actually a very strong feature of Nuxt.js, it helps greatly to simplify deployments. But there are also cases where you dont need this abstraction (anymore) or where this second abstraction layer is just the wrong approach because you deploy serverless. Eg a common approach is to use Nuxt.js serverMiddleware's to deploy an API, but when you are deploying serverless that API should really be run in a separate lambda.

Why is a single responsibility important?

Most important/practical reason is cold boot performance of the lambda. A lambda is started for every single request, although the lambda context might be 'warm' from a previous request there are no guarantees that it will still be warm. Response times/performance of your Nuxt.js website could vary greatly because of this.

Having a single responsibility means less dependencies, less code to parse so quicker cold boots:

Lambda implementation Memory Cold boot time
Full Nuxt.js 1GB ~500ms
Full Nuxt.js 1.5GB ~300ms
nuxt-lambda 1GB ~175ms
nuxt-lambda 1.5GB ?

More information about AWS cold starts in this excellent article by @MikhailShilkov: Cold Starts in AWS Lambda

How does it work

In general the lambda build consists of 4 steps:

  • Create a pre-normalized and optimized nuxt.config
  • Build Nuxt.js
  • Build Lambda, if using the optimized handler then stub any Nuxt.js feature/dependencies we dont need/want
  • Create a Zip file for the lambda:
    • Start with @netlify/zip-it-and-ship-it to zip the Nuxt.js lambda and all/just its dependencies
    • Then add all Nuxt.js resources from <rootDir>/<buildDir>/dist to it
Is stubbing imports to optimize the lambda build really a good approach?

Probably not, it would be better to fix these imports in Nuxt.js core. That might still happen, but stubbing gets us fastests to where we want to be for now. Also it will work with any Nuxt.js version and not just some future release.

Eg just parsing/loading the consola dependency takes up to 10ms. Do we really need fancy logging in a lambda? Plain console should be good enough too, so we replace all consola.<log-fn> statements to console.<log-fn>.

This of course could break your code if you use non-standard log functions. Eg we have to set render.ssrLog: false because otherwise Nuxt.js will try to add a consola reporter

Notice about running nuxt-lambda as Netlify function

At the moment you cant both deploy static files and use a Netlify function as fallback for serving SSR routes due to a redirect issue on their platform. The issue causes your Nuxt function (often, but not always) to be preferred instead of the static files. Meaning that if you dont serve your static file also with your lambda you will get 404's

TODO

  • [ ] (minimal handler) Improve feature testing
  • [ ] (minimal/connect handler) Allow additional middleware's
  • [ ] (maybe) Try to pack vue-server-renderer in a single file (less files could be faster unzipping?)
  • [ ] (maybe) Provide Nuxt.js lambdas as AWS layers (we'd need to remove the stubbing and just use optional requires instead)
    • Then users really only have to deploy their .nuxt/dist folder

License

MIT License

Copyright (c) pimlie

Readme

Keywords

none

Package Sidebar

Install

npm i nuxt-lambda

Weekly Downloads

0

Version

0.5.8

License

MIT

Unpacked Size

64.4 kB

Total Files

37

Last publish

Collaborators

  • pimlie