Koa is a small framework that lets us create backend apps that run on the Node.hs platform.
In this article, we’ll look at how to create our Koa app with our own middlewares.
Middleware is a Building Block of a Koa App
Koa apps are made up of middlewares. We can define and use it by writing the following code:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
ctx.body = 'foo';
});
app.listen(3000);
In the code above, the async function is our middleware. In the function, we just set the body to 'foo'
so that we can see that displayed on the screen if we opening our browser.
Cascading Middleware
We can chain more than one middlewares together with the next
function, which is available from the parameter of the middleware function. It’s the 2nd parameter.
For instance, we can call it as follows:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', ms);
});
app.use(async (ctx, next) => {
ctx.body = 'Hello';
});
app.listen(3000);
In the code above, we have 2 middlewares, which we call one by one with passing each one to their own app.use
call.
In the first middleware, we set the X-Response-Time
response header by setting the ms
constant to the current time minus the start time.
To call the 2nd middleware, we call the next
function from the 2nd parameter of the middleware function.
In the second middleware, we set the response body of our route by setting ctx.body
to 'Hello'
.
Therefore, when we make a request to the /
route, we get that the body is ‘Hello’ and one of the response headers is X-Response-Time
with the number of milliseconds to process the request after the request is made.
Error Handling
We use error handling to catch errors and handle them. To listen to error events and handle them, we call the app.on
method to catch the error.
For instance, we can handle errors as follows:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
try {
await Promise.reject('error');
} catch (err) {
ctx.status = err.status || 500;
ctx.body = err.message;
ctx.app.emit('error', err, ctx);
}
});
app.on('error', (err, ctx) => {
console.log(err);
});
app.listen(3000);
In the code above, we have a try...catch
block to catch any errors that are thrown, which it’s the case since we have Promise.reject
.
Then we called ctx.app.emit
to emit the error event, which sends the err
object that has the rejection reason from the promise, along with the ctx
context object.
Then in the error handler that’s passed in as the 2nd argument as the app.on
method call, we log the error.
Now when we make a request to the /
route, we see that 'error'
is logged in the console.
Using ctx.throw
The ctx.throw
method lets us throw an error in our app. It takes the response code as the first argument and the error message as the 2nd argument.
For instance, we can use it as follows:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
ctx.throw(500, 'error');
});
app.on('error', (err, ctx) => {
console.log(err);
});
app.listen(3000);
In the code above, we called the ctx.throw
method with the error 500 and the error message 'error'
.
The error message will be available within the callback that we passed into the app.on
method. The error message will be available as the value of the message
property.
Using ctx.assert
Koa’s context object also has the ctx.assert
method that throws an error if the condition isn’t met. The condition is passed into the first argument of the method. The 2nd argument is the response code that we want to send if the condition isn’t met.
For instance, we can use it as follows:
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
ctx.assert(ctx.request.accepts('json'), 406);
ctx.body = 'hello';
});
app.on('error', (err, ctx) => {
console.log(err);
});
app.listen(3000);
In the code above, we have called the ctx.assert
method as follows:
ctx.assert(ctx.request.accepts('json'), 406);
Now if our request header has the Accept
request header that’s like the following:
application/json
Then we’ll get hello’ displayed on the screen.
Otherwise, if our request Accept
header is application/atom+xml
, then we’ll get the back a 406 response.
Conclusion
With Koa middlewares, we can build simple apps by writing one or more middleware. If we have more than one middleware, then we can chain them together by calling the next
function that’s available from the parameter of the middleware function.
We also use middlewares to handle errors. However, instead of passing them to the app.use
method, we pass them into the app.on
method.
Then we can use methods like ctx.assert
and ctx.throw
to throw errors, then the error gets sent to the error handler automatically.