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.
Next, we initialize the Node.js App with a package.json file:
We need to install necessary modules: express, pg, pg-hstore, and cors. Run the command:
The package.json file should look like this:
create new folder config and create new file config.js
we need to add some code in server.js
after that add the nodemon module in the dev env
and finally file package.json look like this:
Now we will add multi environment development.env and test.env
and test.env
update file config.js file to be like this
add file folder test and file Task.test.js
Finally we have finished creating the code. To run it simply by doing the following command:
To do testing, you can use the following command:
Credit Source: Giya Pranata
Structure project.
Article ContentsSetup nodejs Express
- Create server js
- Create config database
- Create model
- Create controller
- Create router
- Create error handle
- 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
{
"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 sequelizenpm install sequelize
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.jsconst 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.jsconst 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.jsconst 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.jsconst 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 followsnpm 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