I recently held a talk at the devFest Vienna about a project we did where multiple Angular developers learned nestJS.


This article dives into the reason why nestJS is just from the way the framework works and is set up as the perfect choice for learning how to write a backend when you are an Angular dev.
For that, we will look at the following points:
- General similarities
- Modules
- Interceptors
- Guards
- Pipes


 Angular vs NestJS: The similarities

Before we get into the specifics, let's start with the obvious. Of course any Node.JS framework is a great choice for Angular developers as they know the eco system already. This includes the choice of package manager (npm or yarn), the programming language (java-script or TypeScript) and the community which evolved around it.

 

The structure: Modules

Angular, at its core, has undergone significant changes over the years. One notable evolution is the shift away from a monolithic architecture to a more modular and component-based structure.

At the heart of early Angular development was the concept of modules, a fundamental building block that helped organize and structure applications.

However, as Angular matured, the landscape changed. The traditional modules became more or less deprecated in favor of standalone modules. This shift was driven by a desire for greater flexibility, maintainability, and scalability in modern web development.

The move towards standalone modules allowed developers to decouple different parts of their applications, making it easier to manage and scale individual components.

Despite the deprecation of modules in the Angular framework, their legacy lives on and can still be a valuable reference point. This is particularly true in the context of NestJS, which is built on the same underlying principles as classic Angular, including the module system.

For developers familiar with modules, learning NestJS can be a smoother process. The concepts and approach to modularization remain consistent, providing a sense of continuity and easing the learning curve.

To not tell but show here is an example of a classic Angular module:

@NgModule({
 declarations: [PageComponent],
 exports: [PageComponent],
 imports: [CommonModule, RouterModule, MatProgressBarModule],
 providers: [PageService],
})
export class PageModule {
}

And for comparison a nestJS module:

@Module({
 providers: [PageService],
 controllers: [PageController],
 imports: [PrismaModule],
 exports: [PageService],
})
export class PageModule {}

The difference can be seen in the `controllers` and `declarations` section of the two.
Of course NestJS does not need to declare components the way Angular does. Instead one exposes Endpoints of an API in a controller.
Apart from that the syntax is exactly the same.

In summary, while Angular has evolved beyond its original reliance on modules, their influence persists. Developers can leverage their understanding of these classic modules as a foundation for building robust and scalable applications with NestJS.


Interceptors in Angular and NestJS

Continuing the shared concepts between Angular and NestJS, another significant parallel lies in the use of interceptors. In both frameworks, interceptors play a crucial role, although their application extends beyond HTTP requests.

In Angular, interceptors are primarily employed to manipulate or react to HTTP requests, whether incoming or outgoing. This functionality enhances the ability to modify request/response data, add custom headers, or handle errors at a centralized level.

NestJS, being influenced by Angular's architecture, adopts a similar approach with interceptors. However, in NestJS, interceptors go beyond just HTTP request manipulation. They are versatile tools used for modifying the input or output of any given function within an application.

While they are commonly associated with HTTP request interception, they can seamlessly integrate into various parts of an application, offering flexibility in handling diverse scenarios.

What's notable is that the style of writing interceptors in both frameworks remains remarkably similar. This consistency simplifies the learning curve for developers transitioning between Angular and NestJS.

The familiar syntax and structure make it easier for developers to apply their knowledge seamlessly and leverage interceptors in a cohesive manner.

So here follows an example of an interceptor written in Angular:

export function loggingInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {

return next(req).pipe(tap(event => {

if (event.type === HttpEventType.Response) {

console.log(req.url, 'returned a response with status', event.status);

}

}));

}

And one written in NestJS:

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
 intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
 console.log('Before...');

const now = Date.now();
 return next
 .handle()
 .pipe(
 tap(() => console.log(`After... ${Date.now() - now}ms`)),
 );
 }
}


In summary, interceptors serve as another point of convergence between Angular and NestJS. While Angular primarily focuses on HTTP requests, NestJS broadens the scope by allowing interceptors to be applied across different functions within an application.

The shared writing style enhances the transferability of skills, enabling developers to harness the power of interceptors with ease in both frameworks.


Similar design principle: Guards

In NestJS, guards serve as a distinctive type of interceptors, designed to handle specific functionalities within the application. They act as gatekeepers, controlling access to routes or executing logic based on certain conditions before allowing a request to proceed.

This aligns with the overarching concept of interceptors in NestJS, where they can be tailored for diverse scenarios beyond HTTP request manipulation.

Likewise, in Angular, guards play a comparable role. They are utilized to control and manage access to routes, enabling developers to implement logic that determines whether a user is authorized to navigate to a particular route or not. This resonates with the essence of guards in NestJS, establishing a cohesive approach across the two frameworks.

The similarity in the utilization of guards between Angular and NestJS further emphasizes the consistency in their design principles. Developers familiar with guards in Angular will find a seamless transition to NestJS, as the concepts and usage align closely.

 

Pipes in Angular and NestJS 

Concluding the exploration of shared concepts, pipes emerge as a common pattern embraced by both Angular and NestJS, serving the dual purpose of data transformation and validation.

In both frameworks, pipes play a vital role in manipulating and validating data, contributing to a streamlined and efficient development process. Developers leverage pipes to transform the format of data or ensure its conformity to specific criteria, enhancing the reliability and consistency of applications.

A small greeting pipe can be seen here for Angular:

@Pipe({

name: 'greet',

standalone: true,

})

export class GreetPipe implements PipeTransform {

transform(value: string, param1: boolean, param2: boolean): string {

return `Hello ${value}`;

}
}

While a similar one can be built in NestJS:

@Injectable()
export class GreetingPipe implements PipeTransform {
 transform(value: any, metadata: ArgumentMetadata) {
 return `Hello ${value}`;
 }
}


Angular and NestJS implement the concept of pipes in a similar fashion, showcasing yet another shared element in their design principles. Whether it's transforming the display of data in the frontend or validating input parameters in the backend, the utilization of pipes follows a consistent pattern across both frameworks.

 

Cohesive development experience with Angular and NestJS

In summary, both Angular and NestJS implement the given concepts, fostering a cohesive development experience. The familiarity and transferability of skills between the two frameworks make transitioning intuitive for developers, underscoring their interconnected nature.


This does however not solve the need of a frontend developer learning the concepts of a backend web-framework, as it involves much more than the architecture of the framework but the architecture of different APIs e.g. REST or GraphQL and how to work with databases (SQL or noSQL) which are significant additional tasks on the journey to become a full-stack developer.