Dockerizing A NodeJS App Along With PostgreSQL Database

Arda Örkin
4 min readNov 6, 2021

--

Building an app is an adventure with many processes, algorithms, schema, design, etc. Even though it is not easy to build a robust app, publishing the project’s first release is a real pleasure. While there can be so many problems developers have to deal with throughout the production, deploying comes with its problems and questions like hows, wheres, whats… And when it comes to deployment, Docker is a powerful tool to use.

In this post, I am going to show how to deploy a NodeJS App along with PostgreSQL as a Docker image. Additionally, we are going to use Express.js and Sequelize ORM.

Here we go.

Project Structure

As the post is aiming to discuss deployment issues, we are going to pass the installation of the environment as quickly as possible. So let’s run this command in a shell:

npx express-generator example-project

It will create a project directory called example-project, let’s open it in a text editor. Then install the dependencies and run the project.

npm i && npm start//or with yarn
yarn && yarn start

The project will run on port 3000 of your localhost. Now we are going to create our database schemas using Sequelize ORM. Create a file called .sequelizerc in the project root folder and add these lines to it:

const path = require('path');module.exports = {
'config': path.resolve('config', 'config.js'),
'models-path': path.resolve('db', 'models'),
'seeders-path': path.resolve('db', 'seeders'),
'migrations-path': path.resolve('db', 'migrations')
};

Then install the sequelize library with npm or yarn. Once you have installed it, run this command:

npx sequelize-cli init

This command, using the .sequelizerc file, will create two directories. One is the config folder which includes database configurations and the other called db which has database models along with other directories that we are not going to talk about in this post.

And we need to install the pg package to use PostgreSQL in our NodeJS app.

Database Structure

In this section, we are going to configure a database structure. First, rename the config/database.json file as config/config.js. Then open the .sequelizerc file and change database.json to config.js. It helps Sequelize to find the configuration file. Change the content of the config/config.js like that:

module.exports = {
development: {
username: "postgres",
password: "postgres",
database: "postgres",
host: "localhost",
dialect: "postgres",
},
test: {
username: process.env.CI_DB_USERNAME,
password: process.env.CI_DB_PASSWORD,
database: process.env.CI_DB_NAME,
host: process.env.CI_DB_HOSTNAME,
dialect: "postgres",
},
production: {
username: process.env.PROD_DB_USERNAME,
password: process.env.PROD_DB_PASSWORD,
database: process.env.PROD_DB_NAME,
host: process.env.PROD_DB_HOSTNAME,
dialect: "postgres",
},
};

Now we are going to create our first model. Let’s run this command:

npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string

It will create a file called user.js inside the db/model directory. And by that, we created our first model.

Next, we need to start the database connection before the app starts. To do that let’s call db class which is exported from the db/models/index.js and change relevant lines like that in the bin/www file:

...const db = require("../db/models");.../**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
db.sequelize.sync({ force: false }).then(function () {
server.on("error", onError);
server.on("listening", onListening);
console.log("Database created successfully.");
});

It is time to create a route to add some users. Let’s go.

Creating Database Record

Let’s open routes/index.js and change it like that:

var express = require("express");
var router = express.Router();
const db = require("../db/models/index");
/* GET users listing. */
router.get("/", async function (req, res, next) {
const users = await db.User.findAll();
res.render("index", { users });
});
/*POST user*/
router.post("/", async function (req, res, next) {
const newUser = await db.User.create({ ...req.body });
res.json(newUser);
});
module.exports = router;

That’s it. We have created our first two routes one for creating users and the other for listing them.

Now, we need to change the frontend slightly so that we can add and list users via the app. So, change views/index.jade like that:

extends layoutblock content
h1= title
p Welcome to #{title}
form(name="add-estimation", method="post")
div.input
span.label First Name
input(type="text", name="firstName")
div.input
span.label Last Name
input(type="text", name="lastName")
div.input
span.label Email
input(type="text", name="email")
div.actions
input(type="submit", value="Add")
if users
ul
each user in users
if user
li=user.firstName + " " + user.lastName + " " + user.email

It is time to create Docker configuration files.

Docker Configuration

First, we need a Dockerfile. This is going to be like that:

FROM node:12.13.0-alpineRUN mkdir -p /opt/appWORKDIR /opt/appRUN adduser -S appCOPY . .RUN npm installRUN chown -R app /opt/appUSER appEXPOSE 3000CMD [ "npm", "start" ]

Then we need to create a docker-compose.yaml file and put these lines in it:

version: "3.7"
services:
postgres:
image: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- example-db:/var/lib/postgresql/data
example:
build:
context: .
environment:
PROD_DB_NAME: postgres
PROD_DB_USERNAME: postgres
PROD_DB_PASSWORD: postgres
PROD_DB_HOSTNAME: postgres
depends_on:
- postgres
ports:
- "3000:3000"
volumes:
example-db:

Now, we need persistent data storage that the database will use so that our data isn’t removed in case the container is going down. Persistent data storage are added as running this command:

docker volume create example-db

Now run these commands and then go to the http://localhost:3000

docker build -t example-project . && docker-compose up -d

You will see a form in the http://localhost:3000:

Let’s add a user. Then reload the page. You should see the user you added on the page.

Finally to make sure that the persistent volume works successfully, lets down and up the Docker containers with this command:

docker-compose down && docker-compose up -d

After that, if you still see the user you added in the http://localhost:3000, then congratulations, you have Dockerized a NodeJS app with a PostgreSQL database, using Express.js and Sequelize ORM!

--

--

Arda Örkin

I am a software developer. I like to tell, listen and read stories about software world