Agni

Simple and intuitive MVC web framework for node.js

agni

Simple and intuitive MVC web framework for node.js.

Overview

Agni is a web framework built on top of Express.

It adds a thin layer of abstraction which makes route definition and directory organization easier, without giving up the full power and flexibility of Express.

It does not include any database abstraction system, but leaves this choice to the developer.

Quick start

  1. Install agni globally: npm install agni -g.

  2. Create an application called welcome: agni create welcome

  3. Enter the newly created directory: cd welcome.

  4. Install all dependencies: npm install.

  5. Run the application: npm start.

  6. Point your browser to localhost:3000.

  7. If you see the welcome message, it works!

Directory structure

Agni is loosely based on the MVC development pattern.

The root application directory is app.

Inside app, you have the following directories:

File extensions

You can use the extensions .controller.js, .model.js and .lib.js for more readable file names.

Routing

Routes are defined in YAML format, in the app/config/routes.yaml file (or alternatively as standard javascript objects, but you should rename the file to route.js in that case).

Each route definition is composed of a label, one or more routes, and an action.

---

label:
  route: verb /path
  action: controller.action

Routes are composed of 2 segments, separated by a space:

Actions are composed of 2 segments, separated by a dot (.):

Ex.:

---

foo:
  route: get /foo
  action: foo.index
// app/controllers/foo.controller.js

exports.index = function() {
  this.render('foo');
}

Controllers

Controller properties

Inside controllers, you can access the standard request and response objects as this.req as this.res, respectively.

For convenience, the following properties are also provided:

Controller methods

Inside controllers you also have a this.url method, which helps you build URLs from route labels and arguments.

url(label, arg1, arg2, ...) or url(label, argsArray)

Ex.:

firstPage:
  route: /home
  action: home.index
FooController.prototype.redirectToHome = function() {
  this.res.redirect(this.url('firstPage', 'bar', 'baz'));// Redirects to /home/bar/baz
}

Views

Rendering a view

Rendering views is very easy. Inside a controller, execute:

this.render(viewName, locals, callback).

If you omit the view name, it will be the same as your controller module. For example, let's suppose you have a controller module called hello:

//File: app/controllers/hello.controller.js

exports.index = function() {
  //This will render the view 'app/views/hello.jade'
  this.render();
}

View functions

Inside views, you can use the url function to build URLs from route labels.

Ex.:

a(href='#{url("firstPage", "bar", "baz")}')

See the Controller methods paragraph for more details.

Models

To access a model, you can use this.model(modelName) from any controller or library. It will return the same object as require(__dirname + '../models/' + modelName).

Example:

//File: app/models/article.model.js

function ArticleModel() {
  this.db = getMyDb();
}

ArticleModel.prototype.getArticle = function(id, callback) {
  // Read article from DB
  this.db.findArticleById(id, callback);
}

module.exports = ArticleModel;

And in the controller:

//File: app/controllers/article.controller.js

exports.read = function(id) {
  var self = this;

  var ArticleModel = this.model('article');
  var articleModel = new ArticleModel();
  articleModel.getArticle(id, function(err, data) {
    if(! err) {
      self.render('read_article', {articleData: data});
    }
  });
}

Libraries

To access a library, you can use this.lib(libraryName) from any controller or library. It will return the same object as require(__dirname + '../libraries/' + libraryName).

Example:

//File: app/libraries/string_helpers.js

exports.reverse = function(str) {
  return str.split('').reverse().join('');
}

In the controller:

exports.read = function() {
  var str = 'Hello';
  var reversed = this.lib('string_helpers').reverse(str);
  this.render({reversedHello: reversed});
}

Configuration

In all controller, model and library functions you can access the Express app.settings object as this.settings.

Error handling

Because Agni uses regular Connect middleware, you can pass errors to the next function, available inside controller functions as this.next. This will trigger a 500 Internal Server Error and send the error message to stderr. HTML error pages are in the views/errors directory.

Example:

//File: app/controllers/article.js

exports.read = function(id) {
  var self = this;

  this.model('article').getArticle(id, function(err, result) {
    if(! err) {
      self.res.json(result);
    } else {
      self.next(err);
    }
  });
}