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

@root/async-router

v1.0.14

Published

Write Express middleware and route handlers using async/await

Downloads

347

Readme

@root/async-router

A lightweight, zero-dependency JavaScript library to bring native Promises and async/await to Express.

express-async-router_14 1 1-youtube

Wraps the Express Router and provides a drop-in replacement to allow you to progressively enhance your routes with Promise and await support.

// Handle Async & Promise routes - and normal routes too!
app.get('/foo', async function (req, res) {
    // no more try/catch wrappers
    // no more 'unhandledRejection' errors
    let user = await UserService.findById(req.user.id);

    res.json(user);
});

Features

  • [x] API-compatible with express.Router()
    • [x] NO refactoring required!
  • [x] supports Promises
  • [x] supports async/await
  • [x] Proper error handling!
    • [x] No more unhandledPromiseRejectionWarning
    • [x] No more unhandledRejection
    • [x] No uncaught exception server crashes
  • [x] res.json() can be called automatically

Usage

TL;DR

Swap out app for the async router, and handle the server separately:

let app = require('@root/async-router').Router();

// ...

let server = express().use('/', app);
http.createServer(server).listen(3000, onListen);

Keep existing routes just the (ugly) way they are...

// yuck!
app.get('/profile', async function (req, res, next) {
    try {
        let results = await ProfileModel.get(req.user.id);
        res.json(results);
    } catch (e) {
        return next(e);
    }
});

Or delete the cruft to give them a facelift:

// yay!
app.get('/profile', async function (req, res) {
    let profile = await ProfileModel.get(req.user.id);
    res.json(profile);
});

Caveats

If you need to set express options, you'll move that down to the bottom as well:

let app = require('@root/async-router').Router();

// ...

let server = express().use('trust proxy', 1).use('view engine', 'pug').use('/', app);
http.createServer(server).listen(3000, onListen);

Also, if you do happen to have a few routes that explicitly res.json() in a callback after having returning a value, those would need to be updated - a very rare case, but I'm sure it exists in some code somewhere.

Full Example

'use strict';

let http = require('http');
let express = require('express');
let app = require('@root/async-router').Router();

// Handle Async & Promise routes
app.get('/foo', async function (req, res) {
    let user = await UserService.findById();
    if (!user) {
        throw new Error('User not found');
    }

    // Note: if you return a value,
    // res.json() will be called automatically
    // (however, just because you can doesn't mean you should)
    return user;
});

// Handles existing routes too - no refactoring required!
app.get('/foo', async function (req, res) {
    try {
        let user = await UserService.findById();
    } catch (e) {
        console.error('Unexpected');
        console.error(e);
        res.statusCode = 500;
        res.end('Internal Server Error');
    }

    if (!user) {
        res.statusCode = 404;
        res.json({ error: 'User not found' });
        return;
    }

    res.json(user);
});

// Handle errors (must come after associated routes)
app.use('/', function (err, req, res, next) {
    console.error('Unhandled Error');
    console.error(err);
    res.statusCode = 500;
    res.end(err.message);
});

// Start node.js express server
let server = express().use('/', app);
http.createServer(server).listen(3000, function () {
    console.info('Listening on', this.address());
});

Also, since it's useful to have this snippet for demos:

async function sleep(ms) {
    await new Promise(function (resolve) {
        setTimeout(resolve, ms);
    });
}

API

Router() - same as express.Router()

let app = require('@root/async-router').Router();

This is just a wrapper around express.Router(), which is what provides the default router and "mini apps" of express - so it has all of the same methods and function signatures:

app.use(path, middlewares);
app.route(path, minApp);
app.head(path, fns);
app.get(path, fns);
app.post(path, fns);
app.patch(path, fns);
app.delete(path, fns);
// ... etc

Any incompatibility should be file as a bug.

NOT an express server

It does NOT copy the top-level express server API. You should still use express for that:

// top-level options are still handled by the express server instance
let server = express().set('trust proxy', 1).set('view engine', 'pug').use('/', app);

require('http').createServer(server).listen(3000, onListen);

function onListen() {
    console.info('Listening on', this.address());
}

wrap(app)

The wrap(app) is the best way to add async/await support to your Express app or Router.

let syncApp = express.Router();
let app = require('@root/async-router').wrap(syncApp);

Alternatives

If you'd like to go dependency-free and use vanilla Express.js as-is, your best options are probably:

  1. Use a wrapper promise in each Handler

    app.use(function (req, res, next) {
        async function h() {
            let result = await DB.doStuff();
            res.json(result);
        }
        Promise.resolve().then(h).catch(next);
    });
  2. Wrap around each handler

    app.use(
        nextify(async function (req, res, next) {
            let result = await DB.doStuff();
            res.json(result);
        })
    );
    
    function nextify(fn) {
        return function (req, res, next) {
            async function h() {
                await fn(req, res, next);
            }
            Promise.resolve().then(h).catch(next);
        };
    }

LICENSE

Fork of express-promisify-router to bugfix error handling.

MIT License

See LICENSE.