Serverless & Feather


Why did I do this?

I’ve been toying with a little idea for a product with my partner for a hot minute and wanted to explore ways to create a prototype quickly and for nothing. The most crucial hypothesis for this prototype was to use Quill - an open source WYSIWYG editor.

Being a JS library, it made sense to stick to JS for now. The next logical step for getting up and running quickly was to pick a framework that allowed quick production of a simple API. Feather came up near the top of the list. It’s a service-oriented framework built on top of Express and using it was a nice chance to explore a new framework.

The last consideration was how to run this for free (or minimal cost). Thanks to the cloud, there are quite a lot of options here. Heroku is an easy choice for a free-tier stack, all of the cloud platforms have a free tier, services like Netlify offer more specialised work flows for different use cases… In the end, it didn’t make much difference so I chose to use this as an opportunity to strengthen my knowledge of serverless.

Express & Serverless

At this point, you might be thinking that serverless and express don’t go together. I am mixing the Express server with a serverless deployment strategy. To that I say two things:

  1. Nothing is server-free - you’re just shifting the responsibility somewhere else
  2. For those that already have an Express application on a number of servers, deploying to a serverless platform can provide some real benefits. The application can be refactored after that.
  3. For a small prototype, who cares?

The most likely trouble we could run into is that we hit the memory limit on how much code we can upload for each function. All of the code will be zipped and available to every function. However, in this protoype that’s definitely not going to happen.

Getting it Running

First things first; get the environment set up. For this guide, we’re using AWS as our cloud platform. A full collection of these files can be found in this gist.

Configure Serverless

If you’ve not installed serverless before, follow the Quick Start Guide on their site. Once you’re happy with it all working, move on to the next step.

Set Up A Feather Application

Create the directory and install all the dependencies we need.

mkdir my-feather-app
cd my-feather-app
npm init -f
npm install @feathersjs/feathers @feathersjs/express serverless-http --save

From here, I’m pinching the sample code from the Feather ‘Getting Started’ guide and wrapping the express app in the serverless method provided by serverless-http.

// index.js

const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const serverless = require('serverless-http');


class Messages {
  constructor() {
    this.messages = [];
    this.currentId = 0;
  }

  async find(params) {
    // Return the list of all messages
    return this.messages;
  }

  async get(id, params) {
    // Find the message by id
    const message = this.messages.find(message => message.id === parseInt(id, 10));

    // Throw an error if it wasn't found
    if(!message) {
      throw new Error(`Message with id ${id} not found`);
    }

    // Otherwise return the message
    return message;
  }

  async create(data, params) {
    // Create a new object with the original data and an id
    // taken from the incrementing `currentId` counter
    const message = Object.assign({
      id: ++this.currentId
    }, data);

    this.messages.push(message);

    return message;
  }

  async patch(id, data, params) {
    // Get the existing message. Will throw an error if not found
    const message = await this.get(id);

    // Merge the existing message with the new data
    // and return the result
    return Object.assign(message, data);
  }

  async remove(id, params) {
    // Get the message by id (will throw an error if not found)
    const message = await this.get(id);
    // Find the index of the message in our message array
    const index = this.messages.indexOf(message);

    // Remove the found message from our array
    this.messages.splice(index, 1);

    // Return the removed message
    return message;
  }
}

// This creates an app that is both, an Express and Feathers app
const app = express(feathers());

// Turn on JSON body parsing for REST services
app.use(express.json());
// Turn on URL-encoded body parsing for REST services
app.use(express.urlencoded({ extended: true }));
// Set up REST transport using Express
app.configure(express.rest());

// Set up an error handler that gives us nicer errors
app.use(express.errorHandler());

// Initialize the messages service by creating
// a new instance of our class
app.use('messages', new Messages());

// Export the wrapped application as the 'handler' method
module.exports.handler = serverless(app);

Deployment

And finally, setting up deployment instructions…

// serverless.yml

service: feather-serverless

provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: eu-west-2

functions:
  app:
    handler: index.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'

By now running sls deploy we have got ourselves a nice, quick serverless app ready for protoyping!