Feathers - Let your applications fly!
Shared REST and real-time APIs with Express.
To get started
Feathers extends Express 4, one of the most popular web frameworks for NodeJS. It makes it easy to create shared RESTful web services and real-time applications using SocketIO and several other NodeJS websocket libraries.
If you are not familiar with Express head over to the Express Guides to get an idea. Feathers works the exact same way except that var app = require('express')();
is replaced with var app = require('feathers')()
. This means that you can literally drop Feathers into your existing Express 4 application and start adding new services right away. The following guide will walk through creating a basic Todo REST and websocket API with Feathers and MongoDB and also explain how to add authentication and authorization. For additional information also make sure to read through the API documentation later.
To get started with this guide, lets create a new folder and in it
npm install feathers
Your first service
The most important concept Feathers adds to Express is that of services. Services can be used just like an Express middleware function but instead are JavaScript objects that provide at least one of the following methods:
var myService = {
find: function(params, callback) {},
get: function(id, params, callback) {},
create: function(data, params, callback) {},
update: function(id, data, params, callback) {},
patch: function(id, data, params, callback) {},
remove: function(id, params, callback) {},
setup: function(app, path) {}
}
This object can be registered like app.use('/my-service', myService)
which - if configured - makes it available as a REST endpoint at /my-service
and also through websockets. As usual in NodeJS, callback
has to be called with the error (if any) first and the data as the second parameter.
Simple Todo
With those methods available we can implement a very basic Todo service that returns a single Todo using the id passed to the get
method:
var feathers = require('feathers');
var app = feathers();
var todoService = {
get: function(id, params, callback) {
callback(null, {
id: id,
text: 'You have to do ' + id + '!'
});
}
};
app.configure(feathers.rest())
.use('/todos', todoService)
.listen(3000);
After running
node app.js
You can go to localhost:3000/todos/dishes and should see the following JSON response:
{
"id": "dishes",
"text": "You have to do dishes!"
}
CRUD Todos
As you might have noticed, service methods mainly reflect basic CRUD functionality. Following up is a longer example with comments for implementing a complete Todo service that manages all Todos in memory:
module.exports = {
id: 0,
todos: [],
getTodo: function(id) {
var todos = this.todos;
for(var i = 0; i < todos.length; i++) {
if(todos[i].id === parseInt(id, 10)) {
return todos[i];
}
}
throw new Error('Could not find Todo');
},
find: function(params, callback) {
callback(null, this.todos);
},
get: function(id, params, callback) {
try {
callback(null, this.getTodo(id));
} catch(error) {
callback(error);
}
},
create: function(data, params, callback) {
data.id = this.id++;
this.todos.push(data);
callback(null, data);
},
update: function(id, data, params, callback) {
try {
var todo = this.getTodo(id);
var index = this.todos.indexOf(todo);
data.id = todo.id;
this.todos[index] = data;
callback(null, data);
} catch(error) {
callback(error);
}
},
patch: function(id, data, params, callback) {
try {
var todo = this.getTodo(id);
Object.keys(data).forEach(function(key) {
if(key !== 'id') {
todo[key] = data[key];
}
});
callback(null, todo);
} catch(error) {
callback(error);
}
},
remove: function(id, params, callback) {
try {
var todo = this.getTodo(id);
var index = this.todos.indexOf(todo);
this.todos.splice(index, 1);
callback(null, todo);
} catch(error) {
callback(error);
}
}
}
The above example exports the service as a module from its own file, todos.js
. This means that in app.js
we can replace the previous todoService
with loading that module. In order to parse JSON encoded HTTP bodies we additionally need to install and load the Express body-parser:
npm install body-parser
var feathers = require('feathers');
var bodyParser = require('body-parser');
var app = feathers();
var todoService = require('./todos');
app.configure(feathers.rest())
.use(bodyParser.json())
.use('/todos', todoService)
.listen(3000);
Running app.js
will now provide a fully functional REST API at http://localhost:3000/todos
. You can test it, for example, using the Postman REST client plugin for Google chrome or via CURL:
curl 'http://localhost:3000/todos/' -H 'Content-Type: application/json' --data-binary '{ "text": "You have to do dishes!" }'
Getting real-time
As previously mentioned, a Feathers service can also be exposed through websockets. You can either use SocketIO or Primus - an abstraction layer for differentNode websocket libraries. In the following examples we will use SocketIO.
SocketIO can be enabled by calling app.configure(feathers.socketio())
. Once set up, it is possible to call service methods by emitting events like <servicepath>::<methodname>
on the socket and also receive events by listening to <servicepath> <eventname>
(eventname can be created
, updated
, patched
or removed
). To make it easier to test in a web page, lets also statically host the files in the current folder. app.js
then looks like this:
var feathers = require('feathers');
var bodyParser = require('body-parser');
var app = feathers();
var todoService = require('./todos');
app.configure(feathers.rest())
.configure(feathers.socketio())
.use(bodyParser.json())
.use('/todos', todoService)
.use('/', feathers.static(__dirname))
.listen(3000);
To test the connection, we can create an index.html
file in the same folder. The example will connect to SocketIO, create a new Todo and also log when any Todo has been created, updated or patched:
<!DOCTYPE HTML>
<html>
<head>
<title>Feathers SocketIO example</title>
</head>
<body>
<h1>A Feathers SocketIO example</h1>
<script src="http://localhost:3000/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect();
socket.on('todos created', function(todo) {
console.log('Someone created a new Todo', todo);
});
socket.on('todos updated', function(todo) {
console.log('Someone updated a Todo', todo);
});
socket.on('todos patched', function(todo) {
console.log('Someone patched', todo);
});
socket.emit('todos::create', {
description: 'You have to do something real-time!'
}, {}, function(error, todo) {
socket.emit('todos::find', {}, function(error, todos) {
console.log('Todos from server:', todos);
});
});
</script>
</body>
</html>
After restarting, going directly to localhost:3000 with the console open will show what is happening on the HTML page. You can also see the newly created Todo at the REST endpoint localhost:3000/todos. With the page open, reating a new Todo via the REST API, for example
curl 'http://localhost:3000/todos/' -H 'Content-Type: application/json' --data-binary '{ "text": "Do something" }'
will also log Someone created a new Todo
. This is how you can implement real-time functionality in any web page without a lot of magic using standardized websocket messages instead of having to re-invent your own.
Persisting to MongoDB
Our CRUD Todo functionality implemented in the service is very common and doesn't have to be implemented form scratch every time. In fact, this is almost exactly what is being provided already in the feathers-memory module. Luckily we don't have to stop at storing everything in-memory. For the popular NoSQL database MongoDB , for example, there already is the feathers-mongodb module and if you need more ORM-like functionality through Mongoose you can also use feathers-mongoose.
npm install feathers-mongodb
With a MongoDB instance running locally, we can replace our todoService
in app.js
with a MongoDB storage on the feathers-demo
database and the todos
collection like this:
var feathers = require('feathers');
var mongodb = require('feathers-mongodb');
var bodyParser = require('body-parser');
var app = feathers();
var todoService = mongodb({
db: 'feathers-demo',
collection: 'todos'
});
app.configure(feathers.rest())
.configure(feathers.socketio())
.use(bodyParser.json())
.use('/todos', todoService)
.use('/', feathers.static(__dirname))
.listen(3000);
And just like this we have a full REST and real-time Todo API that stores its data into MongoDB in just 16 lines of code!
Next steps
To learn more about Feathers go to the feathersjs.com homepage and continue reading this guide.