Step-by-Step Guide: Application Using NestJs and Angular
NestJs is, in my opinion, a great solution for Angular developers (like me) to start coding in a NodeJs environment as it is heavily based on Angular.
Join the DZone community and get the full member experience.
Join For FreeJust a few days before Christmas, I came along the NestJs framework. I always wanted to give NodeJs development a try, 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 frontend, NestJs backend, and Postgres database. To integrate with the 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. I will set up first the expense category for this article.
Server-Side Application With NestJs
The first step is to set up and create the application using the following commands:
npm i -g @nestjs/cli
nest new budget-be
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 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.
Set Up 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:
xxxxxxxxxx
export class ExpenseCategoryDto {
readonly id: number;
readonly category: string;
}
2. Expense Category Entity
The Entity class marked with the @Entity
decorator corresponds to an entity in the database. The expense category entity will be:
xxxxxxxxxx
@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 for receiving 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:
xxxxxxxxxx
@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. It is marked with the @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.
xxxxxxxxxx
@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 in Angular. I will define a service using the @Injectable
decorator and will declare it under Providers in the Expense Category Module. Here, I will implement all the methods to create, get, find, update, and delete what I have used inside the Controller.
xxxxxxxxxx
@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.
xxxxxxxxxx
@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:
ng new budget-fe
ng g c expense-category
...we will create the budget management application and the expense category component. In order to connect to the server side, I will configure proxy.conf.json
as below:
xxxxxxxxxx
{
"/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, we will update the expense-category-component
to get the data and add some actions to the UI such as create, modify, and delete.
xxxxxxxxxx
@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.
xxxxxxxxxx
<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:
xxxxxxxxxx
@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 Angular developers (like me) to start coding in a 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”
Opinions expressed by DZone contributors are their own.
Comments