Building a REST API with Node, KOA and PostgreSQL

This post describes how to create a REST API using Node, KOA and PostgreSQL. It’s intended for beginners but is based on best practice. Most articles for beginners will tell you to focus on understanding the basics and don’t worry about design practices. They are probably right but that’s just not how my mind works. You should have your development environment ready with Node, NPM and Git installed before starting. If you need help; look at my previous articles on setting up your web development environment and getting started with Git. All of the code is documented below but the complete application code is here https://github.com/infornite/n4nite-rest-api if you want to refer to it as you follow along.

Setup the project directory

Create an empty directory for your project and cd into it. Execute the command npm init which will guide you through creating a new package.json file. This is where Node will store information about our project. You can select the default answer for most prompts except for the ‘entry point’ where you should type ‘app.js’. This tells Node that we want to run a Javascript file called app.js as the first step when we start our server. Of course we will need to create this app.js file later on.

Install your dependencies

The next step is to install dependencies. Dependencies are simply packages or modular pieces of code that we can plug into our application to give us extra functionality. There are literally thousands of packages that have been created by companies and people and made available under open source licensing. Think of these packages as the tools that we can use to program our application. They are broadly split into dev dependencies which are packages that you use only during development to make your life easier and non-dev dependencies which are the packages that get deployed with your application and make it work.

Dev Dependencies

For now, we are going to install 2 really useful dev dependencies.

  • nodemon: This watches for changes when we save our code and automatically restarts the node server so those changes are reflected. It saves us from having to manually stop and restart the server every time we make a change.
  • typescript: Typescript makes our lives a lot easier by highlighting syntax errors in our code and providing auto-complete functionality. Because we are using Typescript we save our code in files with a .ts extension and write in a Typescript syntax. Typescript converts these files to normal Javascript in the background for us.
npm install nodemon --save-dev
npm install typescript --save-dev

The --save places a note in our package.json file about these dependencies. If you ever need to rebuild your application these entries can be used to do that without having to remember which packages and versions you were using.

We also need to create 2 files in our root directory that contain configuration details for Typescript and Nodemon. Go ahead and create a tsconfig.json and nodemon.json file based on the following.

//tsconfij.json
{
    "compileOnSave": false,
    "compilerOptions": {
      "noImplicitAny": false,
      "emitDecoratorMetadata": true,
      "experimentalDecorators": true,
      "moduleResolution" : "node",
      "lib": [
        "es2017"
      ],
      "types": [
        "node"
      ],
      "sourceMap": true,
      "baseUrl": "./src"
    },
    "exclude": [
      "node_modules",
      "**/*.spec.ts"
    ]
  }

This is all fairly standard. The main things of note is that we set emitDecoratorMetadata and experimentalDecorators because they are needed for Typeorm to work. We also specify "baseUrl": "./src" which allows us to more easily reference paths in our code based on the main folder we will call src (e.g. instead of writing import '../../../entities/card' we can just write import 'models/card').

//nodemon.json
{
    "watch": ["./src"],
    "ext": "ts",
    "exec": "ts-node --inspect -r tsconfig-paths/register ./src/server.ts"
   }

In nodemon.json we are just telling nodemon to watch for changes in the src folder to any file types ending with .ts and to restart the server when these changes are detected. While you are editing the JSON files go ahead and add an entry under the scripts section of your package.json as well as follows.

//package.json
.....
.....
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon"
  },
.....
.....

Non-Dev Dependencies

  • koa: This is the framework that we are going to be using. Check my note below for more information on KOA and why we are using it.
  • koa-router: Needed for creating and managing routes. Basically, we use this to tell the server how to handle client requests to different URL’s (my-website.com/users vs my-website.com/products)
  • koa-bodyparser: Needed to parse the body of any requests that are sent to our API by a client. For example when a client passes their User Id to us we need this parser to extract their User Id from the request.
  • typeorm: This is an ‘Object Relational Mapping’ or ORM package. It allows us to create a model of relational data stored in our database and represent this as an object that we can use in our Javascript code.
  • pg: This is a database driver that is needed to connect to PostgreSQL.
  • class-validator: This package has functionality that we can use to validate objects and data such as checking if an email address is valid.
  • ts-node: Needed so we can run our Typescript code via Node.
  • tsconfig-paths: Allows us to map our paths to a base URL which makes things cleaner when referencing file paths in the code.

[shell] npm install koa koa-router koa-bodyparser typeorm pg class-validator ts-node tsconfig-paths –save [/shell]

Creating the project structure

Lets create the basic project structure next. You will already have a node_modules folder where node stores our installed packages and you should also have 4 JSON files that contain configuration settings for our application. Create a new folder called src with 5 sub folders (controllers, databases, models, qa and routes) and 2 files (app.ts and server.ts) in this src directory.

Project Structure
Project Structure
  • routes: Whenever a client sends a request to our application they will specify a URL (my-website.com/users vs my-website.com/products). Routes are simply the way we tell our application how to handle these different requests and where specific requests or URL’s should be routed to in our application to be handles.
  • controllers: Generally the route logic in our application will pass requests to a controller. Controllers are where we write code to define what we want our application to do. For example we might have one controller with logic that is specific to working with users and another controller that has code that is specifically for working with products.
  • models: In this folder we will create a specification or model of the entities that we want to use in our application. This could include a model for what a user looks like or a product or any other entity that we want to create, store in our database and use in our application. Models are important because they create a specification that can be used to map between the data stored in our DB and the objects we work with in JavaScript.
  • databases: Code that initialises a connection to our database.
  • qa: We will store code here related to testing and Quality Assurance of our application and creating sample data.
  • app.ts: Used to create an instance of our application – its a simple file with only 3 lines of code.
  • server.ts: Initialises mode of the packages and functionality needed to make our server work. This file also opens the database connection and this is where we tells the server where our routes are stored.

Update app.ts as per below. These 3 lines of code are just creating a new instance of a KOA app and making it available to other parts of the application via the exports statement. We could of course store these 3 same lines of code in the server.ts file we are going to create. However, I think its good design to keep the main application initialisation separate.

//app.ts

import * as Koa from 'koa';
var app = new Koa();
module.exports = app;

To test the application update the server.ts file as below. Here are are creating a new app by requiring the “./app” that we exported in app.ts. We are also telling this app to respond to every request by setting the response body to “Welcome to my Server”. Finally we tell the app to listen on port 3000 and we kick all of this off by calling the bootstrap() function which we wrapped everything in.

//server.ts
var app = require('./app');

const bootstrap = async () => {

    //Respond with a message to all client requests
    app.use(async ctx => {
        ctx.body = "Welcome to my Server!";
    });

    //Tell the app to listen on port 3000
    app.listen(3000);
};

bootstrap();

Save these changes and then type npm start into the terminal to start your server. Open your internet browser and type localhost:3000 in the address bar and hit enter. If everything is working you should get a response from your server.

Server Response
Server Response

Before we start using our application to store data we need to get our database running so we can connect to it. You can use any DB that you want but I am using PostgreSQL. For this example I created a free database at www.elephantsql.com. Once you have set this up you can visit the database servers page and see the connection details that you need listed like below.

Elephant SQL
Elephant SQL

 

Under your databases folder create a file called postgres-db.ts and populate it as below (replacing with your server name, database, username and password).

//postgres-db.ts

/*
This file initializes your PostgreSQL database. You need to supply
the host name, username, password and database name for your database.
*/

import { createConnection } from 'typeorm';

export const postgresDB = async () => {

    return await createConnection({
        type     : 'postgres',
        host     : 'tINSERT SERVER NAME',
        port     :  5432,
        username : 'INSERT USER NAME',
        password : 'INSERT PASSWORD',
        database : 'INSERT DATABASE NAME',
        ssl: true,
        logging: ['query', 'error'],
        synchronize: true,
    }).then((connection) => {
        console.log('Database connection established');

        
    });

};

Finally, we need to add a couple of lines into our server.ts file to use the code we have written and tell the app to initialise the database.

//server.ts
import { postgresDB } from 'databases/postgres-db';

var app = require('./app');

const bootstrap = async () => {

    // Initialize the database
    await postgresDB();

    //Respond with a message to all client requests
    app.use(async ctx => {
        ctx.body = "Welcome to my Server!";
    });

    //Tell the app to listen on port 3000
    app.listen(3000);
};

bootstrap();

Save the changes and restart the server (if nodemon doesn’t do it for you). You should receive a message that the ‘Database connection established’.

Building out our Application

Great, at this point our application is in pretty good shape. You have setup your environment, installed dependencies, created a good project structure, created your database and run a test application to validate that everything is hanging together. Now, lets get on with building our application. We are going to focus on creating a User entity that is stored in our database and all of the necessary logic to handle querying, creating, updating and deleting Users. We will do this in a modular way and you will see that we can apply the same principles to create any other type of entity we need to handle in our application. I’ll run through the steps for this User Entity and you can repeat the same steps to add entities and complexity to your application. Lets get started and run through X steps.

  1. Create a model that describes what a User looks like and what fields exist for a User.
  2. Tell the database that this model exists and that you want to store the user in the database.
  3. Create the “routes” that a client can access to query, add, edit and delete users.
  4. Create controllers that will handle the clients request depending on the route they have requested.

1. Create a model

Create a new file called user.ts under our models directory based on the following.

//user.ts

//import type definitions from typeorm that describe the physical 
//charachteristics of fields that we will store in the database
import {
    Entity,
    PrimaryGeneratedColumn,
    Column,
    CreateDateColumn,
    UpdateDateColumn,
} from 'typeorm';

//Import functions from the class-valadiator package that we will
//use to validate data when someone is creating or editing a user
import { Length, IsEmail } from 'class-validator';

//This decorator (denoted by the @ symbol) tells type-orm that
//we want to call the database table users
@Entity('users')

//Export the User class so we can use it elsewhere in our project
export class User {

    @PrimaryGeneratedColumn('uuid')     //Tell Postgre to generate a Unique Key for this column
    id: string;                         //Name of the column is id and type is string

    @Column('text')                     
    name: string;

    @Column('text')
    @Length(5, 100)
    @IsEmail()
    email: string;

    @Column('text')
    hashedPassword: string;
    
    @CreateDateColumn()
    createdAt: Date;

    @UpdateDateColumn()
    updatedAt: Date;
}

2. Tell the database that this model exists

Create a new files under the databases directory called postgres-tables.ts that match the following. This file is simply importing the users model that we created above and creating an array called postgresTables that contains all of the tables we have (only one for now).

//postrgestables.ts

/*
This file simply imports our models and creates an array with a list of
the tables we want to include when we connect to Postgres. Its overkill to
keep this info in a seperate file when we only have one table but it will be
really neat and clean once our app grows to have tens and hundreds of tables
*/

import { User } from 'models/user';
export const postgresTables =[User,]

Update our postgresdb.ts file to tell it about the new User table we want to store our user data on in Postgres.

//postgres-db.ts

/*
This file initializes your PostgreSQL database. You need to supply
the host name, username, password and database name for your database.
*/

import { createConnection } from 'typeorm';
import { postgresTables } from './postgres-tables'

export const postgresDB = async () => {

    return await createConnection({
        type     : 'postgres',
        host     : 'tINSERT SERVER NAME',
        port     :  5432,
        username : 'INSERT USER NAME',
        password : 'INSERT PASSWORD',
        database : 'INSERT DATABASE NAME',
        ssl: true,
        entities: postgresTables,
        logging: ['query', 'error'],
        synchronize: true,
    }).then((connection) => {
        console.log('Database connection established');

        
    });

};

Save all the changes. If your app is still running then nodemon will restart the server. If not then type npm start to get the server running again. What will happen now is that your application will connect to the database. It will also create a table in the database for you that is based on the model you defined in the models/user.ts file. This is pretty cool – as your applications get more complex you might want to disable this option and manually create tables in PostgreSQL rather than having the application do it automatically. If you want to do this then you just need to go back to the postgre-db.ts file and change the synchronize: true setting to be false.

3. Create the routes

As I mentioned before routes are the different ways or URLs that clients can pass to your application. Depending on the route a client requests and their request type we will tell our application to behave in a particular way. If you are not familiar with different HTTP Request Types then just google it. At its most basic level there are 4 request types that we are really interested in.

  • Get: the client is requesting some information from us.
  • Post: the client is providing information to us to do something (typically used to create new data such as a new user)
  • Put: the client is providing information to us to update something (for example to update an existing user)
  • Del: the client is requesting that we delete something (for example deleting a user).

Ideally the next thing I would show you is how to use a Get request to retrieve a list of Users from our database. However, our database is empty at the moment so I am going to sidestep a little bit and instead show you how to create test data in the db using our application.

Sidestep: Creating Test Data

To start create a file called createTestData.ts under our QA folder. This file will just connect to the database and inserts 3 sample records in our users table.

import { BaseContext } from 'koa';
import {getConnection} from "typeorm";
import { User } from 'models/user';

//Creating a class so we can later extend it to include creation of more test data
export class TestData {

    //This handles creating test users. Seperate functions can be added for other test data later.
    public static async createTestUsers(ctx: BaseContext){
    try {
    await getConnection()
    .createQueryBuilder()
    .insert()
    .into(User)
    .values([
        { name: "Michael", email: "michael@osullivan.com", hashedPassword: "pass123" }, 
        { name: "Louise", email: "louise@osullivan.com", hashedPassword: "pass123" },
        { name: "Mary", email: "mary@osullivan.com", hashedPassword: "pass123" }
     ])
    .execute();

    //Return a success message if theer are no errors
    ctx.body = "Test users created successfully"
    
    //Catch any errors and return a 500 error status to the user is there are errors
    }catch (err) {
        // will only respond with JSON
        ctx.status = err.statusCode || err.status || 500;
        ctx.body = {
        message: err.message
    };
    }
}
};

Next create a file in our routes folder called qa-routes.ts as follows. This file specifies that when a client request hits the endpoint ‘/qa/users’ and the type of request is POST then we should call our function createTestUsers, that we created in the last file.

import * as Router from 'koa-router';
import createTestData = require('qa/createTestData');

export const qaRouter = new Router();

//Routes for the user entity
qaRouter.post('/qa/users', createTestData.TestData.createTestUsers);          //Create some test users

Finally we need to update the server.ts file to tell it to include the new routes. Lets also clean up the file a little bit whilst we are at it and remove the “Welcome to my Server!” default message from every request.

//server.ts
import { postgresDB } from 'databases/postgres-db';
import {qaRouter} from 'routes/qa-routes';

var app = require('./app');

const bootstrap = async () => {

    // Initialize the database
    await postgresDB();

    //Tell our application to use the router we have created to handle routes related to testing
    app.use(qaRouter.routes(), qaRouter.allowedMethods())
    
    //Tell the app to listen on port 3000
    app.listen(3000);
};

bootstrap();

To test this out we need to send a POST request to this endpoint (localhost:3000/qa/users). You can use any application you want to do this but I use Postman and its the most popular. You can download it from www.getpostman.com. Then send a request to your endpoint and you should get a response that “Test users created successfully”.

 

Postman Test Output
Postman Test Output

 

Creating Routes for the User Entity

Ok, now that we have some test data lets get back to building out our users entity by creating the routes we need. Lets start by creating an new file under our routes directory called rest-routes.ts. You may get an error because it is importing a controller that we create in the next step.

//rest-routes.ts

import * as Router from 'koa-router';
import controller = require('controllers');

export const restRouter = new Router();

//Routes for the user entity
restRouter.get('/users', controller.user.getUsers);             //Get all users in the database

Update the server.ts file to include the new route we have created. Lets also include koa-bodyparser in the server.ts file. This will be needed when we want to use information the client passes to us in their requests such as the details of a new user they want to create.

//server.ts
import { postgresDB } from 'databases/postgres-db';
import {qaRouter} from 'routes/qa-routes';
import {restRouter} from 'routes/rest-routes';
import * as bodyParser from 'koa-bodyparser';


var app = require('./app');

const bootstrap = async () => {

    // Initialize the database
    await postgresDB();

     // Enable bodyParser which is needed to work with information
    // passed to the server from the client requests 
    app.use(bodyParser());

    //Tell our application to use the router we have created to handle routes related to testing
    app.use(qaRouter.routes(), qaRouter.allowedMethods())

    //Tell our application to use the router we have created to handle routes for our rest api
    app.use(restRouter.routes(), restRouter.allowedMethods())

    //Tell the app to listen on port 3000
    app.listen(3000);
};

bootstrap();

The router we created and included in the server.ts file tells the application that any GET client request to the endpoint ‘/users’ should be sent to the getUsers() controller to handle. Lets create that controller next.

4.Create controllers

Create a new file called user.ts under the controllers directory. For now this will contain just one method getUsers that we can use to return a list of all the users in the database.

//controllers/user.ts

import { BaseContext } from 'koa';
import { getManager, Repository, Not, Equal } from 'typeorm';
import { validate, ValidationError } from 'class-validator';
import { User } from 'models/user';


export default class UserController {

    public static async getUsers (ctx: BaseContext) {

        // get a user repository to perform operations with user
        const userRepository: Repository<User> = getManager().getRepository(User);

        // load all users
        const users: User[] = await userRepository.find();

        // return OK status code and loaded users array
        ctx.status = 200;
        ctx.body = users;
    }

  }

Create a file called index.ts in the same controllers directory and populate it as follows. This is a bit of overkill when we only have one controller that we can import into our application directly. However, its good design practice and will make things much cleaner as we add more complexity to the application.

//controllers/index.ts

//export all controllers through this  index.js file. This is
//cleaner than importing the files individually in the routes file
export { default as user } from './user';

And that’s it – you can now send a GET request to ‘localhost:3000/users’ via Postman and you will get a list of users back in the response. A GET is the default type of client request that your internet browser sends so you can also just put ‘localhost:3000/users’ into the address bar of your internet browser to see the same results.

Adding Create, Update and Delete:

Lets extend the application to include the ability to get a single user by ID as well as the functionality to create, update and delete users. I won’t go through each of these buts its basically a case of extending the users.ts controller to have code to handle each type of action and then adding a corresponding router endpoint to the rest-routes-ts file. Here are the 2 completed files.

 

//controllers/user.ts

import { BaseContext } from 'koa';
import { getManager, Repository, Not, Equal } from 'typeorm';
import { validate, ValidationError } from 'class-validator';
import { User } from 'models/user';


export default class UserController {

    public static async getUsers (ctx: BaseContext) {

        // get a user repository to perform operations with user
        const userRepository: Repository<User> = getManager().getRepository(User);

        // load all users
        const users: User[] = await userRepository.find();

        // return OK status code and loaded users array
        ctx.status = 200;
        ctx.body = users;
    }

    public static async getUser (ctx: BaseContext) {

        // get a user repository to perform operations with user
        const userRepository: Repository<User> = getManager().getRepository(User);

        // load user by id
        const user: User = await userRepository.findOne(ctx.params.id);

        if (user) {
            // return OK status code and loaded user object
            ctx.status = 200;
            ctx.body = user;
        } else {
            // return a BAD REQUEST status code and error message
            ctx.status = 400;
            ctx.body = 'The user you are trying to retrieve doesn\'t exist in the db';
        }

    }

    public static async createUser (ctx: BaseContext) {

        // get a user repository to perform operations with user
        const userRepository: Repository<User> = getManager().getRepository(User);
        
        // build up entity user to be saved
        const userToBeSaved: User = new User();
  
        userToBeSaved.name = ctx.request.body.name
        userToBeSaved.email = ctx.request.body.email;
        userToBeSaved.hashedPassword = ctx.request.body.hashedPassword;

        //validate(ctx.request.body.name);

        // validate user entity
        const errors: ValidationError[] = await validate(userToBeSaved, { skipMissingProperties: true }); // errors is an array of validation errors
        if (errors.length > 0) {
            // return BAD REQUEST status code and errors array
            ctx.status = 400;
            ctx.body = errors;
        } else if ( await userRepository.findOne({ email: userToBeSaved.email}) ) {
            // return BAD REQUEST status code and email already exists error
            ctx.status = 400;
            ctx.body = 'The specified e-mail address already exists';
        } else {
            // save the user contained in the POST body
            const user = await userRepository.save(userToBeSaved);
            // return CREATED status code and updated user
            ctx.status = 201;
            ctx.body = user;
        }
    }

    public static async updateUser (ctx: BaseContext) {

        // get a user repository to perform operations with user
        const userRepository: Repository<User> = getManager().getRepository(User);

        // load the user by id
        const userToBeUpdated: User = await userRepository.findOne(ctx.params.id);

        // return a BAD REQUEST status code and error message if the user cannot be found
        if (!userToBeUpdated) {
            
            ctx.status = 400;
            ctx.body = 'The user you are trying to retrieve doesn\'t exist in the db';  
        } 

        if(ctx.request.body.name) {userToBeUpdated.name = ctx.request.body.name;}
        if(ctx.request.body.email) {userToBeUpdated.email = ctx.request.body.email;}
        if(ctx.request.body.hashedPassword) {userToBeUpdated.hashedPassword = ctx.request.body.hashedPassword;}

        // validate user entity
        const errors: ValidationError[] = await validate(userToBeUpdated); // errors is an array of validation errors
        if (errors.length > 0) {
            // return BAD REQUEST status code and errors array
            ctx.status = 400;
            ctx.body = errors;
        } else if ( !await userRepository.findOne(userToBeUpdated.id) ) {
            // check if a user with the specified id exists
            // return a BAD REQUEST status code and error message
            ctx.status = 400;
            ctx.body = 'The user you are trying to update doesn\'t exist in the db';
        } else if ( await userRepository.findOne({ id: Not(Equal(userToBeUpdated.id)) , email: userToBeUpdated.email}) ) {
            // return BAD REQUEST status code and email already exists error
            ctx.status = 400;
            ctx.body = 'The specified e-mail address already exists';
        } else {
            // save the user contained in the PUT body
            const user = await userRepository.save(userToBeUpdated);
            // return CREATED status code and updated user
            ctx.status = 201;
            ctx.body = user;
        }

    }

    public static async deleteUser (ctx: BaseContext) {

        // get a user repository to perform operations with user
        const userRepository: Repository<User> = getManager().getRepository(User);

        // load the user by id
        const userToRemove: User = await userRepository.findOne(ctx.params.id);

        if (!userToRemove) {
            // return a BAD REQUEST status code and error message
            ctx.status = 400;
            ctx.body = 'The user you are trying to delete doesn\'t exist in the db';
        } else {
            // the user is there so can be removed
            await userRepository.remove(userToRemove);
            // return a NO CONTENT status code
            ctx.status = 204;
        }

    }

  }

 

//routes/rest-routes.ts
import * as Router from 'koa-router';
import controller = require('controllers');

export const restRouter = new Router();

//Routes for the user entity
restRouter.get('/users', controller.user.getUsers);             //Get all users in the database
restRouter.get('/users/:id', controller.user.getUser);          //Get a single user by id
restRouter.post('/users', controller.user.createUser);          //Create a single user in the database
restRouter.put('/users/:id', controller.user.updateUser);       //Update a single user that matches the passed id
restRouter.delete('/users/:id', controller.user.deleteUser);    //Delete a single user that matches the passed id

Test it Out:

Lets test out the functionality for the different endpoints and request types we now support in our application.

Retrieve a list of all users:

  • Send a GET command to “localhost:3000/users”
  • Response: A JSON Object with a list of users including their id, name and password.

Retrieve a specific user

  • Send a GET command to “localhost:3000/users” and specify the users id at the end of the endpoint (e.g. localhost:3000/users/id)
  • You can get a users id by looking at the response from the first query to retrieve a list of all users.
  • Response: A JSON Object with details of that specific user.

Create a new user

  • Send a POST command to “localhost:3000/users”
  • Include a JSON object in the body of the request with details of the user you want to create.
  • {
        "name": "Joe Bloggs",
        "email": "joe@bloggs.com",
        "hashedPassword": "Pass456"
    }
  • Response: A JSON Object with the details of the user you created.

Update an existing user

  • Send a PUT command to “localhost:3000/users” and specify the users id at the end of the endpoint (e.g. localhost:3000/users/id)
  • Include a JSON object in the body of the request with details of for user you want to update.
  • {
        "name": "Joe Bloggs",
        "email": "joes-new-email@bloggs.com",
        "hashedPassword": "Pass456"
    }
  • Response: A JSON Object with the updated details of the user.

Delete a specific user

  • Send a DEL command to “localhost:3000/users” and specify the users id at the end of the endpoint (e.g. localhost:3000/users/id)
  • You can get a users id by looking at the response from the first query to retrieve a list of all users.
  • Response: Blank

Storing your code on GIT:

As a final step lets store our source code on GIT. If you are not familiar with GIT then checkout my article getting started with Git.

git init
git commit -m "first release of n4nite-rest-api"
git remote add origin https://github.com/infornite/n4nite-rest-api.git
git remote -v
git push origin master

That’s it you have a complete working application that is well designed and an approach that you can replicate to extend the complexity of your application by adding new entities. In the next post I will cover securing this API with user registration and authentication. As a last point I am including a note below on why I chose KOA as the framework to build this application.

Why KOA

In order to develop most applications with Node.js you need to use a framework which sits on top of Node. There are several frameworks. The most popular is probably express.js and it’s a great choice if you are starting web development. KOA.js is a reasonably new contender though it has been around a few years now and there are various articles and debates about Express vs KOA and comparisons of a whole host of other frameworks. For me, it came down to a choice between Express and KOA. I read a lot of the technical comparisons but I’m not advanced enough to make a reasonably considered comparison myself. In the end, for it came down to this for me; The team that originally built express spent thousands of hours putting it together. That same team then came along years later and invested thousands of hours to build KOA to take advantage of things that weren’t available when they designed express. I have to surmise that they wouldn’t have invested that time unless they thought there were some fundamental improvements that they could make. I don’t expect that Express is going to be replaced by KOA but as a newbie I though that KOA was the better framework to start with.

Credits

View at Medium.com

https://github.com/javieraviles/node-typescript-koa-rest

View at Medium.com

Leave a Reply

Your email address will not be published. Required fields are marked *