Creating a Modular Koa Application
Introduction
Koa is quite new in Node framework scene. If you have ever worked on Express and have gotten tired of the callback hell when the application gets larger, you will love Koa. It is developed by the same guys behind Express. One of the cool things in Koa is cascading - the middlewares you add will be running 'downstream', then flowing back 'upstream', and that gives you more predictable controls.
The problem
Koa is very minimal and flexible. However, unlike Express which comes with a robust router, Koa by default does not have one. This is a major problem working with Koa as you need to choose a third party package or pick one of packages listed under thier GitHub. You may test out a few and find out they don't work as what you want. This also rises a concern as it lacks of a standard. Probably it is the price for being minimalistic.
After hours of struggling, I finally narrowed it down to combining these two packages that will work almost similar to Express's - koa-mount and koa-trie-router.
Getting started
For how to get started with Koa, there are plenty tutorials available out there (even though many of them were based on the version prior to Koa 2). The purpose of this article is to show you how you can separate the application routes to create a modular directory structure for your application, for an example:
-------------
modules/
home/
controllers/
models/
views/
blog/
controllers/
models/
views/
news/
controllers/
models/
views/
-------------
These are directories and files that I have in my user
module:
-------------
_routes/
delete-user.js
fetch-user.js
index.js
insert-user.js
update-user.js
controllers/
delete-user.js
fetch-user.js
index.js
insert-user.js
update-user.js
models/
schemas.js
user.js
index.js <-- the user main route
-------------
Inside the main index.js
in user
module, I call an instance of koa-trie-router
and import all the routes that belong to user
:
'use strict'
import Router from 'koa-trie-router'
import fetchUsers from './_routes'
import fetchUser from './_routes/fetch-user'
import insertUser from './_routes/insert-user'
import updateUser from './_routes/update-user'
import deleleUser from './_routes/delete-user'
const router = new Router()
export default () => {
router.get(fetchUsers())
router.get(fetchUser())
router.post(insertUser())
router.put(updateUser())
router.del(deleleUser())
return router.middleware()
}
And then in the application route, I import the user
main route in a file called routers.js
, alongside with other main routes from other modules:
//routers.js
'use strict'
import Router from 'koa-trie-router'
import mount from 'koa-mount'
// Import routes from different modules.
import home from 'default/home'
import user from 'default/user'
const router = new Router()
export default () => {
// Add Routes.
router.use(mount('/', home()))
router.use(mount('/users', user()))
return router.middleware()
}
Lastly, I import this routers.js
as a middleware in middlewares.js
:
// middlewares.js
import mount from 'koa-mount'
import routes from './routes'
...
...
...
app.use(mount('/api', routes()))
You can clone or download this basic modular example from the GitHub repository.
Using the application
To use this application, firstly you need to have Mongo already installed in your machine.
If you have Mongo ready, then install the Node packages:
$ npm install
To serve the app at localhost:3000 (for development):
$ npm run dev
Or (for production):
$ npm start
To test:
$ npm run test
After that you can access these HTTP methods below:
1. The GET method
When you visit the app at http://127.0.0.1:3000
, you get:
{"status":404,"message":"Not Found"}
This is because there is not module set for this route /
, the application main route starts at api/
:
app.use(mount('/api', routes()))
So if you visit it at http://127.0.0.1:3000/api
, you get:
{"status":200,"data":{"message":"Hello World!"}}
To vist the user
index page, use http://127.0.0.1:3000/api/users
, then you get:
{"status":200,"data":[]}
Note that the data is empty - "data":[]
, this is because there is no user added to the user
collection in the database yet.
2. The POST method
Now if you go to Google Postman, create the key below and type in the value in the Body
section:
Key Value
--------------------
name rob
Choose POST
method and hit the Send
button, you get:
{
"status": 200,
"data": {
"result": {
"ok": 1,
"n": 1
},
"ops": [
{
"uuid": "ede050e0-9b1c-11e7-a766-4f8befaf2db7",
"name": "rob",
"_id": "59bd884d64fa675e298079e0"
}
],
"insertedCount": 1,
"insertedIds": [
"59bd884d64fa675e298079e0"
]
}
}
When you visit http://127.0.0.1:3000/api/users
again, you get:
{"status":200,"data":[{"_id":"59bd884d64fa675e298079e0","uuid":"ede050e0-9b1c-11e7-a766-4f8befaf2db7","name":"rob"}]}
You can add more users in and when you just want to query a single user, e.g. http://127.0.0.1:3000/api/users/rob
, you get:
{"status":200,"data":{"_id":"59bd884d64fa675e298079e0","uuid":"ede050e0-9b1c-11e7-a766-4f8befaf2db7","name":"rob"}}
3. The PUT method
To update that user, just add the _id
key to Postman:
Key Value
--------------------
name robbie
_id 59bd884d64fa675e298079e0
When you hit the Send
button with the PUT
method, you get:
{
"status": 200,
"data": {
"lastErrorObject": {
"updatedExisting": true,
"n": 1
},
"value": {
"_id": "59bd884d64fa675e298079e0",
"uuid": "ede050e0-9b1c-11e7-a766-4f8befaf2db7",
"name": "robbie"
},
"ok": 1
}
}
4. The DELETE method
Lastly, to delete this user, you just need to provide the _id
key in Postman:
Key Value
--------------------
_id 59bd884d64fa675e298079e0
When you hit the Send
button with the DELETE
method, it results:
{
"status": 200,
"data": {
"ok": 1,
"n": 1
}
}
Conclusion
As you can see it is quite a breeze to develop applications with Koa, and making them modular is possible, once you have the route set up already. Let me know what you think and what router package you use for your projects. Any suggestions and insights, please leave a comment below. Hope you learn something new like I did.