DEV Community

Cover image for Step-by-Step Guide: Setting Up a NestJS Application with Docker and PostgreSQL
Tosin Moronfolu
Tosin Moronfolu

Posted on • Updated on

Step-by-Step Guide: Setting Up a NestJS Application with Docker and PostgreSQL

NestJS, PostgreSQL, and Docker are three powerful tools that can help you build robust web applications quickly and easily. NestJS is a modern, progressive Node.js framework that allows you to build server-side applications using TypeScript. PostgreSQL is a powerful open-source database management system that provides reliable data storage and retrieval. Docker is a containerization platform that simplifies the deployment and management of applications.

These three tools provide a powerful toolkit for building scalable and efficient web applications. This article will give a beginner-friendly guide to using NestJS, PostgreSQL, and Docker to build a web application.

Repository

Nest Logo

A progressive Node.js framework for building efficient and scalable server-side applications.



NPM Version
Package License
NPM Downloads
CircleCI
Coverage
Discord
Backers on Open Collective
Sponsors on Open Collective

Support us

Description

Nest framework TypeScript starter repository.

Installation

$ npm install
Enter fullscreen mode Exit fullscreen mode

Running the app

# development
$ npm run start

# watch mode
$ npm run start:dev

# production mode
$ npm run start:prod
Enter fullscreen mode Exit fullscreen mode

Test

# unit tests
$ npm run test

# e2e tests
$ npm run test:e2e

# test coverage
$ npm run test:cov
Enter fullscreen mode Exit fullscreen mode

Support

Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please read more here.

Stay in touch

License

Nest is MIT licensed.

Prerequisite

  • Basic understanding of TypeScript, PostgreSQL, and RESTful APIs
  • Docker Desktop installed on your computer (run the docker -v command to verify the installation); if not, install it from here

Setting Up a NestJS Application

We need node and its package manager npm to initialize a new project.

To install Node, we go to the Nodejs website and follow the instructions. We verify Node.js’ installation using the terminal command below:

node -v
v18.15.0 //node version installed
Enter fullscreen mode Exit fullscreen mode

The result shows the version of Node.js we installed on our computer.

Next we’ll install NestJS globally and initialise a new project by running the command below in our terminal:

$ npm i -g @nestjs/cli

$ nest new nest-docker-postgres
Enter fullscreen mode Exit fullscreen mode

Make sure to choose npm as the package manager from the prompt.

Start the development server using the command below and visit http://localhost:3000/ to see the application running:

$ npm run start:dev
Enter fullscreen mode Exit fullscreen mode

Containerizing the Application

To containerize our NestJS application, we first create a file named Dockerfile in the root of our project and then a docker-compose.yml file also in the root of our project.

The Dockerfile sets up a container for running the application. It installs the necessary dependencies, copies the application code into the container, builds the application, and starts the server using the production build.

The docker-compose file sets up a multi-container environment. In it, we would run the NestJS app with PostgreSQL and pgAdmin.

Dockerfile

FROM node:18

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

CMD [ "npm", "run", "start:dev" ]
Enter fullscreen mode Exit fullscreen mode

Here's what each command in this file does:

  1. FROM node:18: Specifies the base image for the Docker container. We use the official Node.js 18.x image as the base in this case.

  2. WORKDIR /app: Sets the working directory inside the container to /app. This is where the application code will be copied and where the container will run from.

  3. COPY package*.json ./: Copies the package.json and package-lock.json files from the current directory on the host machine to the /app directory in the container. The * in package*.json allows us to copy both files at once.

  4. RUN npm install: Installs the application dependencies in the container. This step uses the npm install command to install the dependencies listed in package.json.

  5. COPY . .: Copies the application code from the host machine to the /app directory in the container.

  6. RUN npm run build: The npm run build command is used to build the application in the container. This command will typically create a dist folder with the production build of the application.

  7. CMD ["npm", "run", "start:dev"]: Specifies the command to run when the container starts. In this case, we're running the npm run start:dev command, which will start the server in development mode. This command watches for changes in the code and automatically rebuilds the container when changes are detected. This allows for faster development cycles, as changes to the code can be quickly tested in the container without manual rebuilding or restarting.

docker-compose.yml

version: '3.5'

services:
  db:
    image: postgres
    restart: always
    environment:
      - POSTGRES_PASSWORD=postgres
    container_name: postgres
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    ports:
      - '5432:5432'

  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: nest-docker-postgres
    environment:
      - PORT=${PORT}
    ports:
      - '3000:3000'
    depends_on:
      - db
    volumes:
      - ./src:/app/src

  pgadmin:
    image: dpage/pgadmin4
    restart: always
    container_name: nest-pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=admin@admin.com
      - PGADMIN_DEFAULT_PASSWORD=pgadmin4
    ports:
      - '5050:80'
    depends_on:
      - db
Enter fullscreen mode Exit fullscreen mode

In this file:

  • version: This field at the beginning of a Docker Compose file specifies the version of the Compose file format we’re using.

  • db: This service sets up a PostgreSQL database using the official postgres image from Docker Hub. It sets the password for the postgres user to postgres, creates a named volume pgdata for storing the database data, and maps the container port 5432 to the host port 5432. The restart: always option ensures that the container will automatically restart if it crashes or is stopped.

  • app: This service builds a Docker image for the NestJS app using the Dockerfile in the current directory. It sets the container name to nest-docker-postgres, sets the environment variable PORT to the value of the host PORT environment variable, maps the container port 3000 to the host port 3000, and mounts the src directory on the host to the /app/src directory in the container. The depends_on option specifies that this service depends on the db service, meaning that the db container will be started before the app container.

  • pgadmin: This service sets up pgAdmin, a web-based PostgreSQL administration tool, using the dpage/pgadmin4 image from Docker Hub. It sets the container name to nest-pgadmin4, sets the default email and password for the pgAdmin login, maps the container port 80 to the host port 5050, and specifies that this service depends on the db service.

We also need to add a new file called .dockerignore. It specifies files and directories that .dockerignore should exclude from a Docker build context.

.dockerignore

Dockerfile
.dockerignore
node_modules
npm-debug.log
dist
Enter fullscreen mode Exit fullscreen mode

Building the containers

The next thing we need to do is build to run our app in Docker - building the containers. Run the command below to containerize the application:

$ docker compose up
Enter fullscreen mode Exit fullscreen mode

The command looks for the services specified in the docker-compose.yml file and creates containers for them. If the images required by those services are unavailable locally, Docker will pull them from Docker Hub or any other configured registry. If the images are already available locally, Docker will use them instead of pulling them again.

Once the containers are up and running, we can access the NestJS application by visiting http://localhost:3000 in our web browser and pgAdmin by visiting http://localhost:5050 in our web browser.

Login to pgAdmin by using the email and password we specified earlier in the docker-compose.yml file, and then we will see this:

pgAdmin dashboard

Setting up pgAdmin and PostgreSQL Server

To connect to the PostgreSQL server from PgAdmin, we need to create a server object in PgAdmin with the details of the PostgreSQL server.

Here are the steps to create a server in PgAdmin:

  1. Open PgAdmin in the web browser by visiting http://localhost:5050 (assuming we're using the default configuration in the docker-compose.yml file).
  2. Log in using your email and password in the docker-compose.yml file for the pgadmin service.
  3. In the left-hand sidebar, click Servers to expand the Servers menu.
  4. Right-click on Servers and select Register -> Server.
  5. In the General tab of the Create - Server dialog, we can give the server a name of our choice.
  6. In the Connection tab, fill in the following details:
    • Host name/address: db
    • Port: 5432
    • Maintenance database: postgres
    • Username: postgres
    • Password: postgres
  7. Click Save to save the server configuration.

Note: Since the PostgreSQL server is running in a Docker container, the hostname/address would be the name of the Docker service for the database container as defined in the docker-compose.yml file. By default, the name of the service becomes the hostname/address of the container within the Docker network.

We should now see the server we created in the left-hand sidebar of PgAdmin. We can expand the server to see the databases and other objects within it.

PgAdmin dashboard

Connecting to PostgreSQL Server from NestJS

Nest provides the @nestjs/typeorm package for integrating with SQL and NoSQL databases. Install the packages using the command below:

$ npm install --save @nestjs/typeorm typeorm pg
Enter fullscreen mode Exit fullscreen mode

Next, in the app.module.ts file, add the following code in the imports array:

    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'db',
      port: 5432,
      username: 'postgres',
      password: 'postgres',
      database: 'postgres',
      entities: [],
      synchronize: true,
      autoLoadEntities: true,
    }),
Enter fullscreen mode Exit fullscreen mode

We import the TypeOrmModule from the @nestjs/typeorm package.

We pass an options object to the TypeOrmModule to configure the PostgreSQL connection details. The type option specifies the database management system being used, PostgreSQL. The host, port, username, password, and database options provide the connection details for the PostgreSQL server.

We use the entities option to specify the entities that the application uses. Entities represent the data models in the application and can be used to query the database. We don't specify entities in this case because we dynamically load them by setting autoLoadEntities to true.

We set the synchronize option to true, which means that TypeORM will automatically generate database tables based on the entities. However, this option should be used with caution in production because it can cause data loss and conflicts.

To rebuild our app container because of the changes made, we execute the command below:

$ docker compose up --build
Enter fullscreen mode Exit fullscreen mode

And voila, our app is connected to the PostgreSQL server running in Docker. We can now start to create entities and build APIs on top of them.

Conclusion

In conclusion, we have seen how to set up a NestJS application with Docker and PostgreSQL in a step-by-step guide. Using Docker and PostgreSQL in combination with NestJS can make your web application development faster and more efficient.

Resources

Top comments (6)

Collapse
 
cholasimmons profile image
Chola

Great tut! I haven't touched Docker in a while, I did however encounter "file write permission" errors, I'm guessing this is due to using /app instead of /usr/src/app as a working directory?

Collapse
 
deerhound579 profile image
Sixian Li

Great article. Thanks for sharing!

Collapse
 
chukwutosin_ profile image
Tosin Moronfolu

It's my pleasure

Collapse
 
deleanuradu profile image
Radu Deleanu

Really simple and easy to understand!
Thanks for sharing this with us!

Collapse
 
chukwutosin_ profile image
Tosin Moronfolu

I'm glad it helped you. My pleasure

Collapse
 
justageek profile image
Brian Smith

Would you be able to highlight the changes needed to use mysql? That's all my company uses at this time.