A route-wrapper allowing use of async
/ await
syntax in Express
route controllers.
npm install route-async
Assuming you have some async helper function called someAsync
, you might have a route looking a bit like this:
const asyncRoute = require('route-async')
const someAsync = require('./helpers/someAsync')
const myRoute = async (req, res) => {
const result = await someAsync(req.body)
res.json(result)
}
module.exports = asyncRoute(myRoute)
The asyncRoute
wrapper simply takes your route and wraps it, such that the async
promise is either resolved internally, or if rejected a next
function is called. The default next
is just console.error
and you can supply your own.
Your route should not attempt to handle its own errors, but simply throw an Error
, or even better an HttpError
that gets caught by the async-route
wrapper.
This keeps your core route code much simpler.
The following example leverages mocha
, sinon
, and proxyquire
to unit test the above route by supplying a spy
in the place of the next
function.
const { expect } = require('chai')
const { spy, stub } = require('sinon')
const proxyquire = require('proxyquire')
describe('src/routes/myRoute', () => {
const mockSomeAsync = stub()
const myRoute = proxyquire('../../src/routes/myRoute', {
'./helpers/someAsync': mockSomeAsync
})
const req = { body: 'some body' }
const res = { json: stub() }
const next = spy()
const resetStubs = () => {
res.json.resetHistory()
next.resetHistory()
}
context('no errors', () => {
const result = 'some result'
before(async () => {
mockSomeAsync.resolves(result)
await myRoute(req, res, next)
})
after(resetStubs)
it('called someAsync with the right data', () => {
expect(mockSomeAsync).to.have.been.calledWith(req.body)
})
it('called res.json with the right data', () => {
expect(res.json).to.have.been.calledWith(result)
})
it("didn't call next", () => {
expect(next).not.to.have.been.called
})
})
context('has errors', () => {
const error = 'some error'
before(async () => {
mockSomeAsync.rejects(error)
await myRoute(req, res, next)
})
after(resetStubs)
it('called someAsync with the right data', () => {
expect(mockSomeAsync).to.have.been.calledWith(req.body)
})
it("didn't call res.json", () => {
expect(res.json).not.to.have.been.called
})
it('called next with the error', () => {
expect(next).to.have.been.calledWith(error)
})
})
})
Branch | Status | Coverage | Audit | Comments |
---|---|---|---|---|
develop |
Work in progress | |||
main |
Latest release |
- NodeJS. I use
nvm
to manage Node versions —brew install nvm
.
npm install
npm test
— runs the unit testsnpm run test:unit:cov
— runs the tests with code coverage output.
npm run lint
Please see the contributing notes.