@pandapaul/api-handler

0.13.0 • Public • Published

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()

Readme

Keywords

none

Package Sidebar

Install

npm i @pandapaul/api-handler

Weekly Downloads

1

Version

0.13.0

License

MIT

Unpacked Size

28.2 kB

Total Files

22

Last publish

Collaborators

  • pandapaul