Koa Example Web App

Koa is a Node.js framework designed by the team that invented the popular Express framework. Koa aims to be much smaller, more robust, and more expressive. It can be...

a year ago

Latest Post Automatic Offline Backup With a Raspberry Pi by Tyler Moon

Koa is a Node.js framework designed by the team that invented the popular Express framework. Koa aims to be much smaller, more robust, and more expressive. It can be used for web applications and APIs. Koa leverages ES2017 async functions to allow for ditching callbacks and a lot of middleware that was previously needed.

In this article, we are going to explore a simple Koa based Todo List web application written in Node.js. This will be just a brief example but will hopefully show how simple and powerful this new Node package really can be.

Prerequisites

Setup

To get started on our Todo List app we need to set up the basic project structure and install the needed Node.js packages

# Create the needed directories
mkdir koa-example koa-example/views
cd koa-example

# Create empty files to be filled in later
touch server.js render.js views/layout.html views/list.html views/new.html views/show.html

# Initalize a package.json file for storing npm packages
npm init (take defaults)

# Install needed koa packages as well as the HTML parser swig
npm i koa koa-body koa-logger koa-router koa-views swig --save

And now we should have the following directory structure with the needed files

koa-example
| views
  | layout.html
  | list.html
  | new.html 
  | show.html
| package-lock.json
| package.json
| render.js
| server.js

Server Time

Perhaps the easiest place to start is with the server code itself. Start by opening up server.js in your favorite IDE of choice (mine is listed in this article) and entering the following code.

server.js

/**
File: server.js
Author: Tyler Moon
Purpose: A simple Koa server for rendering a Todo List web application
**/
// Require needed Node.js packages
const render = require('./render');
const logger = require('koa-logger');
const router = require('koa-router')();
const koaBody = require('koa-body');

// Initalize a Koa app
const Koa = require('koa');
const app = new Koa();

// In memory "database"
const todoList = [];

// Tell koa to use our logger for displaying all inbound and outbound request
app.use(logger());

// Use our render module in _render.js_ for rendering html with swig
app.use(render);

// Use koaBody to parse body parameters from post request
app.use(koaBody());

// Setup routes to async functions
router.get('/', list)
  .get('/todo/new', add)
  .get('/todo/:id', show)
  .post('/todo', create);

// Tell koa to use our router
app.use(router.routes());

// response functions
// list all todo items
async function list(ctx) {
  await ctx.render('list', { todoList: todoList });
}

// show creation form
async function add(ctx) {
  await ctx.render('new');
}

// show a specific item
async function show(ctx) {
  const id = ctx.params.id;
  const todo = todoList[id];
  if (!todo) ctx.throw(404, 'invalid post id');
  await ctx.render('show', { todo: todo });
}

// create new item
async function create(ctx) {
  const item = ctx.request.body;
  const id = todoList.push(item) - 1;
  item.created_at = new Date();
  item.id = id;
  ctx.redirect('/');
}

// Listen to port 3000 for request
app.listen(3000);

And add the following code to render.js to finish up our Node coding

render.js

/**
File: render.js
Author: Tyler Moon
Purpose: Provide a module for rendering views using the swig template engine
**/
// Require needed Node packages
const views = require('koa-views');
const path = require('path');

// setup mapping the .html views to the swig template engine
module.exports = views(path.join(__dirname, './views'), {
  map: { html: 'swig' }
});

And now running node server.js should start up our server but it will not be able to do anything until we fill out the rest of the code.

Note: in this case, there is no output from the server on startup so navigate to localhost:3000 to make sure the server is working.

Now its time to add some content to our simple web application. Without HTML our website does not know how to format our content and deal with user interactions. Add the following HTML code that has swig templates in it.

layout.html

<!-- layout.html -->
<html>
<head>
  <title>{% block title %}Blog{% endblock %}</title>
  <!-- Add a bootstrap style cdn -->
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
 </head>
<body>
  <!-- main project div -->
  <div class="container">
    <div class="jumbotron">
      <h1>Todo List</h1>
    </div>
    <!-- through swig templating the content section
     will be switched depending on which page is to be rendered -->
    <section id="content">
      {% block content %}
        <p>Missing content!</p>
      {% endblock %}
    </section>
  </div>
</body>
</html>

list.html

<!-- layout.html -->
{% extends 'layout.html' %}

{% block title %}Todo Items{% endblock %}

{% block content %}
<h1>Items</h1>
<p>You have <strong>{{ todoList.length }}</strong> items!</p>
<ul id="items" class="list-group">
  <!-- repeat list items for each todo item -->
  {% for item in todoList %}
    <li class="list-group-item">
      <h4 class="list-group-item-heading">{{ item.title }}</h4>
      <p class="list-group-item-text">
        <a href="/todo/{{ item.id }}">Item Details</a>
      </p>
    </li>
  {% endfor %}
</ul>
<hr/>
<!-- button to create a new item -->
<a href="/todo/new" class="btn btn-info btn-lg">
  <span class="glyphicon glyphicon-pencil"></span> Create an Item
</a>
{% endblock %}

new.html

<!-- new.html -->
{% extends 'layout.html' %}

{% block title %}New Item{% endblock %}

{% block content %}
  <h1>New Item</h1>
  <p>Create a new item.</p>
  <form action="/todo" method="post">
    <div class="form-group">
      <label for="title">Title:</label>
      <input type="text" placeholder="Title" name="title" class="form-control">
    </div>
    <div class="form-group">
      <label for="title">Contents:</label>
      <textarea placeholder="Contents" name="body" class="form-control"></textarea>
    </div>
    <button type="submit" class="btn btn-success" value="Create">Submit</button>
  </form>
{% endblock %}

show.html

<!-- show.html -->
{% extends 'layout.html' %}

{% block title %}{{ todo.title }}{% endblock %}

{% block content %}
  <h1>{{ todo.title }}</h1>
  <p>{{ todo.body }}</p>
  <a href="/" class="btn btn-success">Back</a>
{% endblock %}

And with those HTML files in place, our Todo List web app should be fully functional. In the root directory of the project directory run node server.js and then navigate to http://localhost:3000 and create some todo items!

Summary

In this article, we saw how simple (~ 65 lines of Node.js code) it can be to set up a simple web app using Koa and swig. For more information on Koa check out their documentation here.

Tyler Moon

Published a year ago

Comments?

Leave us your opinion.