Ravel API

Core Application Components

Ravel

This class provides Ravel, a lightweight but powerful framework for the rapid creation of enterprise Node.js applications which scale horizontally with ease and support the latest in web technology.

new Ravel()

Extends EventEmitter

Example
const Ravel = require('ravel');
const app = new Ravel();
Static Members
Error
DatabaseProvider
AuthenticationProvider
httpCodes
Module
Routes
Resource
config
Instance Members
constructor()
cwd
log
ApplicationError
initialized
listening
server
init()
listen()
start()
close()
registerParameter(key, required, defaultValue)
set(key, value)
get(key)
module(modulePath, name)
modules(basePath)
routes(routesModulePath)
resource(resourcePath)
resources(basePath)

Module

Modules are plain old node.js modules exporting a single class which encapsulates application logic and middleware. 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.

new Module()
Example
const inject = require('ravel').inject;
const Module = require('ravel').Module;

@inject('path', 'fs', 'custom-module')
class MyModule extends Module {
  constructor (path, fs, custom) {
    super();
    this.path = path;
    this.fs = fs;
    this.custom = custom;
  }

  aMethod () {
    this.log.info('In aMethod!');
    //...
  }
}

module.exports = MyModule
const inject = require('ravel').inject;
const Module = require('ravel').Module;

@inject('path', 'fs', 'custom-module')
class MyModule {
  constructor (path, fs, custom) {
    this.path = path;
    this.fs = fs;
    this.custom = custom;
  }

  aMethod () {
    // since we didn't extend Ravel.Module, we can't use this.log here.
  }
}

module.exports = MyModule
const inject = require('ravel').inject;
const Module = require('ravel').Module;

class MyMiddleware extends Module {

  async someMiddleware (ctx, next) {
    //...
    await next;
  }
}

module.exports = MyModule
Static Members
postinit
postinit
prelisten
koaconfig
postlisten
preclose
authconfig
Instance Members
app
name
ApplicationError
log
kvstore
params
db

Routes

Provides Ravel with a simple mechanism for registering koa routes, which should generally only be used for serving templated pages or static content (not for building RESTful APIs, for which Ravel.Resource is more applicable). Clients extend this abstract superclass to create a Routes module.

new Routes(basePath: any)
Parameters
basePath (any)
Example
const inject = require('ravel').inject;
const Routes = require('ravel').Routes;
const mapping = Routes.mapping;
const before = Routes.before;

// you can inject your own Modules and npm dependencies into Routes
@inject('koa-bodyparser', 'fs', 'custom-module')
class MyRoutes extends Routes {
  constructor (bodyParser, fs, custom) {
    super('/'); // base path for all routes in this class
    this.bodyParser = bodyParser(); // make bodyParser middleware available
    this.fs = fs;
    this.custom = custom;
  }

  // will map to /app
  @mapping(Routes.GET, 'app')
  @before('bodyParser') // use bodyParser middleware before handler
  async appHandler (ctx) {
    // ctx is a koa context object.
    // await on Promises, and set ctx.body to create a body for response
    // "OK" status code will be chosen automatically unless configured via ctx.status
    // Extend and throw a Ravel.Error to send an error status code
  }
}

module.exports = MyRoutes;
Static Members
GET
POST
PUT
DELETE
mapping
before
transaction
authenticated
cache
Instance Members
constructor(basePath)
app
ApplicationError
log
kvstore
params

Resource

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. This convention-over-configuration approach makes it easier to write proper REST APIs with less code, and is recommended over "carefully chosen" @mappings in a Routes class.

Omitting any or all of the specially-named handler functions is fine, and will result in a 501 NOT IMPLEMENTED status when that particular method/endpoint is requested.

Resources inherit all the properties, methods and decorators of Routes. See Routes for more information. Note that @mapping does not apply to Resources.

new Resource(basePath: any)

Extends Routes

Parameters
basePath (any)
Example
const inject = require('ravel').inject;
const Resource = require('ravel').Resource;
const before = Routes.before;

// you can inject your own Modules and npm dependencies into Resources
@inject(koa-bodyparser', 'fs', 'custom-module')
class PersonResource extends Resource {
  constructor (bodyParser, fs, custom) {
    super('/person'); // base path for all routes in this class
    this.bodyParser = bodyParser(); // make bodyParser middleware available
    this.fs = fs;
    this.custom = custom;
  }

  // will map to GET /person
  @before('bodyParser') // use bodyParser middleware before handler
  async getAll (ctx) {
    // ctx is a koa context object.
    // await on Promises, and set ctx.body to create a body for response
    // "OK" status code will be chosen automatically unless configured via ctx.status
    // Extend and throw a Ravel.Error to send an error status code
  }

  // will map to GET /person/:id
  async get (ctx) {
    // can use ctx.params.id in here automatically
  }

  // will map to POST /person
  async post (ctx) {}

  // will map to PUT /person
  async putAll (ctx) {}

  // will map to PUT /person/:id
  async put (ctx) {}

  // will map to DELETE /person
  async deleteAll (ctx) {}

  // will map to DELETE /person/:id
  async delete (ctx) {}
}

module.exports = PersonResource;
Static Members
mapping
Instance Members
constructor(basePath)

Dependency Injection

inject

Dependency injection decorator for Ravel Modules, Resources and Routes. Based on Aurelia @inject decorator, but without the es6 module format. Since eliminating relative require()s has always been one of the core goals of the Ravel framework, this decorator takes strings instead of actual references to require()d modules. This string is either the name of the module to require, or the registered name of a client Ravel Module. Ravel Modules override npm modules.

This decorator simply annotates the class with modules which should be injected when Ravel initializes. See util/injector for the actual DI implementation.

inject
Parameters
rest (...any)
Example
// @inject works the same way on Modules, Resources and Routes
const inject = require('ravel').inject;
const Module = require('ravel').Module;

@inject('path', 'fs', 'custom-module')
class MyModule extends Module {
  constructor (path, fs, custom) {
    super();
    this.path = path;
    this.fs = fs;
    this.custom = custom;
  }

  aMethod() {
    // can access this.fs, this.path or this.custom
  }
}

Routing and Middleware

mapping

The @mapping decorator for Routes classes. Indicates that the decorated method should be mapped as a route handler using koa.

Can also be applied at the class-level to indicate routes which do nothing except return a particular status code.

mapping(verb: symbol, path: string, status: (number | undefined), suppressLog: (boolean | undefined))
Parameters
verb (symbol) An HTTP verb such as Routes.GET , Routes.POST , Routes.PUT , or Routes.DELETE .
path (string) The path for this endpoint, relative to the base path of the Routes class.
status ((number | undefined)) A status to always return, if this is applied at the class-level. If applied at the method-level, then the method will be used as a handler instead.
suppressLog ((boolean | undefined)) Don't log a message describing this endpoint iff true.
Example
const Routes = require('ravel').Routes;
const mapping = Routes.mapping;

class MyRoutes extends Routes {
  constructor () {
    super('/');
  }

  // will map to /projects
  @mapping(Routes.GET, 'projects')
  async handler (ctx) {
    // ctx is a koa context object
  }
}
const Ravel = reuqire('ravel');
const Routes = Ravel.Routes;
const mapping = Routes.mapping;

// class-level version will create a route '/' which responds with 501 in this case
@mapping(Routes.DELETE, 'projects', Ravel.httpCodes.NOT_IMPLEMENTED)
class MyRoutes extends Routes {
  constructor (bodyParser) {
    super('/');
    this.bodyParser = bodyParser();
  }
}

before

The @before decorator for Routes and Resource classes. Indicates that certain middleware should be placed on the given route before the method which is decorated.

Can also be applied at the class-level to place middleware before all @mapping handlers.

References any middleware AsyncFunctions available on this.

See before for more information.

before(rest: ...any)
Parameters
rest (...any)
Example
// Note: decorator works the same way on Routes or Resource classes

const inject = require('ravel').inject;
const Routes = require('ravel').Routes;
const mapping = Routes.mapping;
const before = Routes.before;

@inject('koa-bodyparser')
class MyRoutes extends Routes {
  constructor (bodyParser) {
    super('/');
    this.bodyParser = bodyParser();
  }

  @mapping(Routes.GET, '/projects/:id')
  @before('bodyParser') // method-level version only applies to this route
  async handler (ctx) {
    // in here, bodyParser will already have run,
    // and ctx.request.body will be populated
  }
}
// Note: decorator works the same way on Routes or Resource classes
const inject = require('ravel').inject;
const Routes = require('ravel').Resource;
const before = Resource.before;

@inject('koa-bodyparser')
@before('bodyParser') // class-level version applies to all routes in class.
class MyResource extends Resource {
  constructor (bodyParser) {
    super('/');
    this.bodyParser = bodyParser();
  }

  async get(ctx) {
    // in here, bodyParser will already have run,
    // and ctx.request.body will be populated
  }
}

Databases and Transaction-Per-Request

transaction

The @transaction decorator for opening a transaction on a Routes or Resource handler method. Facilitates transaction-per-request. Can also be applied at the class-level to open connections for all handlers in that Route or Resource class.

Connections are available within the handler as an object ctx.transaction, which contains connections as values and DatabaseProvider names as keys. Connections will be closed automatically when the endpoint responds (do not close them yourself), and will automatically roll-back changes if a DatabaseProvider supports it (generally a SQL-only feature).

transaction(args: ...String)
Parameters
args (...String) A list of database provider names to open connections for.
Example
// Note: decorator works the same way on Routes or Resource classes
const Routes = require('ravel').Routes;
const mapping = Routes.mapping;

class MyRoutes extends Routes {
  constructor () {
    super('/');
  }

  @mapping(Routes.GET, 'app')
  @transaction // open all connections within this handler
  async appHandler (ctx) {
    // ctx.transaction is an object containing
    // keys for DatabaseProviders and values
    // for their open connections
  }

  @mapping(Routes.GET, 'something')
  @transaction('mysql', 'rethinkdb') // open one or more specific connections within this handler
  async somethingHandler (ctx) {
    // can use ctx.transaction.mysql
    // can use ctx.transaction.rethinkdb
  }
}
// Note: decorator works the same way on Routes or Resource classes
const Resource = require('ravel').Resource;

@transaction('mysql') // all handlers will have ctx.transaction.mysql
class MyResource extends Resource {
  constructor () {
    super('/');
  }

  async post (ctx) {
    // can use ctx.transaction.mysql
  }
}

TransactionFactory

Database transaction support for ravel applications provided in two ways:

  • Koa middlware (transaction-per-request): Will open and close connections automatically and manage rollbacks when errors are thrown. This mechanism is exposed via the transaction decorator.
  • Scoped transaction: For use outside of the context of an explicit web route. Useful for tasks such as database migration
new TransactionFactory(ravelInstance: any)
Parameters
ravelInstance (any)
Instance Members
scoped(args)

DatabaseProvider

Defines an abstract DatabaseProvider - a mechanism by which connections can be obtained and transactions can be entered and exited for a particular database provider such as MySQL (see ravel-mysql-provider for an example implementation).

new DatabaseProvider(ravelInstance: any, name: any)
Parameters
ravelInstance (any)
name (any)
Instance Members
constructor(ravelInstance, name)
name
prelisten(ravelInstance)
end(ravelInstance)
getTransactionConnection()
exitTransaction(connection, shouldCommit)

Authentication

authenticated

The @authenticated decorator for adding authentication middleware before an endpoint within a Routes or Resource class.

Ensures that a user is signed in before flow proceeds to the endpoint handler. Can also be used without arguments.

Assumes that you've require'd an AuthenticationProvider, and that you have set up an @authconfig Module.

If an object is passed as an argument to the decorator, it is used for configuration purposes, and supports the following keys:

  • {boolean} config.redirect should redirect to app.get('login route') if user is not signed in.
  • {boolean} config.register register user automatically if they are not registered (rather than failing and throwing an ApplicationError.Authentication).

See authconfig and AuthenticationProvider for more information.

authenticated(args: (Class | Method | Object))
Parameters
args ((Class | Method | Object)) A configuration object (returning a decorator), or the Method or Class to directly apply this decorator to.
Example
// Note: decorator works the same way on Routes or Resource classes

const Routes = require('ravel').Routes;
const mapping = Routes.mapping;
const authenticated = Routes.authenticated;

class MyRoutes extends Routes {
  constructor () {
    super('/');
  }

  @authenticated({redirect: true}) // works at the method-level, with or without arguments
  @mapping(Routes.GET, 'app')
  async handler (ctx) {
    // will redirect to this.params.get('login route') if not signed in
  }
}
// Note: decorator works the same way on Routes or Resource classes

const Resource = require('ravel').Resource;
const authenticated = Resource.authenticated;

@authenticated // works at the class-level as well (with or without arguments)
class MyResource extends Resource {
  constructor () {
    super('/');
  }

  async handler (ctx) {
    // will respond with a 401 if not signed in
  }
}

authconfig

A decorator for a module, indicating that it will offer specific functions which encapsulate the configuration of passport.js. For more information on how to create an @authconfig module, please see the README for a Ravel AuthenticationProvider.

authconfig(target: Class)
Parameters
target (Class) The class to declare as the @authconfig class.

AuthenticationProvider

The abstract superclass of AuthenticationProviders - modules which are capable of initializing Passport.js with a particular strategy, and seamlessly verifying requests issued by mobile clients via header tokens instead of sessions.

new AuthenticationProvider(ravelInstance: any)
Parameters
ravelInstance (any)
Instance Members
constructor(ravelInstance)
name
init(app, passport, verify)
handlesClient(client)
credentialToProfile(credential, client)

Logging

Logger

An abstraction for a logging service which behaves like intel.

new Logger(source: any)
Parameters
source (any)
Instance Members
trace()
verbose()
debug()
info()
warn()
error()
critical()

Built-in Error Types

Built-in error types are automatically translated into standard HTTP response status codes when thrown.

RavelError

Base error type for Ravel, to be extended into semantic errors which can be used within Ravel modules, as well as by clients' APIs. These Error types are intended to encapsulate standard things that can go wrong and, when used by a client, are translated into HTTP status codes automatically in Resource responses

new RavelError(msg: any, code: any)

Extends Error

Parameters
msg (any)
code (any)
Instance Members
constructor(msg, code)

AccessError

Access Error - Thrown when a user attempts to utilize functionality they are not authenticated to access

new AccessError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

AuthenticationError

Authentication Error - Most useful for creating authentication providers

new AuthenticationError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

DuplicateEntryError

Duplicate Entry Error - Thrown when an attempt is made to insert a record into the database which violates a uniqueness constraint

new DuplicateEntryError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

IllegalValueError

Illegal Value Error - Thrown when the user supplies an illegal value

new IllegalValueError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

NotAllowedError

Not Allowed Error - Used to mark API functionality which is not permitted to be accessed under the current application configuration

new NotAllowedError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

NotFoundError

Not Found Error - Thrown when something is expected to exist, but is not found.

new NotFoundError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

NotImplementedError

Not Implemented Error - Used to mark API functionality which has not yet been implemented

new NotImplementedError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

RangeOutOfBoundsError

Range Out Of Bounds Error - Used when a range of data is requested from a set which is outside of the bounds of that set.

new RangeOutOfBoundsError(msg: any)

Extends RavelError

Parameters
msg (any)
Instance Members
constructor(msg)

Other Stuff

HTTPCodes

Hash of HTTP Response Codes

new HTTPCodes()
Static Members
OK
CREATED
NO_CONTENT
PARTIAL_CONTENT
MULTIPLE_CHOICES
MOVED_PERMANENTLY
FOUND
SEE_OTHER
NOT_MODIFIED
USE_PROXY
SWITCH_PROXY
TEMPORARY_REDIRECT
PERMANENT_REDIRECT
BAD_REQUEST
UNAUTHORIZED
PAYMENT_REQUIRED
FORBIDDEN
NOT_FOUND
METHOD_NOT_ALLOWED
NOT_ACCEPTABLE
PROXY_AUTHENTICATION_REQUIRED
REQUEST_TIMEOUT
CONFLICT
GONE
LENGTH_REQUIRED
PRECONDITION_FAILED
REQUEST_ENTITY_TOO_LARGE
REQUEST_URI_TOO_LONG
UNSUPPORTED_MEDIA_TYPE
REQUESTED_RANGE_NOT_SATISFIABLE
EXPECTATION_FAILED
IM_A_TEAPOT
AUTHENTICATION_TIMEOUT
METHOD_FAILURE
UNPROCESSABLE_ENTITY
LOCKED
FAILED_DEPENDENCY
UPGRADE_REQUIRED
PRECONDITION_REQUIRED
TOO_MANY_REQUESTS
REQUEST_HEADER_FIELDS_TOO_LARGE
LOGIN_TIMEOUT
NO_RESPONSE
RETRY_WITH
BLOCKED_BY_WINDOWS_PARENTAL_CONTROLS
REQUEST_HEADER_TOO_LARGE
CERT_ERROR
NO_CERT
HTTP_TO_HTTPS
TOKEN_EXPIRED_INVALID
CLIENT_CLOSED_REQUEST
INTERNAL_SERVER_ERROR
NOT_IMPLEMENTED
BAD_GATEWAY
SERVICE_UNAVAILABLE
GATEWAY_TIMEOUT
HTTP_VERSION_NOT_SUPPORTED
VARIANT_ALSO_NEGOTIATES
INSUFFICIENT_STORAGE
LOOP_DETECTED
BANDWIDTH_LIMIT_EXCEEDED
NOT_EXTENDED
NETWORK_AUTHENTICATION_REQUIRED
ORIGIN_ERROR
WEB_SERVER_IS_DOWN
CONNECTION_TIMED_OUT
PROXY_DECLINED_REQUEST
A_TIMEOUT_OCCURRED
NETWORK_READ_TIMEOUT_ERROR
NETWORK_CONNECT_TIMEOUT_ERROR

cache

The @cache decorator for Routes and Resource classes. Indicates that response caching middleware should be placed on the given route before the method which is decorated.

Can also be applied at the class-level to place caching middleware before all @mapping handlers.

References any middleware AsyncFunctions available on this.

See cache for more information.

cache(args: ...Any)
Parameters
args (...Any) Options for the caching middleware, or undefined.
Example
// Note: decorator works the same way on Routes or Resource classes

const Routes = require('ravel').Routes;
const mapping = Routes.mapping;
const cache = Routes.cache;

class MyRoutes extends Routes {
  constructor () {
    super('/');
  }

  @mapping(Routes.GET, '/projects/:id')
  @cache // method-level version only applies to this route
  async handler (ctx) {
    // The response will automatically be cached when this handler is run
    // for the first time, and then will be served instead of running the
    // handler for as long as the cached response is available.
  }
}
// Note: decorator works the same way on Routes or Resource classes
const Resource = require('ravel').Resource;
const cache = Resource.cache;

// class-level version applies to all routes in class, overriding any
// method-level instances of the decorator.
@cache({expire:60, maxLength: 100}) // expire is measured in seconds. maxLength in bytes.
class MyResource extends Resource {
  constructor (bodyParser) {
    super('/');
    this.bodyParser = bodyParser();
  }

  async get(ctx) {
    // The response will automatically be cached when this handler is run
    // for the first time, and then will be served instead of running the
    // handler for as long as the cached response is available (60 seconds).
  }
}