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

express-async-api

v0.7.0

Published

A wrapper on top of express.js for simple API creation.

Downloads

27

Readme

express-async-api

A minimalistic wrapper around express, limiting the available feature set to write explicit, JSON-only APIs.

Explicity is the key, that is why routers and middlewares are not available through this wrapper lib.

  • You will have to explicitly define your routes so you will know exactly what route params are available
  • Instead of middlewares, you will have to explicitly call functions in the handler function, so you will exactly know what happens

Besides that, a fundamental assumption is that your route handlers will be asynchronous. If you use async and await, it enables you to simply return the result you want to send back to the client, and you can throw an exception if something went wrong, and that will be handled on a separate error handler.

I built this wrapper, because recently I only use these features of express. Later on, I am going to write a lib with the same interface, that does not depend on express, but on the native http lib of Node.js. Since the interface will be the same, it will be easy to swap this lib with the new one that has no dependencies.

Installation

npm i --save-dev express-async-api

Usage

import createApiServer from 'express-async-api'

import routes from './routes/index.js'

...

function errorHandler(e) {
  // the thrown error will be passed to this function
  // based on the error, you should construct and return the output
  return {
    status: 500,
    error: {
      name: e.name,
      message: e.message
    }
  }
}

function loggerCb(req, res) {
  // it gets express' req and res as params, so you can log everything whereever you want.
  // you can even aggregate your logs in a separate service
}

const apiServer = createApiServer(errorHandler, loggerCb)

routes(apiServer) // this function should append the route handlers to your server

apiServer.listen(port)

The underlying express server is exposed on the apiServer._expressServer property, for testing purposes. See some examples for testing in this very repository.

apiServer.listen(port)

Starts listening on the given port.

apiServer.get(route, handler)

apiServer.get('/v1/group/:groupId/user/:id', async req => {
  // these functions have access to the original express req object
  const response = await fetchGroupUser(req.params.groupId, req.params.id) // if the promise is rejected (and you don't handle it locally), then the global errorHandler will be invoked

  return {
    status: 200, // you can control the status code of the response by this field
    result: {
      ...response
    }
  }
})

apiServer.post(route, handler)

apiServer.post('/your/:route/is/here', async req => {
  return {
    status: 201,
    result: { ... }
  }
})

apiServer.postBinary(route, settings, handler)

The settings object must conftain the following properties:

  • fieldName (string): the name of the input field from which the server ready the binary data. (The name of the form field on the frontend that holds the binary.)
  • mimeTypes (array of strings): containing the allowed mime types. If the uploaded file's mime type is not in the array, then a ValidationError will be thrown from the standard-api-errors lib, which translates to a HTTP 400 respones.
  • maxFileSize (optional, number): the maximum file size limit in bytes.

The maxFileSize property is optional and specifies the maximum file size allowed for upload. If not provided, there is no size limit imposed on the uploaded file

const settings = {
  fieldName: 'avatar',
  mimeTypes: ['image/png', 'image/jpeg', 'image/gif']
}

apiServer.post('/your/:route/is/here', settings, async req => {
  return {
    status: 201,
    result: { ... }
  }
})

apiServer.put(route, handler)

apiServer.put('/your/:route/is/here', async req => {
  return {
    status: 200,
    result: { ... }
  }
})

apiServer.patch(route, handler)

apiServer.patch('/your/:route/is/here', async req => {
  return {
    status: 200,
    result: { ... }
  }
})

apiServer.delete(route, handler)

apiServer.delete('/your/:route/is/here', async req => {
  return {
    status: 201,
    result: { ... }
  }
})

Redirects

You can also redirect in your handlers.

async function handler(req) {
  ...
  return {
    status: 302,
    redirect: 'https://example.com'
  }
}

Binary response

If you want to respond with binary data.

async function handler(req) {
  ...
  return {
    binary: <Node JS Buffer ...>
  }
}

Errors & Logging

If you take a look at the first example, you can see, that the createApiServer function has two parameters. The first is an error handler callback, the second is a callback for logging.

The error handler callback gets the thrown error as an argument, based on which, you can put together the final output that your server produces. You must implement the error handler function.

The log callback is optional. It recieves the original express req and res objects, which contains everything about the request and the response. You can aggreagate these in a log service, or just simply console.log something.

Upcoming features

  • custom error handlers for routes
  • execution time -> log function