@pandapaul/api-handler
v0.14.0
Published
Generic api handling middleware.
Downloads
187
Readme
api-handler
Installation
$ npm install @pandapaul/api-handler
Usage 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') // true
With an expected value, strict equality is checked.
apiHandler.validate(3, 3) // true
apiHandler.validate(3, '3') // false
apiHandler.validate('string', 'string') // true
Types can be checked.
apiHandler.validate(100, String) // false
apiHandler.validate(100, Number) // true
Regular expressions are sure handy.
apiHandler.validate('something', /something/) // true
apiHandler.validate('something', /nothing/) // false
If 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) // true
Objects can be checked as well.
apiHandler.validate({ stuff: { things: 'yep' } }, { stuff: { things: 'yep' } }) // true
apiHandler.validate({ stuff: { things: 'yep' } }, { stuff: { things: 'nope' } }) // false
Mix 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) // true
apiHandler.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()