DEV Community

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

Posted on

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

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"
  });
});
Enter fullscreen mode Exit fullscreen mode

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"
  });
}))
Enter fullscreen mode Exit fullscreen mode

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");
});
Enter fullscreen mode Exit fullscreen mode

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");
});
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

then let's add nodemon for easier development

yarn add --dev nodemon typescript
Enter fullscreen mode Exit fullscreen mode

below is my usual setup when using nodemon for typescript development

{
  "watch": ["dist"],
  "ext": "js",
  "exec": "node index.js"
}
Enter fullscreen mode Exit fullscreen mode

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

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

Enter fullscreen mode Exit fullscreen mode

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}`);
});
Enter fullscreen mode Exit fullscreen mode

now open up 2 terminal. in the first terminal run

yarn build:watch
Enter fullscreen mode Exit fullscreen mode

and in the second terminal run

yarn dev
Enter fullscreen mode Exit fullscreen mode

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

you should see

successfully run app in port 8000
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)