Package Exports
- ravel
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (ravel) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Ravel
Forge past a tangle of node.js code. Make a cool app.
Introduction
Ravel is a tiny, sometimes-opinionated foundation for rapidly creating maintainable, horizontally-scalable web application back-ends in node.
Layered on top of ES2015/2016 and awesome technologies such as koa, babel, Passport, Intel, Redis, and docker, Ravel aims to provide a pre-baked, well-tested and highly modular solution for constructing enterprise web applications by providing:
- A standard set of well-defined architectural components
- Dependency injection
- Automatic transaction-per-request management
- Authentication and authorization with transparent handling of mobile (i.e. non-web) clients
- Rapid, standards-compliant REST API definition
- Easy bootstrapping, via an enforced, reference configuration of koa and standard middleware
- Horizontal scalability
Installation
As Ravel uses the Spread operator from ES2015, you will need to use a 5.x+ distribution of node.
$ npm install ravelRavel needs Redis to run. As part of the Ravel 1.0 release, a reference project including a dockerized development environment will be provided as a Yeoman generator, but for now you'll need to install Redis somewhere yourself.
Ravel Architecture
Ravel applications consist of three basic parts:
Modules
Plain old node.js modules containing a class which encapsulates application logic. Modules support dependency injection of core Ravel services and other Modules alongside npm dependencies (no relative require's!). Modules are instantiated safely in dependency-order, and cyclical dependencies are detected automatically.
modules/cities.js
const Module = require('ravel').Module;
const inject = require('ravel').inject;
const c = ['Toronto', 'New York', 'Chicago']; // fake 'database'
@inject('async')
class Cities extends Module {
constructor(async) {
super();
this.async = async;
}
getAllCities() {
return Promise.resolve(c);
}
getCity(name) {
return new Promise((resolve, reject) => {
const index = c.indexOf(name);
if (index) {
resolve(c[index]);
} else {
this.log.warn(`User requested unknown city ${name}`);
// reject the promise with a Ravel error, and
// Ravel will automatically respond with the
// appropriate HTTP status code! Feel free to
// implement your own errors as well via Ravel.error(name, code).
reject(new this.ApplicationError.NotFound(`City ${name} does not exist.`));
}
});
}
}Routes
Routes are Ravel's wrapper for koa. They support GET, POST, PUT and DELETE requests, and middleware, via decorators. Like Modules, they also support dependency injection. Routes are most useful for implementing non-REST things, such as static content serving or template serving (EJS, Jade, etc.). If you want to build a REST API, use Resources instead (they're up next!).
routes/index.js
const Routes = require('ravel').Routes;
const inject = require('ravel').inject;
const before = Routes.before; // decorator to add middleware to an endpoint within the Routes
const mapping = Routes.mapping;
@inject('middleware1') // middleware from NPM, or your own modules, etc.
class ExampleRoutes extends Routes {
constructor(middleware1) {
super('/'); //base path
this.middleware1 = middleware1;
// you can also build middleware right here!
this.middleware2 = function*(next) {
yield next;
};
}
@mapping(Routes.GET, '/app')
@before('middleware1','middleware2')
appHandler(ctx) {
ctx.body = '<!DOCTYPE html><html><body>Hello World!</body></html>';
ctx.status = 200;
}
}Resources
What might be referred to as a controller in other frameworks, a Resource module defines HTTP methods on an endpoint, supporting the session-per-request transaction pattern via Ravel middleware. Resources also support dependency injection, allowing for the easy creation of RESTful interfaces to your Module-based application logic. Resources are really just a thin wrapper around Routes, using specially-named handler functions (get, getAll, post, put, putAll, delete, deleteAll) instead of @mapping, and supporting advanced functionality such as transactions-per-request.
resources/city.js
// Resources support dependency injection too!
// Notice that we have injected our cities Module by name.
const Resource = require('ravel').Resource;
const inject = require('ravel').inject;
const before = Resource.before; // decorator to add middleware to an endpoint within the Resource
@inject('cities')
class CitiesResource extends Resource {
constructor(cities) {
super('/cities'); //base path
this.cities = cities;
// some other middleware, which you might have injected or created here
this.anotherMiddleware = function*(next) {
yield next;
};
}
@before('respond') // 'respond' is built-in Ravel rest response middleware
getAll(ctx) { // ctx is a koa context. this is the last middleware which will run in the chain
return this.cities.getAllCities()
.then((list) => {
ctx.body = list;
});
}
@before('anotherMiddleware', 'respond')
get(ctx) { // get routes automatically receive an endpoint of /cities/:id (in this case).
return this.cities.getCity(ctx.params.id)
.then((city) => {
ctx.body = city;
});
}
// post, put, putAll, delete and deleteAll are
// also supported. Not specifying them for
// this resource will result in calls using
// those verbs returning HTTP 501 NOT IMPLEMENTED
// postAll is not supported, because that's stupid.
}Babel configuration
Since decorators are not yet available in Node, you will need to use Babel to transpile them into ES2015-compliant code.
$ npm install babel@6.3.26 babel-plugin-transform-decorators-legacy@1.3.4 babel-register@6.4.3 harmonize@1.4.4Place this .babelrc config file at the root of your source code.
.babelrc
{
"plugins": ["transform-decorators-legacy"],
"only": [
"test/**/*.js"
],
"retainLines": true
}Bringing it all together
app.js
//TODO remove when decorators land in node
require('babel-register');
const app = new require('ravel')();
// parameters like this can be supplied via a .ravelrc file
app.set('keygrip keys', ['mysecret']);
app.modules('./modules'); //import all Modules from a directory
app.resources('./resources'); //import all Resources from a directory
app.routes('./routes/index.js'); //import all Routes from a file
// start it up!
app.start();$ node --harmony_rest_parameters app.jsA more complex example
"You mentioned transactions! Authentication and authorization! Mobile-ready APIs! Get on with the show, already!" --Some Impatient Guy
TODO
API Reference
TODO
Deploying and Scaling Ravel Applications
TODO