TypeScript / Express / Backend

Building an Express Application with Express-Kun, a functional-paradigm express helper - Part 1 (Intro and Setup)

In this article, I will try to explain how to simply build an app using a helper that I built

Introduction

Today I was just building a library for express js. the library was functional-first express helpers for the common use cases. I built this library gaining inspiration from frontend libraries like Lodash or redux.

I think the beauty of both library is that those libraries only extends the functionality and not really getting in the way we write our app. for example when using redux, redux doesn’t redefine how we write our react component. it only cares that we pass our component to the connect function. meanwhile Lodash, it let us do common things easily.

I think I haven’t found such a framework/library for express that doesn’t get in the way or change how I write my application. so I build express-Kun (https://github.com/hanipcode/express-kun).

The library is simple enough. it does one of 2 things:

  1. You pass a router, it returned back the modified router
  2. You pass a router and middleware/error handler, it returned callback with the modified router as a param.

there are some function that we will gonna use from express-kun in this series

withMiddleware

you pass a router and middleware, it will return the ‘midlewared’ router

// your router
const router = Router();
// with auth middleware
const protectedRouter = withMiddleware(router, authMiddleware); // also support array of middleware ex: [authMiddleware, myMiddleware2]

protectedRouter.get("/user", (req, res) => {
  res.send({
    message: "success"
  });
});

because this is only return the midlewared router. you can chain it without modifying the first router behavior

// your router
const router = Router();
// with auth middleware
const protectedRouter = withMiddleware(router, authMiddleware); // also support array of middleware ex: [authMiddleware, myMiddleware2]
// will apply authMiddleware and uploadMiddleware
const protectedUploadRouter = withMiddleware(protectedRouter, uploadMiddleware);

protectedRouter.get("/user", (req, res) => {
  res.send({
    message: "success"
  });
});
protectedUploadRouter.post("/user", (req, res) => {
  res.send({
    message: "success upload photo"
  });
}))

withErrorHandler

you pass a router and error handler, it will return the router, and if any of the handler return error in runtime it will passed to the error handler

function errorHandler(err, req, res, next) {
  res.json({
    error: true,
    mesage: "wow error"
  });
}

const withErrorHandlerRoute = withErrorHandler(router, errorHandler);

// when accessed will return json { error: true, message: 'wow error' }
withErrorHandlerRoute.get("/errorrouter", (req: Request, res: Response) => {
  throw new Error("Error here");
});

this provide further more functionality to compose middleware with error handler

function errorHandler(err, req, res, next) {
  res.json({
    error: true,
    mesage: "wow error"
  });
}

function middleware(req, res, next) {
  console.log("midleware");
  next();
}

const middlewaredRoute = withMiddleware(router, middleware);

const withErrorHandlerRoute = withErrorHandler(middlewaredRoute, errorHandler);

// when accessed will return json { error: true, message: 'wow error' }
withErrorHandlerRoute.get("/errorrouter", (req: Request, res: Response) => {
  throw new Error("Error here");
});

partialWithMiddleware

there is also a partial variant for withMiddleware function so you can build a general helper / utils on top of it for example

// in generateAuthMiddleware.js
const generateAuthMiddleware = partialWithMiddleware(authMiddleware);

// in your routes.js
const router = new Router();
const protectedRoute = generateAuthMiddleware(router);

this even support supplying partialWithmiddleware with middleware for easy composition

// in generateAuthMiddleware.js
const generateAuthMiddleware = partialWithMiddleware(authMiddleware);

// in uploadProtectedMiddleware.js
const generateUploadProtectedMiddleware = generateAuthMiddleware(
  uploadMiddleware
);

// in your routes.js
const router = new Router();
const uploadProtectedRouter = generateUploadProtectedMiddleware(router);

You can read more about other function provided by the library in the readme of the repository. in this article, I will focus more on how to build an App using express-Kun. I will be using typescript in this article but I’ll omit some typing stuff so you can feel free if you want to follow along using javascript.

In this article series we will build a backend for forum application. where user can login, post article and then comment the article.

Setup

let’s start.
first, we install express and express-kun

yarn add express express-kun

then let’s add nodemon for easier development

yarn add --dev nodemon typescript

below is my usual setup when using nodemon for typescript development

{
  "watch": ["dist"],
  "ext": "js",
  "exec": "node index.js"
}

you will see how this work later. let’s setup script in our package.json

{
  "scripts": {
    "dev": "nodemon",
    "build:watch": "tsc --watch",
    "build": "tsc",
  },
}

then let’s setup our simple app in /src/index.ts (or .js)

import express from "express";

const app = express();

const PORT = 8000;

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.listen(PORT, () => {
  console.log(`successfully run app in port ${PORT}`);
});

now open up 2 terminal. in the first terminal run

yarn build:watch

and in the second terminal run

yarn dev

this will iterate the build faster than using nodemon + ts-node.

you should see

successfully run app in port 8000

in the second terminal and verify that our app working as for now

Folder Structure

the folder strructure we gonna build are more or less like this

dist/
src/
- index.ts
- modules
- - user
- - - user.model.ts
- - - user.routes.ts
- - - user.controller.ts
- utils
index.js
packae.json

Creating User Endpoint

in this section we will create these routes

GET /users -> get all users (public)
GET /users/:id -> get user detail (protected)
POST /users -> Create user / register (public)
POST /users/auth -> Login User (public)

first thing first let’s create the users module folder like in the above. then build the model in src/modules/user/user.model.ts

Creating User model

We will be using mongoose and bcrypt (for password encryption) for this, so let’s install these

yarn add mongoose bcrypt

then let’s define our model

// user.model.ts

import { Schema, model } from 'mongoose';
import bcrypt from 'bcrypt';

function hashPassword(value: any) {
  return bcrypt.hash(value, 'randomSalt');
}

const UserSchema = new Schema({
  email: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true,
    select: false,
    set: hashPassword
  },
  name: {
    required: true,
    type: String
  }
});

const User = model('User', UserSchema);

export default User;

and then let’s build our mongoose connection in the helper initDb in src/utils/initDB.ts

import mongoose from 'mongoose';

const uri = 'mongodb://localhost:27017/forum';

const initDB = () => {
  return mongoose.connect(
    uri,
    {
      useNewUrlParser: true,
      useFindAndModify: false
    },
    err => {
      if (err) {
        console.log(err.message);
        throw new Error('Error Connecting to Database');
      }
    }
  );
};

export default initDB;

now the db connection and setup is ready. in the next article we will try to build the controller.