@pandapaul/api-handler v0.14.0
api-handler
Installation
$ npm install @pandapaul/api-handlerUsage Overview
const apiHandler = require('api-handler')
const middleware = apiHandler.http(promiseyApiMethod)
const rejection = apiHandler.reject(404) || apiHandler[404]()
const resolution = apiHandler.resolve(201) || apiHandler[201]()
const logger = apiHandler.logger('logFile.log')
const inputIsValid = apiHandler.validate('someInput', /expectedFormat/)
apiHandler.require('someInput', /expectedFormat/)apiHandler.http(promiseyApiMethod)
http turns a promisey API method into an Express middleware.
Here's an example promisey API method that requires a query parameter called name in order to say hello. It rejects if there's no name provided and resolves otherwise with a template string.
const sayHelloInAPromise = (req) => {
if (!req.query.name) {
return Promise.reject(new Error('name is required'))
}
return Promise.resolve(`Hello ${req.query.name}`)
}With apiHandler.http() we can turn it into a middleware.
const sayHelloMiddleware = apiHandler.http(sayHelloInAPromise)And then we can use it like this
const app = require('express')()
app.get('/hello', sayHelloMiddleware)If you resolve or reject with an object having a numeric status property, that status will be set as the HTTP response status.
const sayHelloInAPromise = (req) => {
if (!req.query.name) {
return Promise.reject({ status: 400 })
}
return Promise.resolve({ status: 200})
}When rejecting, a message may be included to respond with an error message.
const sayHelloInAPromise = (req) => {
if (!req.query.name) {
return Promise.reject({ status: 400, message: 'name is required' })
}
return Promise.resolve({ status: 200})
}And when resolving, a data property may be included to respond with some data.
const sayHelloInAPromise = (req) => {
if (!req.query.name) {
return Promise.reject({ status: 400, message: 'name is required' })
}
return Promise.resolve({ status: 200, data: `Hello ${req.query.name}`})
}apiHandler.reject(obj|status|message)
reject is a factory for creating useful promise rejections that are instances of Error.
It accepts an object
const missingRequiredName = apiHandler.reject({ status: 400, message: 'name is required' })Or a numeric status
const notFound = apiHandler.reject(404)Or a message string
const genericRejection = apiHandler.reject('there was a problem')We can leverage reject in conjunction with http to do some nice rejection handling.
const sayHelloInAPromise = (req) => {
if (!req.query.name) {
return apiHandler.reject({ status: 400, message: 'name is required' })
}
return Promise.resolve(`Hello ${req.query.name}`)
}
app.get('/hello', apiHandler.http(sayHelloInAPromise))When the name query parameter is missing, we'll get a 400 HTTP response with our message while adhering to Promise rejection guidelines.
apiHandler.resolve(status|any)
resolve is a factory for creating promise resolutions. Its use is not required but it offers some convenience.
It accepts a numeric status
const created = apiHandler.resolve(201)And anything else will just be passed along to Promise.resolve()
const someStuff = apiHandler.resolve({ some: 'stuff' })apiHandler[status](message|data)
For every HTTP status code included in node's built in http library, there is a corresponding method on apiHandler that acts as a resolve() or reject() depending on the status.
For example, rejecting with a 404 can be accomplished like this
const notFound = apiHandler[404]()And an error message may be provided
const badRequest = apiHandler[400]('missing input')Similarly, resolving with a 201 can be accomplished like this
const created = apiHandler[201]()And result data may be provided
const createdWithData = apiHandler[201]({ some: 'thing' })apiHandler.validate(actual, expected|regex|validator)
validate will check actual values against expected values, types, regular expressions, or validator functions.
Without a second argument, it simply checks truthiness.
apiHandler.validate('') // false
apiHandler.validate('something') // trueWith an expected value, strict equality is checked.
apiHandler.validate(3, 3) // true
apiHandler.validate(3, '3') // false
apiHandler.validate('string', 'string') // trueTypes can be checked.
apiHandler.validate(100, String) // false
apiHandler.validate(100, Number) // trueRegular expressions are sure handy.
apiHandler.validate('something', /something/) // true
apiHandler.validate('something', /nothing/) // falseIf you need to get fancy, pass a validator function.
apiHandler.validate('word', item => item.indexOf('p') > -1) // false
apiHandler.validate('word up', item => item.indexOf('p') > -1) // trueObjects can be checked as well.
apiHandler.validate({ stuff: { things: 'yep' } }, { stuff: { things: 'yep' } }) // true
apiHandler.validate({ stuff: { things: 'yep' } }, { stuff: { things: 'nope' } }) // falseMix and match to create a schema.
const data = {
aNumber: 100,
aString: 'something',
nest: {
anArray: [1, '1']
}
}
const schema = {
aNumber: Number,
aString: /something/,
nest: {
anArray: [Number, String]
}
}
apiHandler.validate(data, schema) // trueapiHandler.require(actual, expected, message, optional)
require wraps validate and throws 400 errors when things are invalid.
// Throws an Error with { message: 'should be a string', status: 400 }
apiHandler.require(100, String, 'should be a string')When a message is not provided, a simple one will be generated when a key is available.
// Throws an Error with { message: 'thing is invalid', status: 400 }
apiHandler.require({ thing: 'something' }, { thing: 'somethingElse' })
// Throws an Error with { status: 400 }
apiHandler.require('something', 'somethingElse')To skip missing parameters, set optional to true.
// Doesn't throw an Error
apiHandler.require({}, { thing: 'somethingElse' }, null, true)
// Throws an Error
apiHandler.require({}, { thing: 'somethingElse' })This works well for request validation when used in conjunction with http.
const app = require('express')()
const sayHelloInAPromise = (req) => {
apiHandler.require(req.query, { name: String })
return Promise.resolve(`Hello ${req.query.name}`)
}
app.get('/hello', apiHandler.http(sayHelloInAPromise))apiHandler.accept(actual, expected, message)
accept is shorthand for require with the optional flag set to true.
// Using `accept` like this
apiHandler.accept('actual', 'expected')
// Is the same thing as
apiHandler.require('actual', 'expected', null, true)apiHandler.logger(filename)
logger is a middleware for logging JSON-formatted request and response information.
const logger = apiHandler.logger('logFile.log')
app.get('/hello', logger, someOtherMiddleware)someOtherMiddleware will be unaffected by the presence of the logger, but we'll get a new line in our logFile.log with JSON describing what occurred.
The log filename is optional. If it is omitted, you'll get a warning message but things will just be written to process.stdout.
const logToStdOut = apiHandler.logger()