Frontend Angular with NestJs as Backend

Estimated read time 7 min read

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)

  1. Authentication and Authorization: Implement authentication and authorization mechanisms using libraries like Passport.js and JWT (JSON Web Tokens) to secure your API endpoints.
  2. Database Integration: Integrate a database (e.g., MongoDB, PostgreSQL) with Nest.js using TypeORM or Mongoose for persistent data storage.
  3. Validation and Error Handling: Implement validation for incoming requests using class-validator and handle errors gracefully with exception filters and middleware.
  4. Pagination and Filtering: Add pagination and filtering options to API endpoints for better data management, especially when dealing with large datasets.
  5. WebSocket Support: Implement WebSocket support using libraries like Socket.io for real-time communication between clients and the server.

Frontend (Angular)

  1. User Authentication: Create a user authentication system with features like signup, login, logout, and password recovery.
  2. User Interface Enhancements: Improve the user interface with responsive design, animations, and material design components from Angular Material.
  3. Forms and Validation: Implement forms with reactive forms or template-driven forms and add client-side validation using Angular’s built-in validators.
  4. State Management: Integrate state management libraries like NgRx or Akita for managing complex application state and handling data flow efficiently.
  5. File Uploads: Add support for file uploads using Angular HttpClient to upload files to the backend server and display file previews or progress indicators.
  6. Internationalization (i18n): Implement internationalization support using Angular’s built-in i18n features to make your application multilingual.

Additional Features

  1. User Profiles: Allow users to create profiles with details like name, email, profile picture, and bio. Users can view and edit their profiles.
  2. Search Functionality: Implement search functionality to allow users to search for specific items or entities within the application.
  3. Notifications: Implement a notification system to notify users of important events or updates within the application.
  4. Social Sharing: Add social sharing buttons to allow users to share content from the application on social media platforms.
  5. 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.

Related Articles