Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save daffl/a66666c8f85d15aba60f to your computer and use it in GitHub Desktop.

Select an option

Save daffl/a66666c8f85d15aba60f to your computer and use it in GitHub Desktop.
CanJS + Feathers + MongoDB + Node

In this post I'd like to introduce a full JavaScript web application stack that uses MongoDB and Node on the server and CanJS on the client. The concept is similar to the MEAN (MongoDB, Express, Angular and Node) stack but has some advantages worth mentioning:

  1. The server boilerplate is small enough to not require a generator
  2. Thanks to Feathers you will get real-time capabilities for free
  3. You will use CanJS on the client

Feathers

Feathers is a small framework built top of Express, the most popular web framework for NodeJS. Just like Express, Feathers is very light weight and unassuming about how you build your applications. You can literally drop it in place of Express (4.0) into any existing application and everything will still work just the same.

The key difference is that additionally to your usual Express middleware function, Feathers can also use services. A service is a JavaScript object that provides one or more of the following methods and signatures:

var feathers = require('feathers');
var app = feathers();

// ...

app.use('/todos', {
  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) {}
});

As you can see, a service represents basic CRUD functionality which makes it perfect for creating RESTful APIs. Just add app.configure(feathers.rest()) before using any service and they will be exposed through a JSON REST webservice.

This is not where things end however. Because services just handle data they can easily be exposed through other means, for example via websockets. Feathers supports SocketIO and Primus (an abstraction layer for websocket libraries) out of the box and it takes as little as app.configure(feathers.socketio()) to make your API real-time. This means you can call service methods like this:

var socket = io();

socket.emit('todos::create', {
  description: 'I have to learn CanJS'
}, {}, function(error, data) {
  console.log('Todo successfully created');
});

And receive created, updated, patched and removed events when service data change:

socket.on('todos created', function(todo) {
  console.log('Someone, somewhere created a new todo', todo);
});

It does not matter what caused the event so even if someone created a Todo via the REST API or on the server the websockets will still receive a todos created event.

Your first service

Lets start our simple Todo webservice by just implementing the get functionality. First install Feathers:

npm install feathers

And then in app.js:

var feathers = require('feathers');

var app = feathers()
  // Set up a REST api
  .configure(feathers.rest())
  // And SocketIO, because we can
  .configure(feathers.socketio());

app.use('/todos', {
  get: function(id, params, callback) {
    callback(null, {
      id: id,
      description: 'You have to do ' + id + '!'
    });
  }
});

app.listen(8080);

When running node app.js and opening http://localhost:8080/todos/dishes you should see the following JSON response:

{
  "id": "dishes",
  "description": "You have to do dishes!";
}

Connecting to MongoDB

As mentioned before, services usually implement basic CRUD functionality. For MongoDB, our persistent data storage of choice, this has already been done in the feathers-mongodb module. We also need the Express body-parser to parse POST, PUT and PATCH requests to our API.

After npm install body-parser feathers-mongodb and a MongoDB instance running with the default configuration on your localhost you can get a persistent Todo REST and websocket service up and running like this:

var feathers = require('feathers');
var bodyParser = require('body-parser');
var app = feathers()
  // Set up a REST api
  .configure(feathers.rest())
  // And SocketIO, because we can
  .configure(feathers.socketio())
  // Parse JSON HTTP bodies
  .use(bodyParser.json())
  // Also let us submit HTML forms
  .use(bodyParser.urlencoded());

// Set up a basic MongoDB CRUD service locally on the
// todo-app database and the todos collection
app.use('/todos', mongodb({
  collection: 'todos',
  db: 'todo-app'
}));

app.listen(8080);

Hosting TodoMVC

CanJS TodoMVC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment