loading...

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

karltaylor profile image Karl Taylor ・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.

Posted on by:

karltaylor profile

Karl Taylor

@karltaylor

Software Developer / Indie Hacker

Discussion

markdown guide