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:
- Nothing is server-free - you’re just shifting the responsibility somewhere else
- 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.
- 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!