Building a Full-Stack Web Application with Angular and Nest.js
In modern web development, creating a seamless user experience often requires a combination of frontend and backend technologies that work in harmony. Angular and Nest.js, both powered by TypeScript, offer a robust and scalable solution for building full-stack web applications. In this tutorial, we’ll explore how to integrate Angular for the frontend and Nest.js for the backend, creating a cohesive web application from start to finish.
Getting Started
Before diving into the code, ensure you have Node.js and npm (Node Package Manager) installed on your machine. We’ll use npm to install Angular CLI for frontend development and Nest CLI for backend development.
# Install Angular CLI globally npm install -g @angular/cli # Install Nest CLI globally npm install -g @nestjs/cli
Setting Up the Backend with Nest.js
Let’s start by setting up our backend using Nest.js. Navigate to your desired directory and run the following commands to create a new Nest.js project:
# Create a new Nest.js project nest new backend
Once the project is created, navigate into the project directory:
cd backend
Now, let’s generate a new controller and service using the Nest CLI:
# Generate a new controller nest generate controller cats # Generate a new service nest generate service cats
This will create a cats.controller.ts
and a cats.service.ts
file in your project, which we’ll use to manage our backend logic.
Setting Up the Frontend with Angular
Next, let’s create the frontend of our application using Angular. In your desired directory, run the following command:
# Create a new Angular project ng new frontend
Navigate into the newly created project directory:
cd frontend
Integrating Backend with Frontend
Now that we have both our backend and frontend projects set up, let’s integrate them together.
In your Angular project, open the src/environments/environment.ts
file and update the environment
object with your backend API URL:
export const environment = { production: false, apiUrl: 'http://localhost:3000', // Update this with your backend URL };
Creating Components in Angular
Let’s create an Angular component to interact with our backend. Run the following command to generate a new component:
ng generate component cat-list
This will create a cat-list
component in your Angular project.
Making HTTP Requests
Open the cat-list.component.ts
file in your Angular project and add the following code to make an HTTP request to your backend:
import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { environment } from '../../environments/environment'; @Component({ selector: 'app-cat-list', templateUrl: './cat-list.component.html', styleUrls: ['./cat-list.component.css'] }) export class CatListComponent implements OnInit { cats: any[]; constructor(private http: HttpClient) { } ngOnInit(): void { this.http.get(`${environment.apiUrl}/cats`).subscribe((response: any[]) => { this.cats = response; }); } }
Displaying Data in Angular Component
Open the cat-list.component.html
file in your Angular project and add the following code to display the fetched data:
<ul> <li *ngFor="let cat of cats"> {{ cat.name }} </li> </ul>
Running the Application
Start your Nest.js backend server:
npm run start:dev
Start your Angular frontend server:
ng serve
Navigate to http://localhost:4200
in your browser to see the frontend of your application fetching data from the backend.
Full-stack application. Here are some ideas to enhance both the frontend and backend components:
Backend (Nest.js)
- Authentication and Authorization: Implement authentication and authorization mechanisms using libraries like Passport.js and JWT (JSON Web Tokens) to secure your API endpoints.
- Database Integration: Integrate a database (e.g., MongoDB, PostgreSQL) with Nest.js using TypeORM or Mongoose for persistent data storage.
- Validation and Error Handling: Implement validation for incoming requests using class-validator and handle errors gracefully with exception filters and middleware.
- Pagination and Filtering: Add pagination and filtering options to API endpoints for better data management, especially when dealing with large datasets.
- WebSocket Support: Implement WebSocket support using libraries like Socket.io for real-time communication between clients and the server.
Frontend (Angular)
- User Authentication: Create a user authentication system with features like signup, login, logout, and password recovery.
- User Interface Enhancements: Improve the user interface with responsive design, animations, and material design components from Angular Material.
- Forms and Validation: Implement forms with reactive forms or template-driven forms and add client-side validation using Angular’s built-in validators.
- State Management: Integrate state management libraries like NgRx or Akita for managing complex application state and handling data flow efficiently.
- File Uploads: Add support for file uploads using Angular HttpClient to upload files to the backend server and display file previews or progress indicators.
- Internationalization (i18n): Implement internationalization support using Angular’s built-in i18n features to make your application multilingual.
Additional Features
- User Profiles: Allow users to create profiles with details like name, email, profile picture, and bio. Users can view and edit their profiles.
- Search Functionality: Implement search functionality to allow users to search for specific items or entities within the application.
- Notifications: Implement a notification system to notify users of important events or updates within the application.
- Social Sharing: Add social sharing buttons to allow users to share content from the application on social media platforms.
- Data Visualization: Integrate charting libraries like Chart.js or D3.js to visualize data in the form of charts or graphs for better insights.
By incorporating these additional features and enhancements, you can create a more comprehensive and polished full-stack web application that provides a rich user experience while leveraging the power and flexibility of Angular and Nest.js.
Some additional features and functionalities to our sample full-stack application, incorporating authentication, database integration, forms, file uploads, and user profiles.
Backend (Nest.js)
Authentication and Authorization
// auth.module.ts import { Module } from '@nestjs/common'; import { AuthService } from './auth.service'; import { JwtModule } from '@nestjs/jwt'; import { jwtConstants } from './constants'; @Module({ imports: [ JwtModule.register({ secret: jwtConstants.secret, signOptions: { expiresIn: '1d' }, }), ], providers: [AuthService], exports: [AuthService], }) export class AuthModule {}
// auth.service.ts import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; @Injectable() export class AuthService { constructor(private readonly jwtService: JwtService) {} async login(user: any) { const payload = { username: user.username, sub: user.userId }; return { access_token: this.jwtService.sign(payload), }; } }
Database Integration (MongoDB with Mongoose)
// cats.service.ts import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; import { Cat } from './interfaces/cat.interface'; import { CreateCatDto } from './dto/create-cat.dto'; @Injectable() export class CatsService { constructor(@InjectModel('Cat') private readonly catModel: Model<Cat>) {} async create(createCatDto: CreateCatDto): Promise<Cat> { const createdCat = new this.catModel(createCatDto); return createdCat.save(); } async findAll(): Promise<Cat[]> { return this.catModel.find().exec(); } }
Frontend (Angular)
User Authentication
// auth.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { environment } from '../environments/environment'; @Injectable({ providedIn: 'root' }) export class AuthService { constructor(private http: HttpClient) { } login(credentials: { username: string, password: string }) { return this.http.post<any>(`${environment.apiUrl}/auth/login`, credentials); } }
User Interface Enhancements
// app.component.ts import { Component } from '@angular/core'; import { AuthService } from './auth.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(private authService: AuthService) {} login() { this.authService.login({ username: 'username', password: 'password' }).subscribe(response => { console.log(response.access_token); }); } }
Forms and Validation
<!-- login.component.html --> <form (ngSubmit)="login()" #loginForm="ngForm"> <input type="text" name="username" ngModel required> <input type="password" name="password" ngModel required> <button type="submit" [disabled]="loginForm.invalid">Login</button> </form>
File Uploads
// file-upload.component.ts import { Component } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { environment } from '../environments/environment'; @Component({ selector: 'app-file-upload', templateUrl: './file-upload.component.html', styleUrls: ['./file-upload.component.css'] }) export class FileUploadComponent { constructor(private http: HttpClient) { } uploadFile(event: any) { const file = event.target.files[0]; const formData = new FormData(); formData.append('file', file); this.http.post(`${environment.apiUrl}/upload`, formData).subscribe(response => { console.log('File uploaded successfully'); }); } }
User Profiles
// user.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { environment } from '../environments/environment'; @Injectable({ providedIn: 'root' }) export class UserService { constructor(private http: HttpClient) { } getUserProfile(userId: string) { return this.http.get(`${environment.apiUrl}/users/${userId}`); } }
By integrating these additional features and functionalities into your full-stack application, you can create a more comprehensive and engaging user experience while leveraging the power of Angular and Nest.js.