Lift It

Forget about maintaining routes files, use dynamic routes created from files in a folder
Install
npm install lift-it
Basic usage
let api = require('lift-it')('./api').lift()
api.run('item/create', {
name: 'Water',
value: 17
}, function (err, response) {
})
Lift-it scans recursively a folder for *.js
files. Those files should export a handler
function, executed when someones 'run' them. The only ignored file is errors.js
.
An example of 'item/create':
module.exports.handler = function (body, success, error) {
Item.create(body, error(function (item) {
success({
id: item.id
})
}))
}
Express example
This module can be easily used with express (or any other route-based web framework).
An example of simple JSON-based POST api:
let app = require('express')()
app.use(require('body-parser').json())
let api = require('lift-it')('./api').lift()
api.actions.forEach(function (action) {
app.post(action.name, function (req, res) {
action.run(req.body, function (err, response) {
res.json(err ? {error: String(err)} : response)
})
})
})
Plugins
The base module will scan a folder and look for exported handler in *.js
files. This base behaviour can be expanded with plugins.
The available bundled plugins are:
- validate: validate the input with validate-fields
- filters: add filters (like middlewares)
- profile: enable profiling async jobs
Docs
require('lift-it')
A function with one parameter, the folder path to scan: require('lift-it')(folder)
.
Returns a lifter
object.
require('lift-it').lean()
Do a lean lift. See lifter.lean()
. Returns a lifted instance
lifter.use(plugIn)
Add the given plugin to the lift process. Read more about available and custom plugins bellow
lifter.recursive
Whether the folder should be scanned recursively or not. The default value is true
.
lifter.requireFile
A callback to handle requiring a file. This allows one to completly change the semantics of a required file. The default value is function (require, path) { return require(path) }
.
lifter.profile
lifter.errorClass
lifter.enableErrorCode
Change settings of run-it. Must be changed only before lifter.lift()
is called.
lifter.lift()
Run the lift process (sync), throws if any error happens. For each *.js
file in the target folder, all plugins will be executed in the order they were 'use'd.
This returns a lifted
object.
lifter.leanLift()
Run the lean lift process, that is, only look for file names. This will not require files, nor apply plugins.
This returns a lifted
object with the lean
flag on. No action can be executed on it.
This is useful if you just want to check action names, not execute them.
lifted.get(name)
Return an action by name. Return an action
object (or null
if not found).
lifted.run(name, ...args, callback)
Run the action with the given arguments. callback(err,...output)
is executed when done. The action is executed in its own domain, see run-it for more info about that.
Throws if no action with the given name is found.
If profile is enabled, the last argument for callback
is the profile data.
lifted.getRunner(name)
Return a run-it runner for the given action. Call runner.exec(...args, callback)
to actually execute the action. See action.getRunner()
.
Throws if no action with the given name is found.
lifted.actions
An array of action
objects
lifted.lean
A boolean. If true
, this is a lean lift (see lifter.leanLift()
), so no action can be executed.
action.path
File path, like '/api/item/create.js'
action.name
Action name, like 'item/create'
. This is the relative path to the target folder, without the trailing '.js'
.
action.filters
An array of filters, executed before the action handler. This is used by plugins.
action.postFilters
An array of post filters, executed after the action handler. This is used by plugins.
action.module
The result of require
-ing the file. action.module.handler
is the handler for this action.
action.profile
A boolean that flags whether profiling is enabled for this action.
action.run(...args, callback)
Run this action. This is the same as lifted.run(action.name, ...args, callback)
.
action.getRunner()
Return the run-it runner for the action. Call runner.exec(...args, callback)
to actually execute the action.
This is useful to make some adhoc changes to the runner, like runner.runInfo(info).profile(true).exec(...args, callback)
. See docs on run-it.
Available plugins
Profile
If the require
-d file exports profile
(a boolean), this value will overwrite the global lift.profile
. Get this plugin with require('lift-it').profile()
.
For example, this file will have profiling always on:
module.exports.profile = true
module.exports.handler = function (body, success, error) {
}
Use:
let liftIt = require('lift-it'),
lift = liftIt('./api')
lift.use(liftIt.profile())
let api = lift.lift()
api.run('item/create', item, function (err, response, profileData) {
})
Validate
Add input validation with validate-fields. Get it with require('lift-it').validate(options)
.
options
is an optional object with the properties displayed bellow (default values indicated):
options = {
exportName: 'fields',
optional: false,
direction: 'input',
position: 0,
getDefaultValue: function () {
throw new Error('Insufficient number of arguments')
},
code: 101,
HTTPStatusCode: 500,
errorHandler: function (action, value, err) {
throw err
},
defineTypes: function (validate) {},
options: {}
}
Example of a file using it:
module.exports.fields = {
name: String,
value: 'uint'
}
module.exports.handler = function (body, success, error) {
}
let liftIt = require('lift-it'),
lift = liftIt('./api')
lift.use(liftIt.validate())
let api = lift.lift()
api.run('item/create', {}, function (err) {
})
This plugin will set action.module[exportName+'-schema']
to validate.parse(action.module[exportName])
.
Filters
Add support for filters. Filters are functions executed sequentially before the handler. One example of their use is implementing authentication. Get it with require('lift-it').filters(folder)
.
Filter handlers are implemented and exported by files in the filters folder. Each of those files may export as many filters as you want.
See an example of a file implementing two filters:
module.exports = function (body, success, error) {
success('Some data I got elsewhere')
}
module.exports.double = function (body, success, error) {
body.value *= 2
success()
}
The 'item/create'
action may use those filters this way:
module.exports.filters = ['myFilter', 'myFilter.double']
module.exports.handler = function (body, moreData, success, error) {
}
If this filter file path is 'filters/myFilter.js'
, the main file (the one that lifts everything) may be:
let liftIt = require('lift-it'),
lift = liftIt('./api')
lift.use(liftIt.filters('./filters'))
let api = lift.lift()
api.run('item/create', {value: 10}, function (err, response) {
})
Custom plugins
A plugin is a function like function (action, lifter) {}
. That function is called once for every file that is found in the lifted folder. Creating your own plugin is that simple:
let myPlugin = function (action, lifter) {
if (action.name.indexOf('drop')) {
throw new Error('Sorry, we do not put up with dropping things...')
}
action.filters.push(function (body, success) {
setTimeout(success, 1e3)
})
action.postFilters.push(function (response, success) {
if (typeof response.status !== 'string') {
throw new Error('Response should have the status field')
}
success(response)
})
}
Warn: the lifter
object should not be modified. It should be treated as read-only.