node package manager
Easy sharing. Manage teams and permissions with one click. Create a free org »

railroad

Railroad

npm version Build Status Coverage Status Dependency Status devDependency Status Shitty logo, help me!

Basic framework for rest api's based on koa and uses Waterline models.

Features:

  • Automatic REST controllers with support for authentication and authorization.
  • Authentication middleware (Json Web Tokens or custom implementation).
  • Configure ACL for controllers and actions.
  • ACL based on the current user (e.g. a user can only edit itself).
  • Pagination
  • Etag and Conditional GET

Usage

let koa = require('koa');
 
let Railroad = require('railroad');
let config = require('./config');
 
let app = koa();
 
Railroad(app, config, {
    acl: require('./acl'),
    auth: require('./auth')
}).then(() => {
    app.listen(3000);
}).catch((err) => {
    console.log(err.stack);
});

Configuration

// config.js 
 
let path = require('path');
let connections = require('./connections');
let routes = require('./routes');
 
module.exports = {
    // Path to the controllers directory 
    controllers: path.join(__dirname, 'controllers'),
 
    // Path to the models directory 
    models: path.join(__dirname, 'models'),
 
    connections: connections,
    routes: routes,
 
    // Options for resource controllers/actions. 
    resource: {
        pagination: true, // turn on pagination 
        pageLimit: 50 // If pagination is turned on, set the limit per page 
    }
 
    // Options for Json Web Tokens (if you want to use it) 
    jwt: {
        expiresIn: 180 * 60,
        secret: 'secret'
    }
};

Database connections

The database connection configuration is a configuration object for the Waterline ORM.

// connections.js 
 
let diskAdapter = require('sails-disk');
 
/**
 * Waterline ORM config.
 */
module.exports = {
    adapters: {
        default: diskAdapter,
        disk: diskAdapter
    },
 
    connections: {
        myLocalDisk: {
            adapter: 'disk'
        }
    },
 
    defaults: {
        connection: 'myLocalDisk',
        migrate: 'safe'
    }
};

Resource Controllers

A resource controller will provide all the CRUD operations for a model. It taps into the authentication and authorization functionality. It will use the global pagination configuration, or you can pass an optional options object. A Resource will return an object. Example:

let Resource = require('railroad/lib/utils/Resource');
 
// model name and optional "resource" options 
module.exports = Resource('User', { pagination: false });

A resource has the following methods:

  • find: GET method, returns an array with resources.
  • findOne: GET method, returns a single resource
  • create: POST method, create a resource and return it.
  • update: PATCH method, update a resource and return it.
  • destroy: DELETE method, delete a resource.

If you do not need all methods, you could make your own controller and use the action helpers individually (located in railroad/lib/utils/ResourceAction).

Pagination

By default a Resource has pagination on the find method. Query parameters page and limit are used to control the pagination. A response will include Link Headers for navigation to other pages.

To turn off pagination pass the following option into the resource function:

Resource('User', { pagination: false });

Routes

The routes configuration maps paths to controllers or actions. For a resource controller, just use the path. For a custom controller action, add the method as a prefix. Example route configuration:

// routes.js 
 
module.exports = {
    '/product': 'ProductController', // Resource route 
    'get /product/custom': 'ProductController.custom', // Custom route 
    'get /product/custom/:title': { // Custom route alternate syntax 
        controller: 'ProductController',
        action: 'customOne'
    },
    '/pet': 'animals/PetController' // Nested file 
};

Authentication

A middleware is used to authenticate a user. An example:

// auth.js 
 
module.exports = function* (next) {
    if (!this.state.route.requiresAuth) {
        return yield next;
    }
 
    this.state.user = {
        id: 2,
        name: 'Test User',
        isAdmin: false
    };
 
    return yield next;
};

Railroad includes an auth middleware and controller that uses Json Web Tokens. See the example.

Authorization

The ACL file must export a function with the user as it's argument. An object explaining the rights of the user must be returned from this function.

Properties names of this object can be controller names, (action names nested within a controller) or * for defining the default ACL.

Values can be a boolean, function or an object.

  • boolean: When set to true, authentication is required.
  • object: Authentication is required, but authorization depends on the accessed resourced. The object will describe criteria that applies to the Waterline model. It is basically a where clause.
  • function: Authentication is required, but the outcome of the function will decide if a user has access. A function can either return a boolean or an object. Depennding on the type, one of the previous rules will apply.
module.exports = function (user) {
    return {
        '*': true, // All routes protected by default 
        'animals/PetController': false, // Allow PetController 
        ProductController: {
            find: {
                userId: user.id
            },
            custom: true, // Require auth 
            customOne: () => user.isAdmin, // Conditional Acl with a function 
            create: false,
            findOne: {
                userId: user.id // Conditions for the Waterline model 
            },
            update: {
                userId: user.id
            },
            destroy: {
                userId: user.id
            }
        }
    }
};

Context added by Railroad

  • ctx.config: The railroad configuration object.
  • ctx.state.route.controllerName: Name of the current controller.
  • ctx.state.route.actionName: Name of the current action.
  • ctx.state.route.path: Path as defined in the route configuration.
  • ctx.state.route.method: Method of the current route.
  • ctx.state.route.requiresAuth: Check if the current route requires authentication.
  • ctx.state.user: The current user.
  • ctx.state.route.acl.authenticated: If the current user is authenticated.
  • ctx.state.route.acl.authorized: If the current user is authorized to access the current route.
  • ctx.state.route.acl.criteria: Criteria for the current route as defined in the acl middleware.