# JavaScript CRUD Rest API using Nodejs, Express, Sequelize,  Postgres, Docker and Docker Compose

Let's create a CRUD rest API in JavaScript, using:

* Node.js
    
* Express
    
* Sequelize
    
* Postgres
    
* Docker
    
* Docker Compose
    

If you prefer a video version:

<iframe width="905" height="510" src="https://www.youtube.com/embed/Uv-jMWV29rU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
---

# Intro

Here is a schema of the architecture of the application we are going to create:

![crud, read, update, delete, to a node.js app and postgres service, connected with docker compose. POstman and tableplus to test it](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/185um7lloh3h4nb99w5p.png align="left")

We will create 5 endpoints for basic CRUD operations:

* Create
    
* Read all
    
* Read one
    
* Update
    
* Delete
    

We will create a Node.js application using:

* Express as a framework
    
* Sequelize as an ORM
    

1. We will Dockerize the Node.js application
    
2. We will have a Postgres istance, we will test it with Tableplus
    
3. We will create a docker compose file to run both the services
    
4. We will test the APIs with Postman
    

---

# Step-by-step guide

Here is a step-by step guide.

create a new folder

```bash
mkdir node-crud-api
```

step into it

```bash
cd node-crud-api
```

initialize a new npm project

```bash
npm init -y
```

install the dependencies

```bash
npm i express pg sequelize
```

* express is the Node.js framework
    
* pg is a driver for a connection with a Postgres db
    
* sequelize is the ORM so we avoid typing SQL queries
    

create 4 folders

```bash
mkdir controllers routes util models
```

Open the folder with your favorite IDE. If you have Visual Studio Code, you can type this from the terminal:

```bash
code .
```

You should now have a folder similar to this one:

![controller, routes, model, util folder, a node_modules and package.json file, package-lock.json file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kx8h0hlg7gbnjobrc276.png align="left")

Now let's start coding.

## Database connection

Create a file called "database.js" inside the "util" folder.

This file will contain the internal configuration to allow the connection between the Node.js application and the running Postgres instance.

Populate the util/database.js file

```javascript
const Sequelize = require('sequelize');

const sequelize = new Sequelize(
    process.env.PG_DB,
    process.env.PG_USER,
    process.env.PG_PASSWORD,
    {
        host: process.env.PG_HOST,
        dialect: 'postgres',
    }
);

module.exports = sequelize;
```

## User model

Create a file called "user.js" inside the "models" folder.

This file will contain the model, in this case a user with an auto-incremented id, a name and an email.

Populate the models/user.js file:

```javascript
const Sequelize = require('sequelize');
const db = require('../util/database');

const User = db.define('user', {
    id: {
        type: Sequelize.INTEGER,
        autoIncrement: true,
        allowNull: false,
        primaryKey: true
    },
    name: Sequelize.STRING,
    email: Sequelize.STRING
});

module.exports = User;
```

## Controllers

This is the file that contains all the functions to execute in order to interact with the database and have the 4 basic functionalities:

Create a file called "users.js" inside the "controllers" folder

Populate the controllers/users.js file

```javascript
const User = require('../models/user');

// CRUD Controllers

//get all users
exports.getUsers = (req, res, next) => {
    User.findAll()
        .then(users => {
            res.status(200).json({ users: users });
        })
        .catch(err => console.log(err));
}

//get user by id
exports.getUser = (req, res, next) => {
    const userId = req.params.userId;
    User.findByPk(userId)
        .then(user => {
            if (!user) {
                return res.status(404).json({ message: 'User not found!' });
            }
            res.status(200).json({ user: user });
        })
        .catch(err => console.log(err));
}

//create user
exports.createUser = (req, res, next) => {
  const name = req.body.name;
  const email = req.body.email;
  User.create({
    name: name,
    email: email
  })
    .then(result => {
      console.log('Created User');
      res.status(201).json({
        message: 'User created successfully!',
        user: result
      });
    })
    .catch(err => {
      console.log(err);
    }); 
}

//update user
exports.updateUser = (req, res, next) => {
  const userId = req.params.userId;
  const updatedName = req.body.name;
  const updatedEmail = req.body.email;
  User.findByPk(userId)
    .then(user => {
      if (!user) {
        return res.status(404).json({ message: 'User not found!' });
      }
      user.name = updatedName;
      user.email = updatedEmail;
      return user.save();
    })
    .then(result => {
      res.status(200).json({message: 'User updated!', user: result});
    })
    .catch(err => console.log(err));
}

//delete user
exports.deleteUser = (req, res, next) => {
  const userId = req.params.userId;
  User.findByPk(userId)
    .then(user => {
      if (!user) {
        return res.status(404).json({ message: 'User not found!' });
      }
      return User.destroy({
        where: {
          id: userId
        }
      });
    })
    .then(result => {
      res.status(200).json({ message: 'User deleted!' });
    })
    .catch(err => console.log(err));
}
```

## Routes

Create a file called "users.js" inside the "routes" folder.

Populate the routes/users.js file

```javascript
const controller = require('../controllers/users');
const router = require('express').Router();

// CRUD Routes /users
router.get('/', controller.getUsers); // /users
router.get('/:userId', controller.getUser); // /users/:userId
router.post('/', controller.createUser); // /users
router.put('/:userId', controller.updateUser); // /users/:userId
router.delete('/:userId', controller.deleteUser); // /users/:userId

module.exports = router;
```

---

## Index file

To run our application we need to create on more file at the root level. this is the file that will be executed by the docker container.

in the root folder, create a file called index.js

Populate the "index.js file":

```javascript
const express = require('express');
const bodyparser = require('body-parser');
const sequelize = require('./util/database');
const User = require('./models/user');

const app = express();

app.use(bodyparser.json());
app.use(bodyparser.urlencoded({ extended: false }));

app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  next();
});

//test route
app.get('/', (req, res, next) => {
  res.send('Hello World');
});

//CRUD routes
app.use('/users', require('./routes/users'));

//error handling
app.use((error, req, res, next) => {
  console.log(error);
  const status = error.statusCode || 500;
  const message = error.message;
  res.status(status).json({ message: message });
});

//sync database
sequelize
  .sync()
  .then(result => {
    console.log("Database connected");
    app.listen(3000);
  })
  .catch(err => console.log(err));
```

---

# Docker Part

Let's create 3 more files at the root level:

* .dockerignore (it starts with a dot)
    
* Dockerfile (capital D)
    
* docker-compose.yml
    

The structure should look like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dggtixcespkaf85xzsl7.png align="left")

the .dockerignore will contain a single line:

```bash
node_modules
```

![the .dockerignore file with a single line: node_modules](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mh72v3t2h21o8uinmisw.png align="left")

---

## The Dockerfile

To create a Docker image we need a simple yet powerfule file. That's called "Dockerfile" (capital D). We might use a different name but let's keep things simple for now.

```yaml
FROM node:14

# Create app directory
WORKDIR /app

COPY package*.json ./

RUN npm install

# Bundle app source
COPY . .

EXPOSE 3000

CMD [ "node", "index.js" ]
```

---

## Docker compose file

To run multiple services an easy way is to create a file called "docker-compose.yml"

The docker-compose.yml file:

```yml
version: "3.9"

services:
  node_app:
    container_name: node_app
    build: .
    image: francescoxx/node_live_app
    ports:
      - "3000:3000"
    environment:
      - PG_DB=node_live_db
      - PG_USER=francesco
      - PG_PASSWORD=12345
      - PG_HOST=node_db
    depends_on:
      - node_db
  
  node_db:
    container_name: node_db
    image: postgres:12
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=node_live_db
      - POSTGRES_USER=francesco
      - POSTGRES_PASSWORD=12345
    volumes:
      - node_db_data:/var/lib/postgresql/data

volumes:
  node_db_data: {}
```

---

# Build the Docker image and run the docker containers

### Run Postgres in a container

First, let's run the postgres container:

```bash
docker compose up -d node_db
```

To check the logs, we can type:

```bash
docker compose logs
```

you should get an output similar to this one:

![..... 2023-02-12 13:07:41.342 UTC [1] LOG:  database system is ready to accept connections](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/icy9s09od5sdja90drft.png align="left")

if we see "database system is ready to accept connections" we are good to go!

Let's test it using TablePlus.

Click on the + to create a new connection

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uhg0bfzo6wdsmrsy8yxo.png align="left")

copy the values from the docker-compose.yml file. (password is 12345 if you left the values as they are)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4itnaoht5klvzsv17sua.png align="left")

### Build and run the Docker service

Second, let's build our Docker iamge:

```plaintext
docker compose build
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dk6fb1pal8f4uky90azw.png align="left")

finally, let's start the service:

```bash
docker compose up node_app
```

This should be the output on the terminal

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vjvfio6uws4ec68uhq5x.png align="left")

### Test the app with Postman

Let's test the app using Postman.

Make a GET request to [localhost:3000](http://localhost:3000)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aey33b5xr0y9snvqueo5.png align="left")

Make a GET request to [localhost:3000/users](http://localhost:3000/users)

We should have an empty array as a response

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5b16b4nfdai8vfyx3dq8.png align="left")

Let's create 3 users: aaa, bbb, and ccc

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f65lhud0t165u5ldax90.png align="left")

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqqjbcmqzmf1hj4gcok1.png align="left")

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bwfgtvg9dj36oo1j3yn5.png align="left")

Let's check again all the users:

Make a GET request to [localhost:3000/users](http://localhost:3000/users)

We should see 3 users:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6izo1s8yisswgo8r7mph.png align="left")

Let's get a single user, for example the user 2

Make a GET request to [localhost:3000/users/2](http://localhost:3000/users/2)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1as9y9ucmw803f9xsato.png align="left")

Let's update an existing user, for example the same user 2

Make a PUT reqeust to [localhost:3000/users/2](http://localhost:3000/users/2) with a different body

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tvcfrgcpyeo5fai54qf4.png align="left")

Finally, let's delete the user number 3

Make a DELETE reuqest to [localhost:3000/users/3](http://localhost:3000/users/3)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5do5l6uza4mubbeix9zs.png align="left")

We can also check the values using TablePlus

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbtdkl3wj4wd388fks4m.png align="left")

# Conclusion

This is a basic example of building a CRUD rest API using Node.js, Express, Sequelize, Postres, Docker, and Docker Compose.

All the code is available in the GitHub repository: [https://github.com/FrancescoXX/crud-node-live](https://github.com/FrancescoXX/crud-node-live)

For a video version: If you prefer a video version:

<iframe width="905" height="510" src="https://www.youtube.com/embed/Uv-jMWV29rU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

That's all. If you have any questions, drop a comment below.

[Francesco](https://francescociulla.com)
