A simple Todo Application with NestJS, TypeORM, PostgreSQL, Swagger, PGadmin4, JWT, and Docker (Part 1)

NestJs & Docker Logo
NestJS & Docker

Aloha !!! This tutorial will be very basic of a development stack and required a basic understanding of Docker. But if you haven’t, don’t worry you can follow along with the instruction and will find it very easy. So let’s Jump in.

What is NestJS?
NestJS is a framework for building efficient Node.js server-side application with fully supports TypeScript and also makes use of Server frameworks Ex. Express.

What is TypeORM?
TypeORM is object-relational mapper library and can be used with TypeScript & JavaScript.

What is PostgreSQL ?
PostgreSQL is a powerful and advanced open-source relational database.

What is Swagger?
Swagger is an open-source API documentation tool.

What is PGadmin4?
PGadmin4 is a complete rewrite of pgadmin which is open source management tools for postgreSQL with a nice UI.

What is JWT?
JSON Web Token (JWT) is an open standard that defines a compact and self-contained way for securely transmitting information between parties as a JSON object and organized with HMAC algorithm.

What is Docker?
Docker is a tool designed to make it easier to create, deploy, and run applications by using containers.

So enough with the introduction, now it’s time to dive in.

N.B: In case if you haven’t docker & docker-compose on your machine, please install it first. Link- https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-20-04

Step 1: Install Nest CLI globally & create a project

npm install -g @nestjs/cli
nest new todoAppOnNestJs

Step 2: Install all necessary NPM modules before you start writing code.

npm install @nestjs/jwt @nestjs/passport @nestjs/swagger @nestjs/typeorm bcrypt class-transformer class-validator dotenv passport passport-jwt pg swagger-ui-express typeorm uuid config

Our Project folder will be like this :

- config
- node_modules
- src
- test
...

Step 3: Regarding this application, create 3 modules under src folder.

nest g module auth
nest g module user
nest g module todo

You will see those modules are added to app.module.ts

...
@Module({
imports: [
AuthModule,
TodoModule,
UserModule
],
...
})
...

For this app - app.controller.ts, app.service.ts, app.controller.spec.ts will not necessary. So delete those files & also delete lines (controllers & providers) from app.modules.ts.

...
@Module({
...
controllers: [AppController],
providers: [AppService],
})
...

Step 4: Create 4 files .env, typeorm.config.ts, docker-compose.yml & Dockerfile respectively & structure should look like this -

...
- src
...
- typeorm.config.ts
...
- .env
- docker-compose.yml
- Dockerfile
...

Step 5: let's write some code -

Update .env, docker-compose.yml & Dockerfile with below codes:

What these 3 files will do?

Whenever you run this below command, it will look for docker-compose.yml file and if it is for the first time then images (postgres & dpage/pgadmin4) will download from dockerhub and create image on your local machine and another image tusharchy/nest-and-postgres-application:latest (any name you want) will do the same also. But don’t do this now, will do later.

docker-compose build

Update main.ts & typeorm.config.ts with the below code.

N.B. In typeorm.config.ts file, entities (Line 10) can be written as below code. But static glob paths won’t work properly with webpacks.

entities: [__dirname + '/**/*.entity{.ts,.js}']

Step 6: Create all necessary entities & migrate them to the database. Go to all modules (auth, user, todo) & create an entity folder on every module. Ex.

...
- src
- auth
- entity
- user.entity.ts
- user
- entity
- user-info.entity.ts
- todo
- entity
- todo.entity.ts
...

Copy the below code & placed it in the respective entity files.

user.entity.ts file.

What is in this file?

We are using typeorm & as you can see, we placed @ Entity() (Line 6) to treat as entity & also set username column as unique (Line 7) because we don’t want any duplicate user. We create @ OneToMany() (Line 22) relationship with todo.entity.ts (Though we haven’t created it yet but we will ) & @ OneToOne() relationship with user-info.entity.ts. And the validatePassword() (Line 29) will use for the comparison of hashed passwords with a solid password for successful login.

What is { eager: true } in line 25 ?
As you can see, it is joined with user-info.entity.ts and sometimes we need combined results of selected tables in a single result like-

"user": {
"username": "abced",
"user_info": {
"id": 67,
"petName": "cat",
"photo": "profile.png",
"modified_photo": "09fe0544-1f3e-470b-8290-ad61cccf9902.png",
"address": "Earth"
}
}

In that case, we can get the userInfo table’s (user-info.entity.ts) data easily. On the other hand, { eager: false } can’t do like this.
What is Salt ?

A salt is a random string that makes the hash unpredictable. These days password encryption is not enough because anyone can decrypt it. With the help of Salt() you can do the decrypted password unpredictable. For more visit- heynode.com

user-info.entity.ts file.

todo.entity.ts file.

Step 7: All the entities have been set up. Now it’s time to migrate into the database. Before you do, need to add a few lines of code in package.json.

...
"scripts": {
...
"typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --config src/typeorm.config.ts",
"typeorm:create": "npm run typeorm migration:create -- -n",
"typeorm:generate": "npm run typeorm migration:generate -- -n",
"typeorm:run": "npm run typeorm migration:run",
"typeorm:revert": "npm run typeorm migration:revert"
},
...

Why do we add these lines?
Because we don’t want to write the below command every time whenever migration is necessary.

docker-compose run nestjs npm run ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js --config src/typeorm.config.ts migration:create -n Migration_name

Before running any command let’s create the migration folder first under src folder. Start & build the docker container with the below commands.

docker-compose up --build

N.B. You need to create server in pgadmin4 before the migration otherwise it will fail.

docker-compose run nestjs npm run typeorm:generate AnyNameYouLike

N.B. nestjs is the service name placed in the docker.compose.yml (Line 13)

You can see a file is created under the migration folder with a unique name. Now migrate to DB run -

docker-compose run nestjs npm run typeorm:run

To see the tables, execute the below command & go to localhost:8080 & provide the Username: postgres@gmail.com, Password: admin12 on pgadmin4 panel.

docker-compose up

You should able to see the tables with empty results. Maybe you also wonder about userInfoId (user table) & userId (todo table) and this is the beauty of typeorm joining & migration which is created automatically.

Step 8: Let's create dto, repository folder in both auth & user module. Stop docker by Ctrl+C & Create files -

...
- auth
- dto
- signin-credentials.dto.ts
- signup-credentials.dto.ts
- repository
- user.repository.ts
- user
- dto
- user-info.dto.ts
- repository
- user-info.repository.ts
...

What is DTO ?
DTO is an object that defines how the data will be sent through the network. In this project, you can assume a property/entity validator.

signin-credentials.dto.ts, signup-credentials.dto.ts & user.repository.ts file.

Create interface folder under auth module and then auth/interface/jwt-payload.interface.ts file -

import { UserInfo } from "../../user/entity/user-info.entity";export interface JwtPayload {    
username: string
user_info: UserInfo
}

Create controller & service in auth module-

...
- auth
- service
- auth.service.ts
- auth.controller.ts
- jwt-strategy.ts
...

You can also create controller & service using the below command but for now, hard coding will be a better approach to learn.

nest g controller auth --no-spec 
nest g service auth --no-spec

auth.controller.ts & auth.service.ts file-

N.B. ValidationPipe is provided by NestJS. Basically, it server all the validation rule which is already declared in respective DTO files.

And your auth.module.ts should look like this-

N.B. Set @ Global() (Line 10) so that we can use this module all over other modules. And forFeature() method defines which repositories are registered in the current scope.

Create jwt-strategy.ts file-

Step 9: Let's create dto, interface, repository, service & controller for user module similar to auth module’s folder structure.

...
- user
- dto
- user-info.dto.ts
- interface
- user-info.interface.ts
- service
- user-info.service.ts
- user.controller.ts
- user.module.ts
...

N.B. Create uploads folder in root directory & utils folder under src/utils

utils/file-upload.utils.ts file.

Create a file under auth/decorator/get-user.decorator.ts .

Why is this decorator ?
To get the authenticated user’s data after successful login which should serve around the application.

Step 10: Let’s check the application using swagger. Go to localhost:3000/api and Tada ! you should see the nice swagger UI.

Go to /api/auth/signup & create a account. After successful action, you should see the below image.

Let’s get login using the created account.

Now copy the access token & placed it in Authorize button’s UI (Top right).

After clicking the Authorize button you should able to do tests with other API. Please follow Part 2 for further application improvement. For complete code, go to — GitHub.

I am passionate programmer and always keep eyes on new technologies and scrutinize until get into a structure.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store