DEV Community

Cover image for CRUD REST API with Node.js, Express.js, and PostgreSQL
Ahmad Mujahid
Ahmad Mujahid

Posted on

CRUD REST API with Node.js, Express.js, and PostgreSQL

API or Application Program Interface serves as a communication facility between services or software. In web programming, APIs are used to access available data and services.

REST API (RESTful API)

Representational State Transfer (REST) contains standards for a web service. REST API is an API architectural style that uses the HTTP protocol to access and use data. The following are some data types that REST API has:

  • GET — Get data
  • PUT — Modify the state or value of data or resource
  • POST — Create data or resource
  • DELETE — Delete data or resource

Which can be used to create a CRUD (Create, Read, Update, Delete) service/application.

CRUD

When creating a service, we want some basic functionalities to manipulate the resource. These functionalities are for creating, reading, updating, and deleting data commonly referred to as CRUD.

CRUD stands for Create, Read, Update, and Delete, which is actually an application of the data types in REST API. The details are as follows:

  • CREATE — To create data (POST)
  • READ — To retrieve/read existing data (GET)
  • UPDATE — To modify/update data (PUT)
  • DELETE — To delete data (DELETE)

Things Required

Before following this article, there are several things that need to be installed first:

Installation procedures can be found in the respective websites' documentation.

Setting up the PostgreSQL Database

Firstly, we will create a database in PostgreSQL that will be used by the service. We use psql program. Here are the steps.

Login using the command:

psql -U postgres
Enter fullscreen mode Exit fullscreen mode

By using the above command, we have logged in to PostgreSQL as the postgres user.

postgres=#
Enter fullscreen mode Exit fullscreen mode

Creating a Database

To create a database, we use the command:

postgres=# CREATE DATABASE tutorial
Enter fullscreen mode Exit fullscreen mode

The above command is used to create a database with the name tutorial. To use/connect to the tutorial database, use the command:

postgres=# \c tutorial
Enter fullscreen mode Exit fullscreen mode

The prompt will change to the following:

tutorial=#
Enter fullscreen mode Exit fullscreen mode

indicates that we have successfully connected to the tutorial database.

Creating a Table

Next, create a table in the tutorial database. We will create a table named students in the public schema. Use the following command:

tutorial=# CREATE TABLE public.students (
    id serial NOT NULL,
    firstname varchar(40) NULL,
    lastname varchar(40) NULL,
    origin varchar(50) NULL,
    CONSTRAINT students_pkey PRIMARY KEY (id)
);
Enter fullscreen mode Exit fullscreen mode

Use the command \dt to check if the table has been successfully created.

tutorial=# \dt
            List of relations
Schema |   Name   |  Type | Owner
-------+----------+-------+---------
public | students | table | postgres
(1 row)

Enter fullscreen mode Exit fullscreen mode

To check the details of the table, use the command \d students.

tutorial=# \d students
Table "public. students”
Column    | Type                  | Collation | Nullable | Default
----------+-----------------------+-----------+----------+-----------------------------------------
id        | integer               |           | not null | nextval(’students_id_seq’::regclass)
firstname | character varying(40) |           |          |
lastname  | character varying(40) |           |          |
origin    | character varying(56) |           |          |

Indexes:
      "students_pkey" PRIMARY KEY, btree (id)
Enter fullscreen mode Exit fullscreen mode

Setting up Express.js Server

1. Project Structure

In the end of this article, the structure of the project will be like this :

tutorial
├───node_modules
│ └─── ....
├───mode
│   └───response.js
├───index.js
├───queries.js
├───package-lock.json
└───package.json
Enter fullscreen mode Exit fullscreen mode

You can use this structure to avoid confusion.

2. Project Setup

First step, create a project directory with name tutorial, then initialize Node.js using this command:

mkdir tutorial
cd tutorial
npm init -y
Enter fullscreen mode Exit fullscreen mode

The npm init -y command will generate package.json file with the following contents:

{
  "name": "tutorial",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

Next, install dependencies (for Express.js and PostgreSQL) using this command :

npm install express dan pg
Enter fullscreen mode Exit fullscreen mode

After the execution finished, the dependency files will be stored inside node_modules folder and list of dependencies will be listed in package.json file like below :

{
  "name": "tutorial",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "pg": "^8.7.1"
  }
}
Enter fullscreen mode Exit fullscreen mode

Next, create the index.js file in the root folder. This file is the main file that needs to be executed to run the Node.js server. Inside the index.js file, we add the following script :

const express = require("express");
const bodyParser = require("body-parser");

const app = express();
const port = 3000;

app.use(bodyParser.json());
app.use(
    bodyParser.urlencoded({
        extended: true
    })
)
Enter fullscreen mode Exit fullscreen mode

The require function is used to all dependency to our project.

Next, we create a root URL (/) that returns JSON.

app.get("/", (request, response) => {
    response.json({
        info: 'Hello world!'
    });
})
Enter fullscreen mode Exit fullscreen mode

Save the file. To run the server, use this command :

node index.js
Enter fullscreen mode Exit fullscreen mode

If the server running successfully, you will get this on your terminal :

$ node index.js
Server is running on 3000
Enter fullscreen mode Exit fullscreen mode

If you access http://localhost:3000 from your client app (Postman or web browser) you will get a JSON response like below :

{
  "info": "Hello world!"
}
Enter fullscreen mode Exit fullscreen mode

3. Connect Express with PostgreSQL

The next important step is to connect Node.js with PostgreSQL. We create a file named queries.js (the name doesn't have to be exactly the same as in the article, but the usage in the program should be consistent) which will be used to set up the database connection and the queries used to access and manipulate data.

The first thing we need to do in this section is to set up a Pool connection to the database that will be used. This can be done using the following script:

const Pool = require("pg").Pool;
const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'nama_db_kamu',
    password: 'password_kamu',
    port: 5432
});
Enter fullscreen mode Exit fullscreen mode

4. Create Response Class (Optional)

This is an optional step, which is my personal preference. The purpose of creating the Response class is to standardize the response or output of the API we create. The Response class that I commonly use is as follows:

class Response {
    constructor(status = false, code = 400, message = "", data = null) {
        this.status = status;
        this.code = code;
        this.message = message;
        this.data = data;
    }
}

module.exports = Response;
Enter fullscreen mode Exit fullscreen mode

The Response class will be used in the queries.js file. In this article, we will always use this Response class for JSON formatting in API responses.

const Pool = require("pg").Pool;
const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'testing_db',
    password: 'development',
    port: 5432
});
const ResponseClass = require("./model/response") // opsional
Enter fullscreen mode Exit fullscreen mode

Creating CRUD Operation Functions

In this section, we will create 5 CRUD functions:

  • getStudents() — to retrieve all student data
  • getStudentById() — to retrieve data of a student based on ID
  • createStudent() — to create a student's data
  • updateStudent() — to update a student's data based on ID
  • deleteStudent() — to delete a student's data based on ID

We will declare these five functions in the queries.js file, which will be accessed by index.js.

GET | getStundents()

const getStudents = (request, response) => {
    var responseReturn = new ResponseClass();
    pool.query('SELECT * FROM students ORDER BY id ASC', (error, results) => {
        if (error) {
            throw error
        }

        responseReturn.status = true;
        responseReturn.code = 200;
        responseReturn.message = "Success";
        responseReturn.data = results.rows;

        response.status(200).json(responseReturn);
    })
}
Enter fullscreen mode Exit fullscreen mode

GET | getStudentById()

const getStudentById = (request, response) => {
    var responseReturn = new ResponseClass();
    const id = parseInt(request.params.id)
    pool.query('SELECT * FROM students WHERE id = $1', [id], (error, results) => {
        if (error) {
            throw error
        }
        if (results.rowCount == 0) {
            responseReturn.status = true;
            responseReturn.code = 404;
            responseReturn.message = "User not found";
            responseReturn.data = null;
        } else {
            responseReturn.status = true;
            responseReturn.code = 200;
            responseReturn.message = "Success";
            responseReturn.data = results.rows[0];
        }
        response.status(200).json(responseReturn);
    })
}
Enter fullscreen mode Exit fullscreen mode

POST | createStudent()

const createStudent = (request, response) => {
    const { firstname, lastname, origin } = request.body;
    pool.query('INSERT INTO students (firstname, lastname, origin) VALUES ($1, $2, $3)', [firstname, lastname, origin], (error, results) => {
        if (error) {
            throw error
        }
        response.status(201).send("Student added");
    })
}
Enter fullscreen mode Exit fullscreen mode

PUT | updateStudent()

const updateStudent = (request, response) => {
    const id = parseInt(request.params.id);
    var responseReturn = ResponseClass();
    try {
        const { firstname, lastname, origin } = request.body;
        pool.query('UPDATE students SET firstname = $1, lastname = $2, origin = $3 WHERE id = $4', [firstname, lastname, origin, id], (error, results) => {
            if (error) {
                throw error
            }

            responseReturn.status = true;
            responseReturn.code = 200;
            responseReturn.message = "User modification successed";
            responseReturn.data = null;
            response.status(200).send(responseReturn);
        })
    } catch (error) {
        responseReturn.status = false;
        responseReturn.code = 500;
        responseReturn.message = error.message;
        responseReturn.data = null
        response.status(500).json(responseReturn);
    }
}
Enter fullscreen mode Exit fullscreen mode

The complete code for queries.js is as follows:

const Pool = require("pg").Pool;
const pool = new Pool({
    user: 'postgres',
    host: 'localhost',
    database: 'testing_db',
    password: 'development',
    port: 5432
});
const ResponseClass = require("./model/response") // opsional

const getStudents = (request, response) => {
    var responseReturn = new ResponseClass();
    pool.query('SELECT * FROM students ORDER BY id ASC', (error, results) => {
        if (error) {
            throw error
        }

        responseReturn.status = true;
        responseReturn.code = 200;
        responseReturn.message = "Success";
        responseReturn.data = results.rows;

        response.status(200).json(responseReturn);
    })
}

const getStudentById = (request, response) => {
    var responseReturn = new ResponseClass();
    const id = parseInt(request.params.id)
    pool.query('SELECT * FROM students WHERE id = $1', [id], (error, results) => {
        if (error) {
            throw error
        }
        if (results.rowCount == 0) {
            responseReturn.status = true;
            responseReturn.code = 404;
            responseReturn.message = "User not found";
            responseReturn.data = null;
        } else {
            responseReturn.status = true;
            responseReturn.code = 200;
            responseReturn.message = "Success";
            responseReturn.data = results.rows[0];
        }
        response.status(200).json(responseReturn);
    })
}

const createStudent = (request, response) => {
    const { firstname, lastname, origin } = request.body;
    pool.query('INSERT INTO students (firstname, lastname, origin) VALUES ($1, $2, $3)', [firstname, lastname, origin], (error, results) => {
        if (error) {
            throw error
        }
        response.status(201).send("Student added");
    })
}

const updateStudent = (request, response) => {
    const id = parseInt(request.params.id);
    var responseReturn = new ResponseClass();
    try {
        const { firstname, lastname, origin } = request.body;
        pool.query('UPDATE students SET firstname = $1, lastname = $2, origin = $3 WHERE id = $4', [firstname, lastname, origin, id], (error, results) => {
            if (error) {
                throw error
            }

            responseReturn.status = true;
            responseReturn.code = 200;
            responseReturn.message = "User modification successed";
            responseReturn.data = null;
            response.status(200).send(responseReturn);
        })
    } catch (error) {
        responseReturn.status = false;
        responseReturn.code = 500;
        responseReturn.message = error.message;
        responseReturn.data = null
        response.status(500).json(responseReturn);
    }
}

const deleteStudent = (request, response) => {
    const id = parseInt(request.params.id)
    pool.query('DELETE FROM students WHERE id = $1', [id], (error, results) => {
        if (error) {
            throw error
        }
        response.status(201).send("Student deleted");
    })
}


module.exports = {
    getStudents,
    getStudentById,
    createStudent,
    updateStudent,
    deleteStudent
}
Enter fullscreen mode Exit fullscreen mode

Creating REST API Endpoints

The next step is to create the endpoints that will be used in the REST API. Endpoints are URLs that can be accessed according to their request methods (GET, POST, PUT, DELETE).

The first step is to import the functions from queries.js by adding the following code in index.js:

const db = require('./queries');
Enter fullscreen mode Exit fullscreen mode

Next, the code that needs to be added is as follows:

const express = require("express");
const bodyParser = require("body-parser");

const app = express();
const port = 3000;
const db = require('./queries');

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

app.listen(port, () => {
    console.log("Server is running on " + port);
});

app.get("/", (request, response) => {
    response.json({
        info: 'Hello world!'
    });
})
app.get("/students", db.getStudents);
app.get("/students/:id", db.getStudentById);
app.put("/students/:id", db.updateStudent);
app.post("/students", db.createStudent);
app.delete("/students/:id", db.deleteStudent);
Enter fullscreen mode Exit fullscreen mode

Now you can run the server and access the endpoints following by correct HTTP datatype for each.

Conclusion

Thus, we have successfully created a REST API with basic CRUD functionality. Hopefully, it will be useful 🙂

Top comments (0)