Table of Contents
Objective
This tutorial guide you to create a simple rest api using node.js and express js. In this article we are going to interact with PostgreSQL database and will create an API for student schema and 5 endpoints for student API. We’ll create below endpoints.
- POST: /create
- GET: /all
- PUT: /update
- GET: /:id
- DELETE: /:id
Introduction
Rest API
Rest API is an architecture for building and communication with web services. It has methods like GET, PUT, POST, DELETE which are used to build APIs for specific task like Read, Update, Create and Delete. It always uses request and response object to access and provide the data, rest api represent resources in Text, JSON, HTML and XML format.
If you are a frontend developer, you probably noticed that there are somethings we can’t implement in frontend, like database interaction, authentication and authorization etc. This is where rest api comes in and it lets you implement some features which you can access it from frontend.
I am assuming you have basic understanding on rest architecture and methods like GET, PUT, POST, DELETE. In this node.js rest api tutorial we are going to use below technologies to build rest api in node and express.
Technology | Description |
---|---|
IDE | Visual Studio Code |
Node.js | We need to have node js installed in our system. |
Express.js | We'll use the most popular HTTP server libraries for Node.js. |
Postgresql | We need to have postgresql database installed in our system and we can use PgAdmin tool as database viewer. |
JSON | Request body and response body will be in JSON format. |
Build Rest Api In Node And Express
Create Database & Table
Login to PostgreSQL via PgAdmin tool or via command prompt and create database named as node_project.
CREATE DATABASE node_project;
Now create a table inside node_project database named as student using below query.
CREATE TABLE public.student
(
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
name text COLLATE pg_catalog."default" NOT NULL,
class_no text COLLATE pg_catalog."default" NOT NULL,
roll_no integer NOT NULL,
address text COLLATE pg_catalog."default",
created_on time with time zone DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT student_pkey PRIMARY KEY (id)
)
Setup Node.js Project
I am assuming you already know how to install node js and vistal studio code IDE. To check node is installed or not in your system, open CMD or terminal and run below command and it’ll show installed node version else need to install node js in your system.
node –version
To setup node.js application, run below command from CMD or terminal
mkdir FirstNodeApp
cd FirstNodeApp
npm init
After running above command, package.json file will be generated in FirstNodeApp project folder. You can open this project in VS Code IDE. We need to install some node.js dependencies to start working on rest api, run below commands to install the dependencies.
npm install express joi pg properties-reader –save
- express – We’ll use the most popular HTTP server libraries for Node.js. It’ll enable us to interact and manipulate request and response object of REST architecture.
- joi – It’ll validate the input data from request object.
- pg – it’ll enable us to interact with PostgreSQL database.
- properties-reader – We can read value from .properties file.
After running above command it’ll create node_modeules folder and it’ll update package.json file also.
package.json
{
"name": "firstnodeapp",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"keywords": [
"nodejs",
"express",
"rest",
"api",
"postgresql"
],
"author": "blog.almightytricks",
"license": "ISC",
"dependencies": {
"express": "^4.17.3",
"joi": "^17.6.0",
"pg": "^8.7.3",
"properties-reader": "^2.2.0"
}
}
Now create some folders and files and I’ll explain why we have created these files. We are going to use MVC architecture to implement this project.
- app.js – This file will be the starting point of this project. it’ll have the logic to start server and communication with the routers.
- constants.js – It’ll have all constants.
- db.js – it’ll have logic for database connection
- db.properties – It’ll have database details like username, database name, password etc., which will be used in db.js file.
As we are following MVC architecture so we have created controller, routers and DAO layer we have separated the logic for how a router can communicate with controller from routers folder, how a controller can communicate with DAO from controllers folder and how DAO can communicate with database from DAO folder.
Let’s start coding for node.js rest api. You can find complete code base from my GitHub account.
app.js
We are running the application in port number 4000 and below code is responsible for creating a node server.
body-parser is used for accepting request and response as json format. no need to install it, it comes with express js bundle.
const express = require('express');
const bodyParser = require('body-parser');
...
const PORT = 4000
...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
...
app.listen(process.env.PORT || PORT, function () {
console.log(`listening on *:${PORT}`);
});
We are calling routers from app.js file using below code.
const students = require('./routers/StudentRouter');
...
/**
* APIs
*/
app.use(`/api/${ constants.API_VESION_V1 }/student`, students)
db.properties
username=postgres
hostname=localhost
dbname=node_project
password=root
port=5432
constants.js
We’ll use below constants for input validation and some messages we’ll send as response. You can understand when we’ll implement actual logic. 😀
'use strict';
const API_VESION_V1 = 'v1';
const SUCCESS = 'Success';
const FAILED = 'Failed';
const SAVE = 'Saved';
const UPDATE = 'Updated';
const DELETE = 'Deleted';
const NOT_FOUND = 'Record not exist in database';
const INVALID = 'Invalid';
const VALID = 'Valid';
const INTERNAL_SERVER_ERROR = 'Something went wrong';
const VALIDATION_MSG = {
BASE_STRING: 'should be a type of text',
BASE_NUMBER: 'should be a type of number',
EMPTY_STRING: 'cannot be an empty field',
MIN_STRING: 'should have a minimum length of {#limit}',
MAX_STRING: 'should have a maximum length of {#limit}',
MIN_NUMBER: 'should have a minimum value of {#limit}',
MAX_NUMBER: 'should have a maximum value of {#limit}',
REQUIRED: 'is a required field'
}
module.exports = {
API_VESION_V1,
SUCCESS,
FAILED,
SAVE,
UPDATE,
DELETE,
NOT_FOUND,
INVALID,
VALID,
INTERNAL_SERVER_ERROR,
VALIDATION_MSG
}
db.js
In this file we can have logic to connect to PostgreSQL database. If you notice we are getting database details from db.properties
file and we are reading it via properties-reader
npm package. Also we are using pg
package to have connection with database.
'use strict';
const { Client } = require('pg');
var PropertiesReader = require('properties-reader');
var properties = PropertiesReader('./db.properties');
const client = new Client({
user: properties.get('username'),
host: properties.get('hostname'),
database: properties.get('dbname'),
password: properties.get('password'),
port: properties.get('port'),
});
client.connect();
module.exports = client;
dao/StudentDAO.js
Lets create some methods which are responsible to do Create, Update, Read, Delete operations with database. We have imported database connection from db.js and all the constants from constants.js file
const client = require(‘../db‘);const constants = require(‘../constants‘);
Read Student
We have created getStudent() and getStudentById() methods to read all students or read student by id. getStudentById() method will work as a common method which will be calling in update, delete and read by id operations.
const getStudent = async data => {
'''
if(data?.id) {
const student = await getStudentById(data.id);
response.message = constants.SUCCESS;
response.data = student;
return response;
} else {
const sql = 'SELECT * FROM student ORDER BY created_on ASC';
const res = await client.query(sql);
'''
}
'''
}
const getStudentById = async id => {
'''
const sql = 'SELECT * FROM student WHERE id = $1';
const values = [id];
const result = await client.query(sql, values);
'''
}
Create a student
We have created saveStudent() method which is responsible to insert student information into the student table. It’ll have one argument which will be passed from controller.
const saveStudent = async data => {
...
const sql = 'INSERT INTO student (name, class_no, roll_no, address) VALUES($1, $2, $3, $4) RETURNING *';
const values = [name, classNo, rollNo, address];
const result = await client.query(sql, values);
await client.query('COMMIT');
...
return response;
}
Update Student
We have created updateStudent() method which is responsible to update student information into the student table. It’ll have one argument which will be passed from controller. Initially it’ll check with id whether the record is exist in database or not, if exist then it’ll update the student data by id.
const updateStudent = async data => {
...
const student = await getStudentById(id);
if(student) {
const sql = 'UPDATE student SET name = $1, class_no = $2, roll_no = $3, address = $4 WHERE id = $5 RETURNING *';
const values = [name, classNo, rollNo, address, id];
const result = await client.query(sql, values);
await client.query('COMMIT');
if(result) {
response.message = constants.UPDATE;
response.data = normalizeData(result.rows[0]);
}
} else {
response.message = constants.NOT_FOUND;
}
...
return response;
}
Delete a student
We have created deleteStudent() method which is responsible to delete student information from the student table. It’ll have one argument which will be passed from controller. Initially it’ll check with id whether the record is exist in database or not, if exist then it’ll delete the student data by id.
const deleteStudent = async data => {
...
const student = await getStudentById(data.id);
if(student) {
const sql = 'DELETE FROM student WHERE id = $1';
const values = [data.id];
const result = await client.query(sql, values);
await client.query('COMMIT');
if(result) {
response.message = constants.DELETE;
}
} else {
response.message = constants.NOT_FOUND;
}
...
return response;
}
controllers/StudentController.js
We’ll create some methods which will be called from StudentRouter.js file. We can have 5 methods like
- createStudent()
- updateStudent()
- getAllStudent()
- getByStudentId()
- deleteStudent()
All these methods will accept request and response as arguments. We have method to validate inputs from request object and we have created a method named as validation(). If inputs are not correct then it’ll return response as 400 status code with appropriate message else it’ll accept the request and it’ll interact with DAO layer and it’ll return the response. If you notice we are using joi package to validate the input and we are using methods of studentDAO which will interact with the database.
'use strict';
const Joi = require('joi');
const studentDAO = require('../dao/StudentDAO');
const constants = require('../constants');
const operations = ['create', 'update', 'forId']
const createStudent = async (req, resp) => {
...
const dbResponse = await studentDAO.saveStudent(reqBody);
...
return resp;
}
const updateStudent = async (req, resp) => {
...
const dbResponse = await studentDAO.updateStudent(arg);
...
return resp;
}
const getAllStudent = async (req, resp) => {
...
const dbResponse = await studentDAO.getStudent('');
...
return resp;
}
const getByStudentId = async (req, resp) => {
...
const dbResponse = await studentDAO.getStudent(arg);
...
return resp;
}
const deleteStudent = async (req, resp) => {
...
const dbResponse = await studentDAO.deleteStudent(arg);
...
return resp;
}
const validation = (data, type) => {
...
const obj = {
id: Joi.number().required().messages({
"number.base": `"id" ${constants.VALIDATION_MSG.BASE_NUMBER}`,
"any.required": `"id" ${constants.VALIDATION_MSG.REQUIRED}`
})
}
let joiSchema = ''
switch (type) {
case operations[0]:
joiSchema = Joi.object(objCreate);
break;
case operations[1]:
joiSchema = Joi.object(objUpdate);
break;
default:
joiSchema = Joi.object(obj);
}
...
return response;
}
module.exports = {
createStudent,
updateStudent,
getAllStudent,
getByStudentId,
deleteStudent
}
routers/StudentRouter.js
This file is called from app.js file to enable student router. In this file if you notice we are using express.Router()
which is using express HTTP feature to enable the routing concept in the application.
Also we are using studentController
methods in the router and we separated the routers with .get(), .post(), .put() and .delete(), there are nothing but REST HTTP methods.
If you notice for put('/update/:id'
, get('/:id'
, delete('/:id'
we are passing id as path variable, we can pass it as request body but it’s a best practice to pass id as path variable for update, delete by id and get by id operations.
'use strict';
const express = require('express');
const router = express.Router();
const studentController = require('../controllers/StudentController')
router.post('/create', studentController.createStudent);
router.put('/update/:id', studentController.updateStudent);
router.get('/all', studentController.getAllStudent);
router.get('/:id', studentController.getByStudentId);
router.delete('/:id', studentController.deleteStudent);
module.exports = router;
Run Project
To run this project please run node app.js
and it’ll listen to port 4000. To test all the APIs you can use postman tool. I have attached postman collection in my GitHub Account you can download and import it in postman tool.
node app.js
Use blow APIs to test the flow in your local.
POST http://localhost:4000/api/v1/student/create
PUT http://localhost:4000/api/v1/student/update/:id
GET http://localhost:4000/api/v1/student/all
GET http://localhost:4000/api/v1/student/:id
DELETE http://localhost:4000/api/v1/student/:id
Great we have implemented rest api in node and express. 🙂
Conclusion
Using node.js and express we can build complex rest application, but to understand the complex rest application we need to have better understanding on simple rest application, I hope this example is clear to you, if you have any doubts or query let us know in your comments.
If you found this post is useful, then please share this post with your friends and colleague. You can find out our other useful tech blogs in our website.
Thank You. Have a great day 🙂