npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

lift-it

v7.1.0

Published

Forget about maintaining routes files, use dynamic routes created from files in a folder

Downloads

38

Readme

Lift It

Build Status Inline docs Dependency Status

Forget about maintaining routes files, use dynamic routes created from files in a folder

Install

npm install lift-it

Basic usage

// Lift the 'api' folder
let api = require('lift-it')('./api').lift()

// Execute the file ./api/item/create.js
api.run('item/create', {
	name: 'Water',
	value: 17
}, function (err, response) {
	// Always async
})

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':

// `body` is the argument passed by 'run' (in this case, `{name:'Water',value:17}`)
// `success` and `error` are callback functions, learn about it here:
// https://github.com/clubedaentrega/run-it
module.exports.handler = function (body, success, error) {
	// Item.create represents an async job
	Item.create(body, error(function (item) {
		// Answer with {id: '54bd55fc13e53ce1077415a2'}
		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:

// Express
let app = require('express')()
app.use(require('body-parser').json())

// Lift
let api = require('lift-it')('./api').lift()

// Set routes
api.actions.forEach(function (action) {
	// `action` is an object. See the docs bellow
	
	app.post(action.name, function (req, res) {
		
		// action.run(...) is the same as api.run(action.name, ...)
		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 = {
	// The export property name
	exportName: 'fields',
	// Whether to error out if the file does not export it
	optional: false,
	// Check input or output
	direction: 'input',
	// Which argument to validate
	// The default will check the first
	position: 0,
	// A function to return a value if the desired input/output is not present
	getDefaultValue: function () {
		throw new Error('Insufficient number of arguments')
	},
	// If lifter.enableErrorCode is true, which code to use in error(code, msg)
	code: 101,
	// If lifter.enableErrorCode is true, which code to use in error(HTTPStatusCode, code, msg)
	HTTPStatusCode: 500,
	// A function to handle error cases
	errorHandler: function (action, value, err) {
		// `action` is an Action instance
		// `value` is the offending value
		// `err` is a lifter.errorClass instance
		// `err.code` is `options.code`
		throw err
	},
	// Let you define your own custom types
	// See doc on validate-fields module for that
	defineTypes: function (validate) {},
	// Options passed to schema.validate() of validate-fields
	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) {
	// err is new Error('I was expecting a value in name')
})

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) {
	// Check auth, read the DB, etc
	success('Some data I got elsewhere')
}
module.exports.double = function (body, success, error) {
	// Simply change the body input
	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) {
	// body.value will be doubled
	// moreData will be the string create by the first filter
	// ...
}

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) {
	// Do something with the action, like checking something
	if (action.name.indexOf('drop')) {
		throw new Error('Sorry, we do not put up with dropping things...')
	}
	
	// Add a filter to every action to delay them by 1s
	action.filters.push(function (body, success) {
		setTimeout(success, 1e3)
	})
	
	// Add a post filter to check something
	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.