CRUD Rest API using node js, express, and postgresql

In this article we will create a simple crud rest api using node js, express, and will connect to the postgresql database. Express is a framework for building web applications on top of Node.js. It simplifies the server creation process that is already available in Node. In case you were wondering, Node allows you to use JavaScript as your server-side language.

Structure project.





Article ContentsSetup nodejs Express

  1. Create server js
  2. Create config database
  3. Create model
  4. Create controller
  5. Create router
  6. Create error handle
  7. Add unit test


1. Setup nodejs Express

First, we create a folder:
$ mkdir nodejs-postgresql
$ cd nodejs-postgresql

Next, we initialize the Node.js App with a package.json file:

$ npm init 
name: (nodejs-postgresql)
version: (1.0.0)
description:
Node.js Restful CRUD API with Node.js, Express and Postgresql
entry point:(index.js) server.js 
test command:
git repository:
keywords:
author: lemoncode21
license: MIT
Is this ok? (yes) yes


We need to install necessary modules: express, pg, pg-hstore, and cors. Run the command:

npm install express pg pg-hstore cors express-async-handler --save


The package.json file should look like this:

{
    "name": "nodejs-postgresql",
    "version": "1.0.0",
    "description": "Node.js Restful CRUD API with Node.js, Express and Postgresql",
    "main": "server.js",
    "scripts": {
        "start": "node server.js",
    },
    "author": "lemoncode21",
    "license": "MIT",
    "dependencies": {
        "cors": "^2.8.5",
        "express": "^4.17.3",
        "pg": "^8.7.3",
        "pg-hstore": "^2.3.4",
    }
}


2. Create server js

In the root folder, let’s create a new server.js file:

const express = require("express");
const cors = require("cors");

const app = express();

var corsOptions = {
  origin: "http://localhost:8081"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json
app.use(express.json());

// parse requests of content-type - application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));

// simple route
app.get("/", (req, res) => {
  res.json({ message: "Welcome to lemoncode21 application." });
});

// set port, listen for requests
const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});

3. Create config database

add new module sequelize

npm install sequelize

create new folder config and create new file config.js

const Sequelize = require("sequelize");
const dotenv = require("dotenv");

const db = new Sequelize(
  process.env.DATABASE_NAME,
  process.env.DATABASE_USERNAME,
  process.env.DATABASE_PASSWORD,
  {
    host: process.env.DATABASE_HOST || "localhost",
    dialect: "postgresql",
  }
);


module.exports = db;

4.Create model

create new folder model and create new file Task.js

const Sequelize = require("sequelize");
const db = require("../config/config");

const task = db.define(
  "task",
  {
    task: Sequelize.STRING,
    description: Sequelize.STRING,
  },
  {
    tableName: "task",  
    timestamps: true,
  }
);

module.exports = task;

5.Create controller

create new folder controller and create new file TaskController.js

const asyncHandler = require("express-async-handler");
const Task = require("../model/Task");

const findAllTask = asyncHandler(async (req, res) => {
  const taskList = await Task.findAll();

  res.status(200).json({
    description: "Success fetch data",
    data: taskList,
  });
});

const createTask = asyncHandler(async (req, res) => {
  if (!req.body.task) {
    res.status(400).json({
      description: "Bad request task must be filled",
    });
  } else if (!req.body.description) {
    res.status(400).json({
      description: "Bad request description must be filled",
    });
  }

  const _task = {
    task: req.body.task,
    description: req.body.description,
  };

  const task = await Task.create(_task);

  res.status(200).json({
    description: "Task was saved successfully.",
  });
});

const findTaskById = asyncHandler(async (req, res) => {
  const task = await Task.findByPk(req.params.id);
  if (!task) {
    res.status(400);
    throw new Error("Task not found");
  }
  res.status(200).json({
    description: "Success fetch data",
    data: task,
  });
});

const updateTask = asyncHandler(async (req, res) => {
  const task = await Task.update(req.body, {
    where: { id: req.params.id },
  });

  res.status(200).json({
    description: "Task was updated successfully.",
  });
});

const removeTask = asyncHandler(async (req, res) => {
    const task = await Task.destroy({
      where: { id: req.params.id },
    });
  
    res.status(200).json({
      description: "Task was deleted successfully.",
    });
  });

module.exports = { createTask, findAllTask, findTaskById, updateTask, removeTask };

6.Create router

create new folder router and create new file TaskRouter.js

const express = require("express");
const router = express.Router();
const {
  createTask,
  findAllTask,
  findTaskById,
  updateTask,
  removeTask
} = require("../controller/TaskController");

router.get("/", findAllTask);
router.get("/:id", findTaskById);

router.post("/", createTask);

router.put("/:id", updateTask);

router.delete("/:id", removeTask);

module.exports = router;

7. Create Global error handler

create new folder middleware and create new file ErrorHandler.js

const erroHandler = (err, req, res, next) => {
  const statusCode = res.statusCode ? res.statusCode : 500;

  res.status(statusCode);
  res.json({
      message: err.message,
      status: process.env.NODE_ENV === 'production' ? null : err.stack
  });
};

module.exports = {
    erroHandler
}

we need to add some code in server.js

const express = require("express");
const cors = require("cors");
const db = require("./config/config");
const { erroHandler } = require("./middleware/ErrorHandler");

const app = express();

// set port, listen for requests
const port = process.env.PORT || 5000;

var corsOptions = {
  origin: "http://localhost:5000"
};

app.use(cors(corsOptions));

// parse requests of content-type - application/json
app.use(express.json());

// parse requests of content-type - application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));

// route
app.use("/api/task", require("./router/taskRouter"));

// global error handler
app.use(erroHandler);

// database sync
db.sync().then(() => {
  console.log("Generate table".green);
  app.listen(port, () => console.log(`Sever started on port ${port}`.yellow));
});


module.exports = app;


8. Add unit test

Before we make unit tests there are several modules that we must add as follows

npm i chai chai-http mocha dotenv


after that add the nodemon module in the dev env

npm install --save-dev nodemon


and finally file package.json look like this:

{
  "name": "nodejs-postgresql",
  "version": "1.0.0",
  "description": "nodejs-postgresql",
  "main": "server.js",
  "scripts": {
    "test": "set NODE_ENV=test&& mocha test/*.js --exit",
    "start": "node server.js",
    "dev": "set NODE_ENV=development&& node server.js",
    "server": "nodemon server.js"
  },
  "author": "lemoncode21",
  "license": "MIT",
  "dependencies": {
    "chai": "^4.3.6",
    "chai-http": "^4.3.0",
    "colors": "^1.4.0",
    "cors": "^2.8.5",
    "dotenv": "^16.0.0",
    "express": "^4.17.3",
    "express-async-handler": "^1.2.0",
    "mocha": "^9.2.2",
    "pg": "^8.7.3",
    "pg-hstore": "^2.3.4",
    "sequelize": "^6.17.0",
    "should": "^13.2.3"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}


Now we will add multi environment development.env and test.env

NODE_ENV=development
PORT=8080
DATABASE_NAME=postgres
DATABASE_HOST=localhost
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=postgres


and test.env

NODE_ENV = test
PORT = 8080
DATABASE_NAME = postgres_test
DATABASE_HOST = localhost
DATABASE_USERNAME = postgres
DATABASE_PASSWORD = postgres


update file config.js file to be like this

const Sequelize = require("sequelize");
const dotenv = require("dotenv");
const path = require("path");

dotenv.config({
  path: path.resolve(__dirname, `../${process.env.NODE_ENV.trim()}.env`),
});

const db = new Sequelize(
  process.env.DATABASE_NAME,
  process.env.DATABASE_USERNAME,
  process.env.DATABASE_PASSWORD,
  {
    host: process.env.DATABASE_HOST || "localhost",
    dialect: "postgresql",
    // timezone: 'GMT +7'
  }
);

console.log(`NODE_ENV=${process.env.NODE_ENV}`);

module.exports = db;


add file folder test and file Task.test.js

const assert = require("assert");
const chai = require("chai");
const chaiHttp = require("chai-http");
const app = require("../server");

chai.use(chaiHttp);
chai.should();



describe("/POST task", () => {
  it("it should CREATE task", (done) => {
    let task = {
      task: "create test task",
      description: "create test task",
    };

    chai
      .request(app)
      .post("/api/task/")
      .send(task)
      .end((err, res) => {
        res.should.have.status(200);
        res.body.should.be.a("object");
        done();
      });
  });
});

describe("/PUT/:id task", () => {
  it("it should UPDATE task by id", (done) => {
    const id = 1;
    let task = {
      id: id,
      task: "eat test update",
      description: "eat test update",
    };

    chai
      .request(app)
      .put("/api/task/" + id)
      .send(task)
      .end((err, res) => {
        res.should.have.status(200);
        res.body.should.be.a("object");
        done();
      });
  });
});

describe("/GET task", () => {
  it("it should GET all the tasks", (done) => {
    chai
      .request(app)
      .get("/api/task/")
      .end((err, res) => {
        res.should.have.status(200);
        res.body.should.be.a("object");
        done();
      });
  });

  it("it should GET task by id", (done) => {
    const id = 1;
    chai
      .request(app)
      .get(`/api/task/${id}`)
      .end((err, res) => {
        res.should.have.status(200);
        res.body.should.be.a("object");
        done();
      });
  });
});

describe("/DELETE/:id task", () => {
  it("it should DELETE task by id", (done) => {
    const id = 9;
    chai
      .request(app)
      .delete("/api/task/" + id)
      .end((err, res) => {
        res.should.have.status(200);
        res.body.should.be.a("object");
        done();
      });
  });
});


Finally we have finished creating the code. To run it simply by doing the following command:

npm run dev


To do testing, you can use the following command:

npm run test





Credit Source: Giya Pranata
Previous Post Next Post