NestJS: Server-Framework auf Steroide 🎉

NestJS ist das Turbo-Framework fĂŒr NodeJS-Devs! 🚀 Mit TypeScript, OOP & FP wird jede Codezeile zum VergnĂŒgen! 😎

NestJS: Server-Framework auf Steroide 🎉
Photo by Christina @ wocintechchat.com / Unsplash / Image

In einem vorherigen Artikel habe ich schon einmal beschrieben, wie man mit blankem JavaScript und NodeJS eine REST-API bauen kann. Wenn dich der Artikel interessiert, dann schau einmal hier rein 👇

Eigene API mit NodeJS und Express
Du willst dir eine eigene API fĂŒr deine Dienste bauen? Wir machen es zusammen 👇

Nun ist es aber so, dass es selten bei nur einer Kleinigkeit bleibt. In der Regel wird es spĂ€ter noch Anforderungen geben wie Authentifizierungen, Autorisierung, Ablage von Daten in einer Datenbank oder Ähnliches.

Um das zu vereinfachen und immer wiederkehrenden Code nicht selbst neu schreiben zu mĂŒssen, setzt man auf etablierte Frameworks. Im JavaScript/TypeScript-Umfeld hat sich fĂŒr mich NestJS sehr etabliert und fĂŒr genau den Fall ausgezeichnet.

Im Kern komme ich aus der Frontend-Entwicklung (um genau zu sein Angular) und wollte mich hier nicht auf eine andere Technologie einlassen und mich in meiner Komfortzone weiter bewegen. Und genau hier kommt NestJS ins Spiel.

Was ist NestJS đŸ€”

NestJS ist ein serverseitiges, aber durch NodeJS, plattformunabhĂ€ngiges Framework, welches dir fĂŒr viele Aufgabenstellungen bereits sehr viele Lösungen und Tools anbietet.

NestJS - A progressive Node.js framework
NestJS is a framework for building efficient, scalable Node.js web applications. It uses modern JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

Es ist sozusagen die Werkzeugkiste die du im Keller stehen hast und immer darauf zurĂŒckgreifst, wenn du etwas spezielles tun willst. Kleines Zitat hierzu aus der Doku:

Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with and fully supports TypeScript (yet still enables developers to code in pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

Under the hood, Nest makes use of robust HTTP Server frameworks like Express (the default) and optionally can be configured to use Fastify as well!

Nest provides a level of abstraction above these common Node.js frameworks (Express/Fastify), but also exposes their APIs directly to the developer. This gives developers the freedom to use the myriad of third-party modules which are available for the underlying platform.

NestJS unterstĂŒtzt dich auch aktiv dabei, eine robuste und skalierbare Anwendung zu bauen, in dem es dich an die Hand nimmt und dir eine bereits ausgereifte Architektur mit an die Hand gibt. Nice!

Grundlegendes zu NestJS đŸ§±

NestJS ist also nicht nur ein weiteres Framework, sondern eine wahre Bereicherung fĂŒr jeden Entwickler, der seine Anwendungen skalierbar, wartbar und erweiterbar gestalten will. Aber was genau macht NestJS so besonders? Fangen wir bei der Architektur an. NestJS basiert auf Modulen, Providern und Controllern, die in einer hierarchischen Struktur organisiert sind. Dies erlaubt eine klare Trennung der verschiedenen Anwendungsbereiche und fördert so eine modulare und wartbare Codebasis.

NestJS setzt auf TypeScript ☝

Ein weiterer Punkt, der NestJS hervorhebt, ist die Integration von TypeScript. TypeScript bringt Typsicherheit in die Welt von JavaScript und hilft, viele Fehler schon wĂ€hrend der Entwicklungsphase zu erkennen. FĂŒr jemanden wie mich, der aus der Angular-Welt kommt, ist das eine sehr willkommene Eigenschaft, da es das Entwickeln sicherer und vorausschauender macht.


Warum du auch TypeScript fĂŒr deine Projekte nutzen solltest und welche Vorteile es dir bringt, habe ich bereits in einem vorherigen Artikel erklĂ€rt 👇

Warum du nur noch TypeScript nutzen solltest ☝
JavaScript ist tot, lang lebe TypeScript! Wie TypeScript dich aktiv unterstĂŒtzt besseren und zuverlĂ€ssigeren Code zu schreiben, erklĂ€re ich dir in diesem Artikel 🎉

Nun zu den praktischen Aspekten: NestJS bietet eine Vielzahl von Modulen fĂŒr gĂ€ngige Aufgaben wie Authentifizierung, Datenbankintegration, Caching, Dateiverarbeitung und vieles mehr. Durch die Nutzung dieser Module kann man sich wiederkehrende Aufgaben sparen und sich auf das Wesentliche, die eigene GeschĂ€ftslogik, konzentrieren. NestJS macht es leicht, sauberen, gut strukturierten Code zu schreiben, der sich auch leicht testen lĂ€sst.

Aufbau von NestJS 🚧

In der Welt von NestJS sind Controller, Module und Services einige der Kernbestandteile, die es ermöglichen, eine strukturierte und effiziente API zu gestalten. Hier sind praktische Beispiele fĂŒr diese wichtigen Bausteine:

Controller

Controller in NestJS sind verantwortlich fĂŒr die Handhabung eingehender Requests und das ZurĂŒckgeben von Responses.

Diagram von NestJS

Sie werden durch die Dekorierung von Klassen mit @Controller() definiert. Hier ist ein einfaches Beispiel:

import { Controller, Get, Post, Body, Param } from '@nestjs/common';

@Controller('items')
export class ItemsController {
    @Get()
    findAll(): string {
        return 'This action returns all items';
    }

    @Get(':id')
    findOne(@Param('id') id: string): string {
        return `This action returns a single item with id ${id}`;
    }

    @Post()
    create(@Body() createItemDto: any): string {
        return 'This action adds a new item';
    }
}

In diesem Beispiel definiert der ItemsController Routen fĂŒr das Abrufen aller Elemente, eines einzelnen Elements anhand seiner ID und das Erstellen eines neuen Elements.

Services

Services sind in NestJS dafĂŒr verantwortlich, die GeschĂ€ftslogik zu kapseln. Sie sind typischerweise Anbieter, die mit @Injectable() dekoriert sind und in Controller oder andere Services injiziert werden können. Hier ist ein einfaches Beispiel fĂŒr einen Service:

import { Injectable } from '@nestjs/common';

@Injectable()
export class ItemsService {
    private readonly items = [];

    findAll(): string[] {
        return this.items;
    }

    findOne(id: string): string {
        return this.items.find(item => item.id === id);
    }

    create(item: any): void {
        this.items.push(item);
    }
}

Die Services werden ĂŒber die Provider im Modul bereitgestellt und sind so ĂŒber eine Dependency Injection verfĂŒgbar.

Provider

In NestJS sind Provider ein fundamentales Konzept, da sie fĂŒr die meiste GeschĂ€ftslogik in einer Anwendung verantwortlich sind. Provider können Services, Repositories, Factories, Helfer und mehr sein. Sie sind in der Regel Klassen, die mit @Injectable() dekoriert sind und in das Dependency Injection System von NestJS eingebunden werden.

Diagram von NestJS

Hier einige Beispiele und Anwendungen von Providern:

Service als Provider
Services sind die am hÀufigsten genutzten Provider in NestJS. Sie kapseln GeschÀftslogik, Datenzugriffe und andere Funktionen, die von verschiedenen Teilen der Anwendung wiederverwendet werden können.

Custom Provider
NestJS ermöglicht es auch, benutzerdefinierte Provider zu erstellen. Dies ist nĂŒtzlich, wenn man eine komplexe Logik oder Drittanbieterdienste einbinden möchte. Hier ist ein Beispiel fĂŒr einen benutzerdefinierten Provider:

import { Injectable, Inject } from '@nestjs/common';

const CATS_REPOSITORY = 'CATS_REPOSITORY';

@Injectable()
export class CatsService {
  constructor(@Inject(CATS_REPOSITORY) private catsRepository) {}

  findAll(): any[] {
    return this.catsRepository.findAll();
  }
}

const catsRepository = {
  findAll() {
    // Implementierung der Methode
  }
};

const customProviders = [
  {
    provide: CATS_REPOSITORY,
    useValue: catsRepository,
  },
];

Factory Provider
Factory Provider sind eine weitere Form von Providern, bei denen eine Factory-Funktion verwendet wird, um den Provider zu erstellen. Dies kann nĂŒtzlich sein, wenn die Erstellung des Providers komplizierter ist oder von bestimmten Bedingungen abhĂ€ngt.

import { Injectable, Inject, FactoryProvider } from '@nestjs/common';

@Injectable()
export class LoggerService {
  log(message: string) {
    console.log(message);
  }
}

const loggerFactory: FactoryProvider = {
  provide: 'LOGGER',
  useFactory: () => {
    const logger = new LoggerService();
    // Konfiguration des Loggers
    return logger;
  },
};

Hier wird ein LoggerService als Factory Provider erstellt, indem eine Factory-Funktion definiert wird, die den LoggerService instanziiert und zurĂŒckgibt.

Module

Module sind ein Weg, um Anwendungen und Komponenten in NestJS zu organisieren. Ein Modul kapselt Provider, Controller und andere Module.

Diagram von NestJS

Hier ist ein einfaches Beispiel:

import { Module } from '@nestjs/common';
import { ItemsController } from './items.controller';
import { ItemsService } from './items.service';

@Module({
  controllers: [ItemsController],
  providers: [ItemsService],
})
export class ItemsModule {}

Middleware

Middleware sind Funktionen, die vor den Routen-Handlern ausgefĂŒhrt werden. Sie können fĂŒr eine Vielzahl von Aufgaben verwendet werden, wie z.B. Logging, Authentifizierung, und so weiter.

Diagram von NestJS

Hier ist ein einfaches Middleware-Beispiel in NestJS:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`Request made to: ${req.baseUrl}`);
    next();
  }
}

Guards

Guards sind verwendet fĂŒr Authentifizierung und Autorisierung. Sie entscheiden, ob eine bestimmte Anfrage fortgesetzt werden soll.

Diagram von NestJS

Hier Beispiel, wie man einen einfachen Guard in NestJS implementieren könnte:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return user && user.role === 'admin';
  }
}

Exception Filters

Exception Filters ermöglichen eine anpassbare Fehlerbehandlung. Sie können definieren, wie unerwartete Fehler und Ausnahmen behandelt werden sollen.

Diagram von NestJS

Hier ist ein einfaches Beispiel fĂŒr einen Exception Filter:

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: ctx.getRequest().url,
    });
  }
}

Interceptors

Interceptors bieten eine Möglichkeit, zusĂ€tzliche Logik vor oder nach der AusfĂŒhrung einer Methode einzufĂŒhren. Sie sind nĂŒtzlich fĂŒr Logging, Umformen von Responses und vieles mehr.

Diagram von NestJS

Hier ist ein Beispiel:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@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`)),
      );
  }
}

Pipes

Pipes sind verwendet fĂŒr Daten Transformation und Validierung. Diese können eingesetzt werden, um Eingabedaten zu ĂŒberprĂŒfen oder zu bearbeiten, bevor sie einen Handler erreichen. Hier ist ein Beispiel fĂŒr einen einfachen Pipe:

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    if (!value) {
      throw new BadRequestException('No data provided');
    }
    // Perform validation logic
    return value;
  }
}

Die CLI đŸȘ„

Die NestJS CLI (Command Line Interface) ist ein wesentliches Werkzeug, das den Entwicklungsprozess beschleunigt, indem es das Einrichten, Entwickeln und Verwalten von NestJS-Anwendungen vereinfacht. Mit der CLI können neue Projekte, Module, Controller, Services und mehr generiert werden, was zu einer konsistenten und effizienten Entwicklung beitrÀgt. Die CLI wird wie folgt installiert:

npm install -g @nestjs/cli
# oder
yarn global add @nestjs/cli

Erstellen eines neuen Projekts

Mit der CLI kann man ein neues NestJS-Projekt mit einer vorkonfigurierten Projektstruktur erstellen. Nach der Installation fĂŒhrt man einfach den folgenden Befehl aus:

nest new project-name

Dieser Befehl erstellt ein neues Verzeichnis mit dem Namen project-name und richtet ein neues NestJS-Projekt mit allen erforderlichen AbhÀngigkeiten ein.

Generieren von Komponenten 📃

Die CLI erleichtert das HinzufĂŒgen von Komponenten zu Ihrem Projekt. Dadurch lassen sich schnell Dinge erzeugen. Dazu zĂ€hlen:

Erstellen eines Controllers:

nest generate controller cats

Dieser Befehl erstellt einen neuen Controller mit dem Namen cats.controller.ts und das zugehörige Testfile cats.controller.spec.ts im Verzeichnis src/cats.

Erstellen eines Services:

nest generate service cats

Dieser Befehl erstellt einen neuen Service namens cats.service.ts und das zugehörige Testfile cats.service.spec.ts.

Erstellen eines Moduls:

nest generate module cats

Dieser Befehl erstellt ein neues Modul namens cats.module.ts.

Starten der Anwendung đŸŽïž

Um Ihre NestJS-Anwendung zu starten, navigier in das Projektverzeichnis und fĂŒhren den folgenden Befehl aus:

npm run start

Weitere nĂŒtzliche CLI-Befehle đŸ€–

  • Linting: Um die Code-QualitĂ€t zu prĂŒfen, kann man npm run lint verwenden.
  • Build: Um ein Projekt fĂŒr die Produktion zu kompilieren, verwenden man npm run build.

Die NestJS CLI bietet eine Vielzahl weiterer Funktionen und Optionen, die Ihren Workflow optimieren und die Entwicklung von NestJS-Anwendungen erleichtern. Durch die Verwendung der CLI kann man sicherstellen, dass die Projektstruktur konsistent und gut organisiert ist und den Best Practices folgt. Weitere Informationen und eine detaillierte Dokumentation findet man in der offiziellen NestJS-Dokumentation.

Documentation | NestJS - A progressive Node.js framework
Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

Coole weitere Features ✹

GraphQL leicht gemacht

Mit NestJS kannst du auf einfache Weise GraphQL-APIs implementieren. Das Framework bietet eine nahtlose Integration mit Apollo und ermöglicht dir, mit wenigen Zeilen Code leistungsstarke und flexible GraphQL-Schnittstellen zu erstellen. Das ist besonders cool, wenn du auf moderne Datenabfrage und -manipulation setzt.

Hybride Anwendungsarten

NestJS ist nicht nur auf REST oder GraphQL beschrÀnkt. Du kannst hybride Anwendungen erstellen, die beispielsweise gleichzeitig REST- und GraphQL-Endpunkte bieten. Das erhöht die FlexibilitÀt und macht NestJS zu einem vielseitigen Werkzeug in deinem Entwicklerarsenal.

WebSockets integriert

EchtzeitfunktionalitĂ€ten sind in modernen Anwendungen oft unverzichtbar. NestJS macht die Arbeit mit WebSockets einfach, indem es die Integration von Socket.io oder anderen Bibliotheken vereinfacht. So kannst du ohne großen Aufwand Echtzeitkommunikation in deine Anwendungen einbauen.

CQRS-Modul

FĂŒr komplexere AnwendungsfĂ€lle, in denen eine Trennung zwischen Befehls- (Command) und Abfrageverantwortung (Query) erforderlich ist, bietet NestJS ein CQRS-Modul an. Dies unterstĂŒtzt dich beim Aufbau von skalierbaren und wartbaren Softwarearchitekturen.

Dekorative Programmierung

Mit dem dekorativen Ansatz von NestJS kannst du deine Intentionen klar und prĂ€zise ausdrĂŒcken. Ob du Routen definierst, AbhĂ€ngigkeiten injizierst oder Guards und Interceptors anwendest – alles geschieht mit einer sauberen und lesbaren Syntax.

Erstklassige Mikroservice-UnterstĂŒtzung

NestJS ist nicht nur fĂŒr die Erstellung monolithischer Anwendungen geeignet. Es bietet robuste UnterstĂŒtzung fĂŒr Mikroservices mit einer Vielzahl von Transportmechanismen, darunter MQTT, Redis, RabbitMQ und mehr. Dies ermöglicht dir, skalierbare und effiziente Mikroservice-Architekturen zu entwerfen.

Dynamische Module

Ein weiteres cooles Feature sind dynamische Module. Mit ihnen kannst du Module konfigurieren und sogar Module basierend auf verschiedenen Umgebungen oder Bedingungen erstellen. Das bietet dir eine enorme FlexibilitÀt und Kraft, insbesondere in komplexen und dynamischen Anwendungsszenarien.

OpenAPI (Swagger) Integration

NestJS bietet eine integrierte UnterstĂŒtzung fĂŒr OpenAPI, auch bekannt als Swagger. Mit wenigen Dekoratoren in deinen Controllern und Modellen kannst du eine vollstĂ€ndige OpenAPI-Spezifikation deiner API automatisch generieren lassen. Das bedeutet, du erhĂ€ltst interaktive Dokumentation, Client-SDK-Generation und vieles mehr, praktisch ohne zusĂ€tzlichen Aufwand. Dies ist besonders nĂŒtzlich, um API-Endpunkte fĂŒr Frontend-Entwickler oder externe Partner schnell und klar zu dokumentieren. Es verbessert die ZugĂ€nglichkeit und VerstĂ€ndlichkeit deiner API erheblich.

Prisma Integration

Prisma ist ein nextgen ORM fĂŒr Node.js und TypeScript, das sich durch seine einfache Handhabung und Leistung auszeichnet. NestJS kann nahtlos mit Prisma integriert werden, um eine effiziente und einfache Datenbankabstraktion und -manipulation zu ermöglichen. Mit Prisma kannst du deine Datenbankschemata als Modelle definieren und leistungsfĂ€hige Abfragen schreiben, die typsicher sind, was die Entwicklung und Wartung von datenintensiven Anwendungen vereinfacht.

Hot Reload (mit Webpack HMR)

Entwickler lieben schnelles Feedback. NestJS unterstĂŒtzt Hot Reload durch Webpack's Hot Module Replacement (HMR). Damit kannst du Änderungen an deinem Code vornehmen, die in Echtzeit in die laufende Anwendung eingespielt werden, ohne dass ein Neustart erforderlich ist. Dies spart wertvolle Entwicklungszeit und erhöht die ProduktivitĂ€t, da du sofortiges Feedback ĂŒber die Auswirkungen deiner Änderungen erhĂ€ltst. Um dies zu nutzen, musst du lediglich deinen NestJS-Server mit der entsprechenden HMR-Konfiguration starten, und schon kannst du die Änderungen live sehen, sobald du deinen Code speicherst.

CRUD Generator

Mit dem CRUD Generator von NestJS kannst du mit wenig Aufwand schnell effektive und standardisierte CRUD-Operationen fĂŒr deine Anwendung erstellen. Dieses Tool automatisiert die Erstellung von typischen CRUD-Modulen, Controllern und Services, sodass du dich auf die spezifischen Anforderungen und die GeschĂ€ftslogik deiner Anwendung konzentrieren kannst, statt immer wiederkehrenden Code manuell zu schreiben.

Fazit 💡

Zusammenfassend lĂ€sst sich sagen, dass NestJS eine hervorragende Wahl fĂŒr moderne, serverseitige Anwendungen ist. Die Kombination aus modernem JavaScript, Typsicherheit durch TypeScript, einer soliden Architektur und einer großen Auswahl an vorbereiteten Modulen macht es zu einem unschĂ€tzbaren Werkzeug fĂŒr Entwickler.

NestJS nimmt uns viel Arbeit ab und lĂ€sst uns gleichzeitig genug Raum, um unsere Anwendungen nach unseren Vorstellungen zu gestalten. Ob fĂŒr kleine Projekte oder große Unternehmensanwendungen – NestJS ist definitiv einen Blick wert.

Zudem ĂŒberzeugt es durch die Anlehnung an die Konzepte von Angular und ist dadurch prĂ€destiniert fĂŒr Angular-Entwickler.

Also, warum nicht gleich in das nÀchste Projekt mit NestJS springen und selbst erleben, wie es die Entwicklung vereinfacht und beschleunigt?