How to create an OpenJS Architect serverless app with Babel and Rollup

avatar
Brian Leroux
October 29, 2019

Express train

In this article we’ll set up an OpenJS Architect project that uses Babel and Rollup to take advantage of next-generation and experimental JS syntax features. Future editions will explore TypeScript and JSX.

Other articles in this series you may be interested in:


Let’s get started! First:

npm init @architect ./myapp

The Architect init script generated one Lambda function in src/http/get-index/index.js which we’ll overwrite with code generated by Babel.

For now, define a new JS entry file at src/http/get-index/entry.js. Here’s a basic handler with some modern syntax:

// src/http/get-index/entry.js
export async function handler(req) {
  let response = `
    <h1>Test Babel</h1>
    <pre>
    <b>req?.queryStringParameters?.foo</b> ${req?.queryStringParameters?.foo}
    </pre>
    <hr>
    <pre>${JSON.stringify(req, null, 2)}</pre>
  `
  return {
    headers: {
      'content-type': 'text/html; charset=utf8'
    },
    body: response
  }
}

View code

Notice: we’re using ES modules syntax! This is not something the default Node.js runtime for Lambda can do natively… yet!

Install Babel

Babel is extremely configurable and can be extended much farther than the scope of this tutorial; these are the minimal dependencies required.

npm i @babel/core @babel/preset-env core-js

Bonus: add the optional chaining plugin!

npm i @babel/plugin-proposal-optional-chaining

Install Rollup

Rollup creates the smallest possible bundle, which is also nice for cloud function coldstarts. We add it and a few plugins for making module interop less of a headache:

npm i rollup rollup-plugin-babel rollup-plugin-commonjs rollup-plugin-node-resolve

Configure Rollup

The final boss (already)! We need to configure Rollup to transpile entry.js into index.js. This way both local dev and live deployment will work as we’d expect them to.

// rollup.config.js

import babel from 'rollup-plugin-babel'
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'

export default {
  input: 'src/http/get-index/entry.js',
  output: {
    file: 'src/http/get-index/index.js',
    format: 'cjs'
  },
  plugins: [
    resolve(),
    babel({
      exclude: 'node_modules/**',
      presets: [[
        '@babel/env', {
          modules: 'false',
          targets: {node: 10},
          useBuiltIns: 'usage',
          corejs: 3
        }
      ]],
      plugins: ["@babel/plugin-proposal-optional-chaining"]
    }),
    commonjs()
  ]
}

View code

We won’t dig into the config too much, but you’re certainly welcome to! Suffice to say, this will generate a CommonJS module that AWS Lambda can run.

Configure npm scripts

Add the classic npm start and npm run build commands:

"scripts": {
    "build": "npx rollup -c",
    "start": "npm run build && npx arc sandbox",
    "deploy": "npm run build && npx arc deploy"
  },

Work locally, then deploy to AWS when you’re happy with your app

Set up CI/CD

Setup CI/CD

Now that everything is working locally we can set up Begin to deploy our code from GitHub whenever we commit. Login to Begin with your GitHub account, click New app, and scroll to the big Import app button. (My first build was completed in 11.474 seconds!)

You can use the GitHub repo brianleroux/arc-example-babel to get started.

That’s it!

Now you’re up and running with some serverless Babel. Here’s another example of enabling optional chaining on a request object’s queryStringParameter property — something that would normally require nested accessors:

Babel test in browser

req.queryStringParameters && req.queryStringParameters.foo no more!

You can find the full source code for this tutorial here.

Stay tuned: we’ll look at combining AWS Lambda with TypeScript and JSX next!

Next steps