Subscribe to our list to receive the latest updates on Node on Fire

Features

An open-source Node.js framework with a rich set of features: a powerful ORM, datastore migrations, universal services, real-time streams, full-text search, cron-like schedulers, one-off tasks and more.

Real-time and web sockets

Out of the box, Node on Fire enables real-time streams on all your models. It only takes a minute to write a chat app.

The streams module uses PostgreSQL's LISTEN and NOTIFY commands to push new messages over web sockets to users. This is all you have to do:

$scope.messageStream = MessageModel.stream({}, {limit: 30, orderBy:{createdAt: -1}});

In your AngularJS view you simply show the stream's results.

<li class="message" ng-repeat="message in messageStream.results">{{message.text}}</li>

Dependency injection

Angular's dependency injection is great. That's why, in Node on Fire, that dependency injection is also available in the back-end.

You can even use the simpler implicit annotation because Node on Fire seamlessly replaces implicit annotations with inline array notations in the build phase.

app.post('/api/users', function(UserModel, request) {
    return UserModel.create(request.body)
        .then(function(user) {
            return user;
        });
    });

Universal services

When you create a service, it's available on both the front- and the back-end. This makes it easy to re-use code in your UI but also in your back-end logic.

// This creates a GET route and returns 123 from MyService (in the back-end).
app.get('/api/value', function(MyService) {
    return MyService.getValue();
});

// This creates a controller and sets the scope's value to 123 from MyService (in the front-end).
app.controller('/', function MyController(MyService, $scope) {
    $scope.value = MyService.getValue();
});

// This is an universal service, available in both the front- and the back-end.
app.service(function MyService() {
    this.getValue = function() {
        return 123;
    };
});

Full-text search

It's trivial to leverage PostgreSQL's powerfull full-text search. With just a few lines you create a search column, keep it up-to-date, create an API endpoint and an universal Model#search method.

$scope.searchCities = function(query) {
    return CityModel.search(query, {}, {limit: 20});
};

Data model (ORM)

It's easy to declare your data model and all associations (one-to-one, one-to-many and many-to-many).

app.model(function TodoItem(TodoListModel) {
    this.list = [this.BelongsTo(TodoListModel), this.Required];
    this.name = [this.String, this.Required];
    this.completed = [this.Boolean, this.Required, this.Default(false)];
    this.createdAt = [this.DateTime, this.Default('CURRENT_TIMESTAMP')];
});

app.model(function TodoList(TodoItemModel) {
    this.items = [this.HasMany(TodoItemModel), this.AutoFetch];
    this.createdAt = [this.DateTime, this.Default('CURRENT_TIMESTAMP')];
});

Migrations

All changes to your data model are applied through migrations. This makes it super easy to share your data model. You do not need to write migrations yourself, instead, migrations are generated automatically based on the changes of your models.

Migration.prototype.up = function() {
this.models.createModel('TodoItem', {
    id: [this.UUID, this.CanUpdate(false)],
    list: [this.BelongsTo(this.models.TodoList), this.Required],
    name: [this.String, this.Required],
    createdAt: [this.DateTime, this.Default('CURRENT_TIMESTAMP')]
});

this.models.createModel('TodoList', {
    id: [this.UUID, this.CanUpdate(false)],
    items: [this.HasMany(this.models.TodoItem)],
    createdAt: [this.DateTime, this.Default('CURRENT_TIMESTAMP')]
});
};

Migration.prototype.down = function() {
    this.models.destroyModel('TodoItem');
    this.models.destroyModel('TodoList');
};

Integrated A/B testing

It's trivial to create A/B tests. Tests work with your existing analytics service e.g. Mixpanel.

function StartController(textOfButtonTest, $scope) {
    if(textOfButtonTest == 'A') {
        $scope.buttonText = 'Register for FREE';
    }
    else {
        $scope.buttonText = 'Register now';
    };
}
app.controller('/', StartController);

StartController.prototype.resolve = function() {
    return {
        textOfButtonTest: function() {
            return TextOfButtonTest.participate();
        }
    };
};

Workers and queues

Workers execute background tasks to off-load intensive tasks from the web process. It is super easy to create workers.

app.worker(function MailWorker() {
    this.sendResetPasswordMail = function(user, resetPassword) {
        var defer = Q.defer();

        mandrill('/messages/send', {
            message: {
                to: [{
                    email: user.email,
                    name: user.name
                }],
                ...
            }
        }, defer.makeNodeResolver());

        return defer.promise;
    };
});

It's easy to queue a background task from a web process which the worker executes in a worker process.

app.post('/api/forgot-password', function(request, MailWorker, UserModel) {
    return UserModel
        .getMe(request)
        .then(function(user) {
            return MailWorker.sendResetPassword(user, user.resetPassword);
        });
});

Config management

All your config is stored in the .env file (this file shouldn't be tracked in version control), but you can use the fire command line interface to manage the config. You can e.g. set NODE_ENV to production by invoking fire config:set NODE_ENV=production or view your config by invoking fire config.

$ fire config
DEBUG:
NODE_ENV:     development
SESSION_KEYS: XI4frrvs+z1JU9auFEmOIAtM...FL3di8Eysw==
DATABASE_URL: postgres://martijndeh@127.0.0.1/todomvc

Smart caching

To make your app feel even more snappy, Node on Fire utilizes smart caching. Node on Fire instantly shows a result if a cache is available, and quickly replaces it with a fresh version from your back-end.

Node on Fire automatically purges any cache when a related model gets created or updated.

// retrieve a recipe
RecipeModel.findOne({id: $route.params.id}, {autoReload: true, cache: 1000 * 60 * 5})
    .then(function(recipes) {
        // recipes
    });
Subscribe to our list to receive the latest updates on Node on Fire