app.get("/", (request, response, next) => { database.doStuff((error, firstResult) => { // will send it the error middleware below if (error) return next(error); // we need another database call // a little bit of callback hell here but: // – we could have extract our callback to functions… // …living on the first level of our route handler database.doAnotherStuff(firstResult, (error, finalResult) => { // will send it the error middleware below. Again. if (error) return next(error); // no error, send the result response.json(finalResult); }); }); }); // our middleware that handle any errors // – will catch anything that might have happened in our route // only if called with next(error) // – won't catch an JSON.parse() error app.use(functionerrorMiddleware(error, request, response, next) { response.status(500); response.send(error); });
So far so good. But luckily for us our database object support also promises.
promises
The following will do the same as the code above but:
You see? We write again and again try {} catch(error){ next(error) } Not a big deal but quite boring at the end… But luckily we can write a wrapper function for that!
// make a closure to keep a reference to our original async function functionasyncWrapper(asyncRouteHandler) { // this is what will be called by express.js returnfunctionrouteHandler(request, response, next) { // because it's an async function it will always return a promise // – just call it with express' callback parameters return ( asyncRouteHandler(request, response, next) // catch any error that might happen in our async function .catch(next) ); }; } // OR: // thanks to arrow functions and params destructuring // we can write it that way: const asyncWrapper = fn =>(...args) => fn(...args).catch(args[2]);
A more detailed article about that was written by Alex Bazhenov
So far so good. But we still have to write some boilerplate to handle that… Here comes KOA!
KOA
what is KOA?
to sum up: it’s the same team behind express.js that have written a web framework using the recent additions in the Javascript language. At its core it’s using promises with async/await You can find the full introduction here
Setting up a server with Koa is very straightforward. For the routing, as nothing is provided by default, we will use koa-router
const app = new Koa(); const router = new Router();
// unlike in Express.js // – declare our error middleware to the top most position // – this will ensure to catch all the errors // that might happen in the following middleware call app.use(asyncfunctionhandleError(context, next) { // call our next middleware try { await next(); // catch any error that might have occurred } catch (error) { context.status = 500; context.body = error; } });
/* * We will configure here our router later */
// mount the router to our web application app.use(router.routes()); app.use(router.allowedMethods());
// launch the server app.listen(3000);
writing our routes
And this is how we will write our application code:
no need to wrap all our route handlers into that middleware
handle both sync/async errors
About Koa ecosystem
As for now, Koa hasn’t as much middleware as express.js. This can be an issue in migrating.
But the must have middlewares are already here, and writing your own is quite easy. I never found myself in a situation where I couldn’t achieve what I wanted to do with Koa.
So if you like async/await code style, give Koa a try 🙂