DEV Community

loading...

10 steps to start building your own Serverless Plugin using Typescript.

Karl Taylor
Software Developer / Indie Hacker
・4 min read

I've found building things with serverless really fun, and after skimming the surface of the documentation trying to build my own plugin, I wanted to start off my development using typescript, here's how I did it.

Step 1:

Setup your npm module to build the serverless plugin.

$ mkdir my-cool-plugin
$ cd my-cool-plugin
$ serverless create --template plugin
$ ls
index.js // <- What serverless made us.
Step 2:

Run npm init so we get ourselves a package.json.

$ npm init
# Whilst we're here, lets initialize git and add a .gitignore file and add node_modules to it.
$ git init
$ echo "node_modules" >> .gitignore
Step 3:

Add typescript as a dependency along with @types/node. We can init typescript so we get our tsconfig.json file.

$ npm i typescript --save-dev
$ npm i @types/node --save-dev
$ node_modules/typescript/bin/tsc --init
Step 4:

Add a tsc build script to our package.json.

{
  "scripts": {
    "build": "tsc ./src/index.ts"
  },
}

Good thing to note here is that scripts inside your package.json will look inside node_modules. That's why in step 3 I had to specify node_modules/typescript/bin/tsc when initialising.

Step 5:

Create our src folder with a basic index.ts file in.

$ mkdir src
$ echo "console.log('hello typescript')" >> src/index.ts
Step 6: (Totally optional)

Check it's all working!

$ npm run build
> my-cool-plugin@1.0.0 build /Users/karltaylor/code/my-cool-plugin
> tsc ./src/index.ts

You would now have an index.js inside your src folder which has been compiled from typescript to normal javascript. But the src directory isn't exactly where we want it.

Step 7:

Add a rootDir and outDir to our tsconfig.json and a watch script to our package.json to re-compile our files on save.

In our tsconfig.json:

{
  "compilerOptions": {
    "rootDir": "./src"
    "outDir": "./dist",
  }
}

And our package.json:

{
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w"
  },
}
Step 8:

Let's copy the contents of the index.js file that serverless gave us when we created in step 1 into our index.ts file.

You will be inundated with plenty of errors in the console that we now need to go and fix...

Now, unfortunately, after a lot of digging, I couldn't find the specific types for building a serverless plugin. But there is a @types/serverless file. To combat all the errors, your index.ts should look something like this:

import Serverless from "serverless";

class ServerlessPlugin {
  serverless: Serverless;
  options: any;

  commands: {};
  hooks: { [key: string]: Function }

  constructor(serverless: Serverless, options: any) {
    this.serverless = serverless;
    this.options = options;

    this.commands = {
      welcome: {
        usage: "Helps you start your first Serverless plugin",
        lifecycleEvents: ["hello", "world"],
        options: {
          message: {
            usage:
              "Specify the message you want to deploy " +
              "(e.g. \"--message 'My Message'\" or \"-m 'My Message'\")",
            required: true,
            shortcut: "m",
          },
        },
      },
    };

    this.hooks = {
      "before:welcome:hello": this.beforeWelcome.bind(this),
      "welcome:hello": this.welcomeUser.bind(this),
      "welcome:world": this.displayHelloMessage.bind(this),
      "after:welcome:world": this.afterHelloWorld.bind(this),
    };
  }

  beforeWelcome() {
    this.serverless.cli.log("Hello from Serverless!");
  }

  welcomeUser() {
    this.serverless.cli.log("Your message:");
  }

  displayHelloMessage() {
    this.serverless.cli.log(`${this.options.message}`);
  }

  afterHelloWorld() {
    this.serverless.cli.log("Please come again!");
  }
}

module.exports = ServerlessPlugin;

Running yarn build in the console should successfully build your index.ts severless plugin boilerplate into dist/index.js

Step 9

Let's create a new serverless project in a new directory.

$ ~/code mkdir my-serverless-test-directory
$ ~/code cd my-serverless-test-directory
$ ~/code/my-serverless-test-directory npm init
$ ~/code/my-serverless-test-directory serverless create --template=hello-world

Let's install our npm module locally simply by referencing its absolute or relative path:

$ ~/code/my-serverless-test-directory npm i --save-dev ~/code/my-cool-plugin

Open up the serverless.yml file and add the name of your plugin to the plugins section:

# Welcome to serverless. Read the docs
# https://serverless.com/framework/docs/

# Serverless.yml is the configuration the CLI
# uses to deploy your code to your provider of choice

# The `service` block is the name of the service
service: my-serverless-test-directory

plugins:
  - my-cool-plugin # <------ Right here! 🚀

# The `provider` block defines where your service will be deployed
provider:
  name: aws
  runtime: nodejs12.x

# The `functions` block defines what code to deploy
functions:
  helloWorld:
    handler: handler.helloWorld
    # The `events` block defines how to trigger the handler.helloWorld code
    events:
      - http:
          path: hello-world
          method: get
          cors: true
Step 10

Let's run our serverless boilerplate plugin to check everything is working as it should:

$ ~/code/my-serverless-test-directory serverless welcome -m "Hello World Serverless Plugin in Typescript"
Serverless: Hello from Serverless!
Serverless: Your message:
Serverless: Hello World Serverless Plugin in Typescript
Serverless: Please come again!

And voila! Your compiled typescript serverless plugin is working!

Personally I've found there is a distinct lack of documentation for building Serverless Plugins, I am very much developing in the dark and figuring out what things do what, but it's very fun to play with.

Feel free to follow me on twitter where I tweet about more tech-related adventures.

Discussion (0)