#serverless #architecture #codestructure #lambda

Setup Serverless Offline hot reload in Node.js in 2 minutes

How to:
  • avoid serverless offline restart on each code change?
  • run serverless offline faster?
  • apply TypeScript watcher for serverless offline?

      Introduction to the problem

      "This Serverless plugin emulates AWS λ and API Gateway on your local machine to speed up your development cycles."
      (c) Serverless Offline
      And it doesn't provide your hot reload out of the box.

      BUT to do this you don't need any additional serverless plugin and even the webpack.
      Nikolay Matvienko
      Serverless expert
      @matvi3nko

      Don't have a time?

      1. Run your serverless offline command in terminal.
      2. Then open a new tab in terminal and run the command (for code in TS):
      package.json
        "scripts": {
          "watch": "tsc -w -p tsconfig.build.json",
      terminal
      npm run watch
      And you're Done!

      Do you want to know how and want to optimize startup time?

      1. Package your node_modules faster and just only once

      In order to package all your node_modules to serverless /dist folder faster use instrument like 'copy-node-modules'.
      And it will do for you 3 things:
      1. Read package.json from source directory and read dependencies field and not devDependencies.
      2. Search for existing modules and their dependencies in source directory.
      3. Copy all modules to destination directory
      In order to run it on serverless offline start use script like an example below:
      This script below :
      1. runs TS code watcher in child process
      2. parses package.json and copies only right dependencies (node_modules) to dist folder and only once
      cli/packModules.ts
      import util from 'util';
      import fs from 'fs';
      import copyNodeModules from 'copy-node-modules';
      
      import { exec } from 'child_process';
      
      const distModules = './dist/node_modules';
      const modules = './node_modules';
      const exists = util.promisify(fs.exists);
      
      console.log('Running TS Watcher');
      const watcher = exec('npm run watch', async () => {
        await compile();
      });
      
      (async function start() {
        // in order to start the watcher before the serverless itself
        await delay(2000);
      })();
      
      function delay(t) {
        return new Promise((resolve) => {
          setTimeout(async () => {
            await compile();
            resolve();
          }, t);
        });
      }
      
      async function compile() {
        try {
          if (await exists(distModules)) {
            console.log('node_modules found');
            process.exit(0);
          } else if (await exists(modules)) {
            console.log('Copying node_modules to dist.');
            copyNodeModules('', './dist', { devDependencies: false }, (err, results) => {
              if (err) {
                console.error(err);
                process.exit(1);
              }
              Object.keys(results).forEach((name) => {
                const version = results[name];
                console.log(`Package name: ${name}, version: ${version}`);
              });
              process.exit(0);
            });
          } else {
            console.error('run "npm install" before "run localhost"');
            process.exit(1);
          }
        } catch (e) {
          console.error(e);
          watcher.kill('SIGINT');
        }
      }
      
      process.on('SIGINT', () => {
        watcher.kill('SIGINT');
      });
      

      2. Use Serverless offline allowCache: false

      wServerless offline executes your requests to Lambda in a Node.js child processes (or worker-threads if you select this option).

      And there is a cli command flag that is responsible for caching:
      --allowCache Allows the code of lambda functions to cache if supported.


      By the default it equals false.
      It means that on each new request Serverless offline in InProcessRunner.run

      Cons: this gives a small overhead for the function call, since in this case, it reads the file again on every request. But it nothing, since this is not comparable to restarting of application. Don't worry, because it's only for local development. And in the cloud, caching is used and these parameters do not affect each other.

      Pros: this allows the watcher to transpile your TS file on the fly to JS!

      Summary

      The fact that the serverless offline handles each request as new allows us to get rid of the restart and we use code watcher.
      In simple terms, it looks like this.
      package.json
        "scripts": {
          "build": "tsc -p tsconfig.build.json",
          "watch": "tsc -w -p tsconfig.build.json",
          "offline": "rmdir-cli dist/src && npm run build && node dist/cli/packModules.js && sls offline --d --noAuth --location dist --cfnRoleArn arn:aws:iam::XXXXXX:role/AWSCloudFormationServiceExecutionRole --stage LOCAL || 0"
        },
      Ask me a question
      Feel free to write me. I really love to communicate.
      matvi3nko@gmail.com
      @matvi3nko