├── README.md ├── package.json └── index.js /README.md: -------------------------------------------------------------------------------- 1 | ## express-async-router 2 | 3 | ```js 4 | const router = require('express-router-async')(); 5 | router.getAsync(async (req, res) => { 6 | const wow = await makeSomethingAmazing(); 7 | res.json(wow); 8 | }); 9 | ``` 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@replit/express-router-async", 3 | "version": "0.4.0", 4 | "description": "Creates an express router augmented with promise handling routes", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "express", 11 | "router", 12 | "async", 13 | "await" 14 | ], 15 | "peerDependencies": { 16 | "express": "4.x" 17 | }, 18 | "repository": "https://github.com/replit/express-router-async", 19 | "author": "Amjad Masad", 20 | "license": "MIT" 21 | } 22 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Router = require('express').Router; 2 | 3 | /** 4 | * A helper for `createNamedAsyncMiddleware` 5 | * If the function already has a name, we'll just use that and tack 6 | * `Async` on the end. If the function doesn't have a name we'll 7 | * use the path for the target handler plus the index of the current 8 | * middleware. 9 | */ 10 | function getFunctionName(fun, handlerPath, middlewareIndex) { 11 | if (fun.name && fun.name !== 'anonymous') { 12 | return fun.name + 'Async'; 13 | } 14 | 15 | // Path can be a string, regex, or array of either 16 | const strPath = handlerPath.toString(); 17 | 18 | return strPath + middlewareIndex; 19 | } 20 | 21 | /** 22 | * Create a middleware which calls the original function but catches 23 | * unhandled promise rejections (either from async functions or returned promise) 24 | * and passes the error to `next` which will call the next error handler middleware. 25 | * 26 | * For better stack traces we want to make sure that the functions are 27 | * named function for each middleware the simplest to have a dynamic 28 | * function.name is to initialize it in an object with the key being 29 | * the dynamic function name 30 | */ 31 | function createNamedAsyncMiddleware(fun, handlerPath, middlewareIndex) { 32 | const functionName = getFunctionName(fun, handlerPath, middlewareIndex); 33 | 34 | return { 35 | [functionName]: (req, res, next) => { 36 | // Execute the function 37 | const p = fun(req, res, next); 38 | 39 | // If the result isn't a promise 40 | if (!p || typeof p !== 'object' || !p.then || !p.catch) { 41 | console.error( 42 | 'Expected then middleware number %d for route %s to return a promise', 43 | i, 44 | req.url, 45 | ); 46 | 47 | return; 48 | } 49 | 50 | // catch the error and pass it to the next error handling middleware 51 | const pp = p.catch(next); 52 | if (pp.done) { 53 | // Terminate promise for bluebird and Q 54 | pp.done(); 55 | } 56 | }, 57 | }[functionName]; 58 | } 59 | 60 | function createAsyncRouter(options) { 61 | const router = new Router(options); 62 | 63 | function createHandler(method) { 64 | return function handler(path, ...rest) { 65 | const asyncRest = rest.map((fun, i) => 66 | createNamedAsyncMiddleware(fun, path, i), 67 | ); 68 | 69 | return router[method](path, ...asyncRest); 70 | }; 71 | } 72 | 73 | router.getAsync = createHandler('get'); 74 | router.postAsync = createHandler('post'); 75 | router.deleteAsync = createHandler('delete'); 76 | router.putAsync = createHandler('put'); 77 | 78 | return router; 79 | } 80 | 81 | module.exports = createAsyncRouter; 82 | --------------------------------------------------------------------------------