When it comes to web development, chances are you’re going to pick up a framework to get your backend API up and running. Aside from Node having good built-in mechanisms for the construction of web servers, frameworks, on the other side, facilitate this job by providing nice features like middleware, routing mechanisms, etc.
Express is by far the leader in terms of web development in the Node.js universe. It’s also the oldest, created back in 2009, which is perhaps one of the reasons that made it mature enough to be so beloved by the community.
Koa.js is another great option that’s been gaining more and more space. But they are actually not competitors. Koa was designed by the same team behind Express, which is a common trend in the frontend community, take Deno, for example, whose creator was the same mind behind Node.
Koa was created to be smaller, faster, and more expressive in terms of coding. When it comes to the development of web APIs, Express is great, but it lacks a bit of expressiveness and still leads us to commit some boilerplate code that could be avoided, as we’re going to see in practice in a few minutes.
It was designed with a big focus on async functions, making them simpler so you can manipulate callbacks and handle the errors of your code flows more easily.
At the end of this post, we will have an app that retrieves information from a Koa-based API using some nice features of the framework. The mock data is going to be retrieved from the fake data generation website called https://randomuser.me/. So, let’s dive into it!
Like everything else in the Node universe, setting things up is fast and easy. Make sure that you have Node installed, choose a folder of your preference for the development of this example, and run the following commands into it:
npm init npm i koa koa-router koa-ejs axios
Fill in the information that the command line is going to ask you about your Node project. I left all of them blank, but feel free to inform anything that you want.
The first three dependencies are Koa-related, being the first one at the heart of the framework.
The koa-router is the Express routing system equivalent. Yes, it comes within a second package that needs to be added to your package.json separately.
Koa-ejs is a bonus. It is a Koa middleware that adds support to all features of the ejs project. This way, we can embed JavaScript templates within our backend web app without the need for a client project. In other words, it renders the HTML output of our web pages.
Finally, the famous Axios library is here because we need to call an external API, the randomuser.me.
Now, go ahead and create a new index.js file at the root of your project. Place the following code into it:
const koa = require("koa"); const path = require("path"); const render = require("koa-ejs"); const koaRouter = require("koa-router"); const axios = require("axios"); const app = new koa(); const router = new koaRouter(); render(app, { root: path.join(__dirname, "views"), layout: "index", viewExt: "html", }); router.get("hello", "/", (ctx) => { ctx.body = "<h1>Hello World, Koa folks!</h1>"; }); router.get("users", "/users", async (ctx) => { const result = await axios.get("https://randomuser.me/api?results=5"); return ctx.render("index", { users: result.data.results, }); }); app.use(router.routes()).use(router.allowedMethods()); const PORT = process.env.PORT || 3000; app.listen(PORT, () => console.log(`running on port ${PORT}`));
The first few lines of this listing relate to the imports of the dependencies we’ve just installed. Both the main Koa and its router objects need to be instantiated through the new operator.
The render function, from koa-ejs, is here to delineate the parameters of our ejs template system (still to be built). The first param it receives is the Koa app object recently created, and the second is another object containing information like the root folder in which the ejs templates are placed, the layout main file, and the extension of those files (HTML). This is the most basic configuration, but you can find the other available parameters here.
Next, we get two endpoints created. The first is just a simple Hello World mapped to the root endpoint, while the second is a bit more complex and returns the list of random users attached to the template input.
Notice how similar it is to route endpoints on Koa and Express. The first param asks for a canonical name for your endpoint, and the second requires the path string that maps to this endpoint. The main difference is within the callback function (the third param) that deals with the response handling, which is manipulated by the ctx object, the Koa Context.
The Koa Context embraces both Node’s request and response objects into a single one, which simplifies the approach, differently from what Express does. Since they’re used so frequently, the Koa creators found that would be better to stick together:
app.use(async ctx => { ctx; // This is the context ctx.request; // This is a Koa request ctx.response; // This is a Koa response });
There’s no need, however, to always call the request or response objects individually to access their properties. If you want to access the request’s body, for example, just do it directly:
ctx.body; // It delegates to Koa’s ctx.request
Koa magically delegates the access of any property to their respective equivalents, just for convenience.
Koa also allows you to decide if you want to work with async functions or not. Let’s take the second endpoint as an example. Note that we’re calling an external URL within its callback function to get the random users array and send it directly to our koa-ejs template (we’ll build it in a few minutes).
This is a much cleaner approach compared to the promise-based Express one. With Express, we’d have something like that:
app.get('/users', (req, res, next) => { axios.get("https://randomuser.me/api?results=5").then((result) => { res.status(200).json(result.data.results); }).catch((err) => next(err)); });
It may look okay at first sight, because the example is simple. However, if your development gets more complex, like with the adding of a data retrieval from a database, things can become tricky:
app.get('/users', (req, res, next) => { MyDatabaseObj.findByPk(myPk).then(data => if (!data) { return res.status(404).send({}); } axios.get("https://randomuser.me/api?results=5&id=" + data.id).then((result) => { res.status(200).json(result.data.results); }).catch((err) => next(err)); ); });
This style can lead developers to nest many code blocks, which are hard to maintain.
Back to the index.js code, the rest of it is very similar to what you do with Express. The Koa middleware must be stated via use()
method. To get the server up and running, just start it via listen()
method. Very similar, isn’t it?
Now that we’ve finished with the API creation, let’s move on to the ejs template. First, you need to create a new folder called views at the root of your project. And inside of it, create the main file called index.html.
Since breaking down the template system in minimal pieces is not the focus of the article, we’re going to stick to a single file as our template. This is the code you should add to it:
<html> <head> <title>First Steps with Koa</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" /> </head> <body> <div class="container"> <div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center"> <h1 class="display-4">Koa.js</h1> <p class="lead"> A simple list of users retrieved from <a href="https://randomuser.me/">randomuser.me</a> </p> </div> <div class="list-group"> <% users.forEach( function(user) { %> <div class="list-group-item"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1"> <%= user.name.title %> <%= user.name.first %> <%= user.name.last %> </h5> <small >Registered at: <%= new Date(user.registered.date).toISOString().split('T')[0] %></small > </div> <p class="mb-1"> <strong>Address:</strong> <%= user.location.street.number %> <%= user.location.street.name %>, <%= user.location.city %> <%= user.location.postcode %> - <%= user.location.country %> </p> <small>Phone: <%= user.phone %></small> </div> <% }); %> </div> </div> </body> </html>
Notice that it’s plain HTML, so nothing new here. Right at the beginning, within the head tag, we’re adding the Bootstrap’s CSS stylesheet. It’ll help to input styling to our example page.
The koa-ejs library makes use of expressions to separate what’s HTML from what’s JavaScript code.
Whenever you want to open a new JavaScript code block, wrap the code with the <% ... %>
operators. Otherwise, if you want to print some value directly into the HTML, use the <%= … %>
operators. Just remember that, like any other JavaScript code block, your blocks need to open and close properly, like as you usually do within JavaScript code files.
Go ahead and have a look over the iteration we’re doing. It’s plain JavaScript code, pretty simple.
Now, let’s test it. Run the application by issuing the node index.js
command on your console and type the http://localhost:3000/ address at your browser. This will produce the following output:
Then, go ahead and change the address to http://localhost:3000/users. You may see the list of users changing every time you refresh the browser.
Errors are a very important part of your web applications. Although you can always handle them individually on each endpoint, and just like with Express, Koa also allows you to create a centralized middleware to deal with your app errors.
Look at the following code snippet:
app.use(async (ctx, next) => { try { await next(); } catch (err) { console.error(err); ctx.body = "Ops, something wrong happened:<br>" + err.message; } });
This middleware must be placed before all the others, so you can grab and log any kind of error your app may face, including the ones related to the other middleware.
If you have more than one JavaScript file in which endpoints are mapped, you can centralize this middleware in one place and export it to all of them.
Yes, Koa also has another package that deals with more fine-grained logging mechanisms for development and debugging.
For this, you need first to install the proper dependency:
npm i koa-logger
And add the following content to your index.js:
// At the beginning of the file const Logger = require("koa-logger"); ... app.use(Logger()) .use(router.routes()) .use(router.allowedMethods());
Make sure that the Logger
middleware is also the first one to be added, otherwise, Koa won’t listen to the logs from your routes and HTTP calls.
When you restart your app and access the endpoints again, you may see logs similar to the ones below:
This was just a brief introduction to the powers of Koa.js and how it can make things clearer when it comes to code organization. To see more of its benefits, the use of common frameworks like Sequelize for database handling, Jasmine for testing, etc. is a must, or perhaps you may test its inclusion right into your current project. Just create a fork and go for it!
Koa also offers support for a bunch of additional features like CSRF and JWT (JSON Web Tokens), access logs, charset conversions, caching, minifiers and compressors, and many more. Please, make sure to refer to the official GitHub project for more on these. Good luck!
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.