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

aileron

v5.2.1

Published

Minimal URL matching for NodeJS

Downloads

932

Readme

Aileron

Build Status

Aileron simplifies an API server to these steps:

  • The connect server maps URL pattern strings to handler functions.
  • If the handler returns a value (typically a JSON object), this is sent as a 200 response.
  • If the handler throws an error, this is sent as a 500 response.

Additional features:

  • URL pattern strings support :wildcards, which are useful for specifying IDs in the URL string for example.
  • You can specify different handler functions for different request methods (GET, POST, PUT, PATCH, DELETE).
  • You can customize the centralized successHandler and errHandler functions to perform tasks like logging, specifying different status codes etc.
  • You can specify type definitions for inputs to APIs. Aileron will check requests and reject incorrect inputs with a 409 response. This response can also be customized through a centralized badInputHandler function.

Middlewares:

  • For tasks like authentication, we require a way to create a "gatekeeper" functions, that allow only some requests through.
  • For this, aileron allows you to define a middleware.
  • Middleware are different from normal routes in two ways:
    • You can use middleware when you only want to match against the beginning of the URL, rather than exact matches.
    • When a middleware handler function returns, we don't send a response, we call next(), sending the request forward along the connect server chain.

Typical Use

const connect = require("connect")
const aileron = require("aileron")

const { router, middleware } = aileron()

const teamDetails = {
  get: {
    errMsg: "Unable to retrieve team details"
    handler: async (req, data) => {
      const teamDetails = await getTeamDetails(data.teamId)
      return { id: data.teamId, teamDetails }
    }
  },
  put: {
    errMsg: "Unable to update team details"
    handler: async (req, data) => {
      const result = await updateTeamDetails(data.teamId, data.teamList)
      return result
    }
  }
}

const authMiddleware = {
  errMsg: "Unauthorized request",
  handler: (req, data) => {
    const isAuthorized = await authorize(req)
  }
}

let app = connect()

app
  .use(middleware("/api/:apiVersion"), authMiddleware)
  .use(router("/api/:apiVersion/team/:teamId", teamDetails))

Router

router(urlFormat, routeConfig)
  • urlFormat is a string URL, where you can have :wildcard placeholders by prefixing a :
    // urlFormatExample
    "/api/:apiVersion/authenticate"
  • routeConfig is an object containing a handler for each supported request method.
    // route config example
    const routeConfig = {
      post: // Request method
        {
          inputs: // Input type definitions,
          errMsg: // Error message string,
          handler: (req, data) => {
            // Function that returns a value.
            // Returned value is passed to the successHandler which sends a response
            // If an err is thrown, it is passed errHandler which sends a response
          }
        }
    }
  • Each handler receives (req, data)
  • If the URL exactly matches the urlFormat, the handler for the corresponding req.method is called.
  • If the matching fails, next() is called.
  • Each route allows you to specify the inputs it receives and their types. If inputs are missing / incorrect, aileron will automatically invoke badInputHandler with a detailed error object. For advanced input validation, see the Input Checking section.
  • Each handler function is passed a data parameter. This will contain the wildcard values and the parsed inputs, ready for use.

For example:

const loginApi = {
  post: {
    inputs: { username: "String", password: "String" },
    errMsg: "Unable to login. Please try again!",
    handler: async (req, data) => {
      const userDetails = await loginUser(data.username, data.password, data.apiVersion)
      return {message: "Login successful", userDetails}
    }
  }
}

let app = connect()

app
  // Other routes and middleware
  .use(...)
  .use(...)
  // The team route
  .use(router("/api/:apiVersion/team/:teamId", teamApi))
  // Other routes and middleware
  .use(...)
  .use(...)
  • Note that the URL format that you provide decides the key (apiVersion) under which the variable is made available to the handler function.

Middleware

middleware(urlFormat, routeConfig)

Very similar to router, so we only explain the differences:

  • If the beginning of the URL matches the provided urlFormat, the middleware function is called.
  • If the handler function returns, next() is called.
  • If the handler function throws, the errHandler is called.

For example:

const printRequestInfo = (req, data) => {
  console.log(req.method, req.url, data.apiVersion)
}

let app = connect()

app
  .use(middleware("/api/:apiVersion", printVersionNumber))
  // other middleware / routes follow
  .use(...)
  .use(...)
  • Note that the URL format that you provide decides the key (apiVersion) under which the variable is made available to the middleware function.

Input checking

  • As mentioned above, aileron supports input checking by simply configuring an "inputs" object and an error message.
  • Aileron uses the type-check library to validate inputs. Check the library docs for a list of valid type definitions.
  • For advanced input checking, aileron allows you to define an inputCheck function. This function receives all the parsed inputs specified in your inputs object. Simply throw an error inside this function and badInputHandler will be called with the thrown error.
  • For optional inputs, you can use ? to specify they're optional. {age: "Number | Undefined"} can be written as {age: "Number?"}
const inputCheckingController = {
  post: {
    inputs: { name: "String", age: "Number" },
    inputCheck: parsedInputs => {
      // Custom check to disallow the name "Jon Snow"
      if (parsedInputs.name === "Jon Snow") {
        throw "You know nothing, Jon Snow"
      }
    },
    errMsg: "Unable to process your request.",
    handler: (req, res, next, data) => {
      const { name, age } = data
      res.ok().json({ name, age })
    }
  }
}

Error handling

  • Aileron automatically wraps all your middlewares / handlers in a try-catch block and sends an error response if an uncaught error occurs.
  • This prevents your node server from crashing :)
  • Aileron allows you customize these error handlers.
  • You can supply an errHandler and a badInputHandler when you initialize Aileron.
  • Here is an example:
// MyCoolProject
const { router, middleware } = aileron({
  badInputHandler: (req, res, err, errMsg) =>
    res.forbidden().json({ err, message: "Bad Input: " + errMsg }),
  errHandler: (req, res, err, errMsg) =>
    res.error().json({ err, message: "Uncaught error!!" })
})

Strict Mode

  • Aileron allows a strict mode, where it will force you to provide inputs and errMsg for all handlers.
  • Strict mode will also complain if you have not supplied custom error handlers.
  • This ensures that error handling code is always supplied for every API in your project.
  • To enable strict mode, simply supply it as an option when you initialize aileron.
// MyCoolProject
const { router, middleware } = aileron({
  strict: true,
  badInputHandler: (req, res, err, errMsg) =>
    res.forbidden().json({ err, message: "Bad Input: " + errMsg }),
  errHandler: (req, res, err, errMsg) =>
    res.error().json({ err, message: "Uncaught error!!" })
})