-
Notifications
You must be signed in to change notification settings - Fork 6
Migration Guide (0.25.x 1.0.0 rc.x)
Core Ravel components are now defined using decorators instead of via class extension.
const Ravel = require('ravel');
const Module = Ravel.Module;
class MyModule extends Module {
// ...
}
becomes
const Ravel = require('ravel');
const Module = Ravel.Module;
@Module('name') // the injection name for your module. Brackets and argument can be omitted for name inference as before.
class MyModule {
// ...
}
const Ravel = require('ravel');
const Routes = Ravel.Routes;
class MyRoutes extends Routes {
constructor () {
super('/my/base/path');
}
// ...
}
becomes
const Ravel = require('ravel');
const Routes = Ravel.Routes;
@Routes('/my/base/path')
class MyRoutes {
// ...
}
Same as Routes
, now appearing as:
const Ravel = require('ravel');
const Resource = Ravel.Resource;
@Resource('/my/base/path')
class MyResource {
// ...
}
In Ravel, it was common to inject several things into a class and then perform assignments in the constructor. This has been streamlined via the addition of the @autoinject
decorator. This decorator can be mixed with the @inject
decorator for those injectables which require some instantiation in your constructor before assignment to this
.
const Ravel = require('ravel');
const Module = Ravel.Module;
const inject = Ravel.inject;
@inject('moment', 'fs')
class MyModule extends Module {
constructor(moment, fs) {
this.moment = moment;
this.fs = fs;
}
}
becomes
const Ravel = require('ravel');
const Module = Ravel.Module;
const autoinject = Ravel.autoinject;
@Module('name')
@autoinject('moment', 'fs')
class MyModule {
// this.moment and this.fs are
// now automatically available in
// any method, after construction
}
Since class extension has been eliminated, core services such as errors this.ApplicationError
or logging this.log
have become "core services" which are available for injection into any application component.
-
this.app
can be injected via@autoinject('$app')
and then used asthis.$app
-
this.ApplicationError
->@autoinject('$err')
->this.$err
-
this.log
->@autoinject('$log')
->this.$log
-
this.kvstore
->@autoinject('$kvstore')
->this.$kvstore
-
this.params
->@autoinject('$params')
->this.$params
-
this.db
->@autoinject('$db')
->this.$db
Fields of app
have been renamed to be consistent with these new injection names (i.e. app.log
is now app.$log
).
Directory scanning is now consolidated into app.scan
. Classes can also be loaded directly via app.load
, which makes testing much easier.
-
app.modules()
->app.scan()
-
app.resources()
->app.scan()
-
app.routes()
->app.scan()
Note that calling app.scan()
on your entire tree of Module
s, Resources
and Routes
is completely viable, though it may change the inferred injection names of your Modules
(since you'll have changed the directory you are scanning "from" to be one level higher than before).
app.load
is useful for testing:
const Ravel = require('ravel');
const Module = Ravel.Module;
@Module('name')
class MyModule {
}
const app = new Ravel();
// ...
app.load(MyModule);
// ...
The original approach to provider registration is not very eslint
-friendly, since it relies on instantiation side-effects. The new approach is better:
new MySQLProvider(app, 'mysql');
becomes
app.registerProvider(MySQLProvider, 'mysql');
Creating middleware in Ravel used to look something like this:
const MyResource extends Resource {
constructor() {
super('/base/path');
this.myMiddleware = async (ctx, next) => { /* ... */ }
}
@before('myMiddleware')
async getAll(ctx) {
// ...
}
}
While this worked, it often led to bloated constructors for Routes
and Resource
classes, and forced business logic to live there when it would have been better suited for definition in a Module
. Ravel now includes the @middleware
decorator to permit the definition of middleware in Module
s:
@Module('middleware')
const MyMiddleware {
@middleware('my-middleware') // give it a name
async handler(ctx, next) {
// ...
}
}
// then
@Resource('/base/path')
const MyResource {
@before('my-middleware') // use it directly, by name, without injection!
async getAll(ctx) {
// ...
}
}
You may now reference environment variables in .ravelrc.json
and they will automatically be loaded from the environment during app.init()
:
{
'redis host': '$REDIS_HOST',
// ...
}
Ravel lifecycle methods are now async
, and require an IIFE until node adds top-level async
support:
(async () => {
await app.init();
await app.listen();
await app.close();
})();
This has the added benefit of ensuring that lifecycle-decorated methods in Module
s will finish before the next lifecycle event occurs. Declare your lifecycle-decorated methods async
and be sure to return
and/or await
on Promise
s whenever possible:
@prelisten
async handler () {
// do something async
}
-
app.set('koa public directory')
is nowapp.set('public directory')
-
app.set('koa favicon path') now
app.set('favicon path')` - Session cookies for Ravel are now
ravel.sid
andravel.sid.sig
instead ofkoa.sid
andkoa.sid.sig
.
app.set('koa view directory')
and app.set('koa view engine')
are now deprecated. The underlying koa-views
library was not particularly functional, and using pug
etc. directly is easy, well-documented, and eliminates needless wrapper libraries. In general, wrapper libraries will be avoided whenever feasible.
Useful for testing. Ravel application components can now be accessed via app.module('injectionName')
, app.routes('/base/path')
and app.resource('/base/path')
after app.init()
. This means that a Ravel app can be bootstrapped and unit tested without having to provide mocks for $log
, $kvstore
, etc. See the new docs for more information.
Ravel now includes WebSocket integration. See the docs for more information.
Using signed session cookies is an important concept in Ravel. Productionizing this means exposing a mechanism for clients to rotate signing keys periodically. This can now be accomplished via app.rotateKeygripKey
, or in a Module
via $app.rotateKeygripKey
. It is encouraged to store and retrieve keys in an external store, such as redis
and then synchronize rotations between Ravel application instances. Automated synchronization will be considered before the 1.0.0 release (i.e. calling rotateKeygripKey
on an instance of your app causes all nodes in the cluster to rotate).