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

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