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

apone

v1.1.0

Published

Configure and validate express routes with objects. Register routes in any order. Extend as necessary.

Downloads

7

Readme

Apone

Configure and validate express routes with objects. Register routes in any order. Extend as necessary.

Install and Quick Start

npm install apone

const Express = require('express')
const Apone = require('apone')

let app = Express()
let apone = new Apone(app)

apone.register({
  path: '/hello',
  method: 'get',
  handle: (req, res, next) => {

    res.send('world')
    return next()
  }
})

app.listen(3000)

table of contents

API

new Apone(app, options)

  • app - Express instance
  • options - object (optional)
    • prefix - string prepends all routes without prefixs
    • extensions - array of extensions
  • throws AponeValidationError for invalid options

register(routes, state)

  • routes - array|object
  • state - object (optional) if a route declares handle(state) this will be passed in (simple DI)
  • throws AponeValidationError for invalid or duplicate routes

Routes

By defining the following properties, routes opt-into different behaviors. Each property name resolves to a function and each property value is an argument to that function. Custom properties, or extensions, can be specified in Apone's constructor options.

Properties

  • method - string http verbs: GET, POST, PATCH, DELETE
  • path - string endpoint at which handle will be invoked (no regex allowed)
  • handle - function|array middleware OR function which returns middleware and accepting one potential argument. If an argument is specified, state (from the register method) is passed in
  • validation - object object with up to three properties, each a valid schema
    • params - Schema
    • query - Schema
    • body - Schema
  • prefix - string path prefix which takes priority over Apone's constructor option global prefix
  • metadata - object - flexible bucket for anything else

example:

  let route = {
    method: 'PUT',
    path: '/puppers/:id',
    metadata: {
      favorite: true
    },
    prefix: '/api/v2',
    validation: {
      params: Joi.object().keys({ id: Joi.number().integer().min(1).max(1).required() }),
      body: {
        validate: (query) => {

          // can coerce a more complicated type here, and return valid payload only
          if (query.name && typeof query.name === 'string') {
            return { value: { name: query.name } }
          }
          return new { error: Error('name is required!') }
        }
      }
    },
    // injected dependency makes for easy testing
    handle: (state) => [

      // if you regularly need internalOnly, create an extension!
      state.internalOnly,
      (req, res, next) => {

        res.send(state.pupperRepository.update(req.body))
        return next()
      }
    ]
  }

Schemas

Schemas are used to validate user requests. Joi is reccomended, but any schema can be used. Coerce and sanitize requests here.

Properties

example:

  let schema = {
    validate: (body) => {

      if (body.id === 1) {
        return { value: { id: body.id } }
      }
      return { error: new Error('value was not 1!') }
    }
  }

ValidationResult

Returned by schema validation

Properties

  • error - Error object
  • value - any coerced and sanitized result

example:

  let validationResult = {
    error: isValid ? null : new Error(),
    value: isValid ? value : null
  }

Extensions

Extensions are custom route properties. They must be defined in the Apone constructor options and will execute in their array order, according to request behavior.

Properties

  • name - string used by routes to opt into this extension behavior
  • factoryFunc - function invoked by Apone during route registration, returning middleware which is inserted into request behavior.
  • type - string defaults to pre, or optionally post.

example:

The following extension adds a traceId to each request prior to validation. First, Apone is instantiated with the trace extension. During registration, trace(true) is called and the return function is added to the stack. Extensions execute in order, by type.

const Express = require('express')
const Apone = require('apone')

let app = Express()
let extensions = [{
  name: 'trace',
  factoryFunc: (doTag) =>  (req, res, next) => {

    if (doTag) {
      res.locals.traceId = Math.random()
      return next()
    }
    else {
      next()
    }
  }
}]

let apone = new Apone(app, { extensions })

apone.register({
  path: '/hello'
  method: 'get',
  trace: true,
  handle: (req, res, next) => {

    console.log(res.locals.traceId) // 0.23456...
    res.send('world')
    return next()
  }
})

Route Behavior

The lifecycle of middleware steps assembled by Apone for routes

  1. Apone appends the finished route object to res.locals.route for logging, etc
  2. pre extensions
  3. request param validation
  4. request query validation
  5. request body validation
  6. handle middleware
  7. post extensions

Contributing

Contributions are welcome.

Reporting Bugs

Please open an issue and describe the situation clearly. Include reproduction instructions, expected outcomes, and project version number.

Pull Requests

  • One feature per request
  • Descriptive commit messages
  • Test coverage is manditory
  • Pass project linter

FAQ

  1. If I use this with an existing application will the dupe check work
  • Not currently
  1. Any plans to expand the framework?
  • Apone was designed to be small and flexible, but feel free to open an issue
  1. What about express-router
  • You can probably live without it. This is a simple alternative.
  1. How do I pronounce it