Step by step guide: Application using NestJs and Angular

Just a few days before Christmas I came along with NestJs framework. Always wanted to give a try to NodeJs development, but the idea of a javascript backend seemed to keep me away.

NestJs is a framework for building Node.js server side applications and supports Typescript. Since almost all my experience is with Angular and NestJs provides an out-of-the box application architecture heavily inspired by Angular, it seemed like all the boxes were checked and I had to give it a try. For more information please visit NestJs Official Documentation.

In this post I will show you how to create a simple application with Angular Front End, NestJs Back End and Postgres Database. To integrate with database Nest uses TypeORM and we will need to specify postgres when installing TypeORM.

Application Description

I will create a demo application that will manage the user budget. The user can create, update, view and delete expenses and incomes. For each expense there will be an expense category and I will set up first the expense category for this article.

Server Side Application with NestJs

First step is to set up and create the application using the following commands:

  1. npm i -g @nestjs/cli
  2. nest new budget-be
  3. npm install — save @nestjs/typeoprm typeorm postgres

At this point we are ready to start working with the server side application. When creating the server side application by default it is created an App Module, App Controller and App Service.

Connecting to the database

I have created the budget-management database in Postgres. In App Module we will need to set up the connection to the database by specifying it inside TypeOrmModule in the import.

TypeOrmModule.forRoot({
type: "postgres",
host: "localhost",
port: 5432,
username: "postgres",
password: "root",
database: "budget-management",
entities: [ExpenseCategory],
synchronize: true
})

Inside the TypeOrmModule import I have specified the host, username, database name and password according to my definitions. At this point we are ready to start implementing the Expense Category Module.

Setup Expense Category Module

Here will show you steps to create Expense Category Module that will be responsible for all the requests coming to ‘expense-category’ from the front end.

  1. Expense Category Dto

First I will create the Expense Category DTO (Data Transfer Object) which determines how the data will be sent over the network. We will use it inside the controller later. For this example it will be a very simple object containing only an id and a category. The implementation is done as follows:

export class ExpenseCategoryDto {
readonly id: number;
readonly category: string;
}

2. Expense Category Entity

Entity class marked with @Entity decorator corresponds to an entity in the database. The expense category entity will be:

@Entity()
export class ExpenseCategory {
@PrimaryGeneratedColumn()
id: number;

@Column()
category: string;
}

In order to use the entity we should specify it inside the TypeOrmModule.

3. Expense Category Controller

Controllers are responsible to receive requests and return a response to the client. It is the routing mechanism that decides which controller should handle the incoming request. My application will display a list of expense categories that the user can update, delete or create a new one. So I will need to manage in my controller all these types of requests. Here is the controller I implemented:

@Controller('expense-category')
export class ExpenseCategoryController {

constructor(private expenseCategoryService: ExpenseCategoryService){}

@Get()
getExpenseCategories() {
return this.expenseCategoryService.find();
}

@Post()
create(@Body() expenseCategory: ExpenseCategoryDto) {
return this.expenseCategoryService.create(expenseCategory);
}

@Get(':id')
findOne(@Param('id') id: string) {
return this.expenseCategoryService.findOne(id);
}

@Put()
@ApiBody({ type: ExpenseCategoryDto })
update(@Body() expenseCategory: ExpenseCategoryDto) {
return this.expenseCategoryService.update(expenseCategory);
}

@Delete(':id')
remove(@Param('id') id: number) {
this.expenseCategoryService.remove(id);
}
}

4. Expense Category Repository

TypeORM supports the repository design pattern, for this reason the Expense Category Entity will have its own repository. To implement a custom repository we need to extend Repository from typeorm and it is marked with @EntityRepository decorator. The implementation is as below. I have implemented this just to try it, but even the default repository is fine for this case.

@EntityRepository(ExpenseCategory)
export class ExpenseCategoryRepository extends Repository<ExpenseCategory> {
createExpenseCategory = async (expenseCategoryDto: ExpenseCategoryDto) => {
return await this.save(expenseCategoryDto);
};
}

5. Expense Category Service

Dependency Injection works the same as Angular. I will define a service using @Injectable decorator and will declare it under Providers in Expense Category Module. Here I will implement all the methods to create, get, find, update and delete that I have used inside the controller.

@Injectable()
export class ExpenseCategoryService {

constructor(
@InjectRepository(ExpenseCategoryRepository) private readonly expenseCategoryRepository: ExpenseCategoryRepository){}

find() {
return this.expenseCategoryRepository.find();
}

create(expenseCategory: ExpenseCategoryDto) {
return this.expenseCategoryRepository.insert(expenseCategory);
}

findOne(id: string) {
return this.expenseCategoryRepository.findOne(id);
}

update(expenseCategory: ExpenseCategoryDto) {
return this.expenseCategoryRepository.save(expenseCategory);
}

remove(id: number) {
this.expenseCategoryRepository.delete(id);
}
}

6. Expense Category Module

At this point we have set up the expense category entity, controller, service, repository and dto. We will include this in the Expense Category Module which later will be imported in the App Module and we can consider the server side implementation finished.

@Module({
imports: [
TypeOrmModule.forFeature(
[ExpenseCategory, ExpenseCategoryRepository]
)],
controllers: [ExpenseCategoryController],
providers: [ExpenseCategoryService]
})
export class ExpenseCategoryModule {}

Front End Application with Angular

After setting up the server side application, I will create a new Angular Application that will connect to NestJs Application to display and modify expense categories. I will not get in deep details regarding the Angular implementation, but rather give you a general implementation.

By executing:

  1. ng new budget-fe
  2. ng g c expense-category

will create the budget management application and the expense category component. In order to connect to server side I will configure proxy.conf.json as below:

{
"/api/*": {
"target": "http://localhost:3000",
"secure": false,
"logLevel": "debug",
"changeOrigin": true,
"pathRewrite": {
"^/api": ""
}
}
}

Implementing CRUD in Angular for Expense Category.

First I will implement the service that will handle all the requests as below:

After implementing the service will update the expense-category-component to get the data and add some actions to the ui such as: create, modify and delete.

@Injectable()
export class ExpenseCategoryService {
static URL = '/api/expense-category';

constructor(public http: HttpClient){}

getCategories(): Observable<ExpenseCategoryModel[]> {
return this.http.get<ExpenseCategoryModel[]>
ExpenseCategoryService.URL);
}

createCategory(category: ExpenseCategoryModel) {
return this.http.post(ExpenseCategoryService.URL, category);
}

modifyCategory(category: ExpenseCategoryModel): Observable<ExpenseCategoryModel> {
return this.http.put<ExpenseCategoryModel>(ExpenseCategoryService.URL, category);
}

deleteCategory(id: number): Observable<any> {
return this.http.delete(ExpenseCategoryService.URL + `/${id}`);
}
}

The template for this example will be a list of items displaying the id and name of the category.

<button mat-mini-fab (click)="add()">
<mat-icon>add</mat-icon>
</button>
<mat-list *ngIf="expenses$ | async as expenses">
<mat-list-item *ngFor="let expense of expenses">
<div mat-line>{{expense.id}}
<button mat-mini-fab (click)="modify(expense.id)" >
<mat-icon>edit</mat-icon>
</button>
<button mat-mini-fab (click)="delete(expense.id)">
<mat-icon>delete</mat-icon>
</button>
</div>
<div mat-line>{{expense.category}} </div>
</mat-list-item>
</mat-list>

Component definition will be:

@Component({
selector: 'app-expense-category',
templateUrl: './expense-category.component.html',
styleUrls: ['./expense-category.component.scss']
})
export class ExpenseCategoryComponent implements OnInit {

expenses$: Observable<any[]>;

constructor(private service: ExpenseCategoryService) { }

ngOnInit(): void {
this.getCategories();
}

getCategories = () => {
this.expenses$ = this.service.getCategories();
}

add(): void {
const newCategory = {id: null, category: 'New Category'};
this.service.createCategory(newCategory);
}

modify(id: number) {
const updatedCategory = {id, category: 'Modified Category'};
this.service.modifyCategory(updatedCategory);
}

delete(id: number) {
this.service.deleteCategory(id).subscribe(this.getCategories);
}
}

In the component I am injecting the service in order to get, update and delete expense categories. As I mentioned before the front end is a simple implementation just to see that the applications are connecting with each other and I have correctly implemented the server side.

Conclusions

NestJs is in my opinion a great solution for the Angular Developers (like me) to start coding in NodeJs environment as it is heavily based on Angular. Creating a simple application, both server side and front end was easy and quite interesting for me. I will continue to work and experiment in NestJs and will give you more updates. I would highly recommend giving it a try because as they say: “The proof of the pudding is in the eating”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s