Jak zbudować Nest.js CRUD REST API przy użyciu TypeORM i PostgreSQL

Podobnie jak inne frameworki oparte na Node.js, Nest.js oferuje rozbudowany zestaw narzędzi do tworzenia solidnych i skalowalnych usług backendowych. Kluczowe jest zrozumienie, jak efektywnie zaimplementować operacje tworzenia, odczytu, aktualizacji i usuwania (CRUD), które są podstawowymi funkcjami w procesie budowy interfejsów API.

W tym artykule omówimy, jak skonstruować API REST oparte na CRUD w Nest.js, wykorzystując TypeORM i bazę danych PostgreSQL.

Pierwsze kroki z Nest.js

Aby rozpocząć, zainstaluj narzędzie wiersza poleceń Nest.js za pomocą poniższej komendy:

 npm i -g @nestjs/cli 

Następnie, utwórz nowy projekt, używając:

 nest new crud-app 

Narzędzie CLI poprosi Cię o wybranie menedżera pakietów. Wybierz preferowaną opcję. W naszym przypadku użyjemy npm, domyślnego menedżera pakietów dla Node.js.

CLI wygeneruje podstawową strukturę projektu Nest.js, zawierającą wszystkie wymagane pliki konfiguracyjne i początkowe zależności, niezbędne do uruchomienia aplikacji.

Na koniec, przejdź do katalogu projektu i uruchom serwer deweloperski:

 cd crud-app
npm run start

Kod źródłowy tego projektu można znaleźć w repozytorium GitHub.

Utwórz bazę danych PostgreSQL

W tym poradniku wykorzystana zostanie instancja PostgreSQL w chmurze. Możesz również skonfigurować lokalną bazę danych PostgreSQL, instalując ją na systemie Windows, macOS lub Linux.

Aby skonfigurować instancję PostgreSQL w chmurze:

  • Przejdź do serwisu ElephantSQL, zarejestruj się i zaloguj na stronie swojego konta.
  • Kliknij przycisk „Utwórz nową instancję” w lewym górnym rogu strony, aby utworzyć nową instancję dla swojej aplikacji.
  • Wprowadź nazwę instancji, wybierz darmowy plan i region, aby zakończyć proces konfiguracji.
  • Po utworzeniu instancji, przejdź do strony ustawień i skopiuj wygenerowany adres URL bazy danych.
  • Konfiguracja połączenia z bazą danych

    W katalogu głównym projektu utwórz plik `.env` i wklej adres URL połączenia z bazą danych w następujący sposób:

     DATABASE_URL="<twój adres URL połączenia>" 

    Następnie zainstaluj wymagane pakiety:

     npm install pg typeorm @nestjs/typeorm @nestjs/config 

    Stwórz moduł bazy danych za pomocą narzędzia CLI:

     nest g module database 

    Otwórz plik `database/database.module.ts` i dodaj poniższy kod, który skonfiguruje połączenie z bazą danych:

     import { Module } from '@nestjs/common';
    import { ConfigModule, ConfigService } from '@nestjs/config';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { User } from '../users/models/user.entity';

    @Module({
      imports: [
        TypeOrmModule.forRootAsync({
          imports: [ConfigModule],
          inject: [ConfigService],

          useFactory: async (configService: ConfigService) => ({
            type: 'postgres',
            url: configService.get('DATABASE_URL'),
            entities: [User],
            synchronize: true
          }),
        }),
      ],
    })

    export class DatabaseModule {}

    Moduł bazy danych zarządza połączeniem, konfigurując moduł TypeORM z niezbędnymi danymi, w tym adresem URL bazy danych.

    Dodatkowo, definiuje on encję `User` w ramach konfiguracji, co opisuje strukturę danych i atrybuty przechowywane w tabeli bazy danych PostgreSQL.

    Na tym etapie, Twój kod może generować błędy, ponieważ encja użytkownika nie została jeszcze utworzona. Zrobimy to w kolejnych krokach.

    Aktualizacja pliku app.module.ts

    Na koniec, zaktualizuj główny moduł aplikacji, aby włączyć konfigurację modułu bazy danych:

     import { Module } from '@nestjs/common';
    import { ConfigModule } from '@nestjs/config';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { DatabaseModule } from './database/database.module';

    @Module({
      imports: [
        ConfigModule.forRoot({
          envFilePath: '.env',
        }),
        DatabaseModule,
      ],

      controllers: [AppController],
      providers: [AppService],
    })

    export class AppModule {}

    Zdefiniowanie modułu użytkowników

    Moduł użytkowników służy jako centralny element, odpowiadający za zarządzanie logiką implementacji funkcjonalności API CRUD.

    Uruchom poniższe polecenie w terminalu, aby utworzyć moduł użytkowników API:

     nest g module users 

    Narzędzie CLI automatycznie zaktualizuje plik `app.module.ts`, aby odzwierciedlić wprowadzone zmiany, a także utworzy nowy moduł `users`. Zapewnia to prawidłową integrację nowego modułu z konfiguracją modułu aplikacji.

    Utworzenie encji użytkownika

    TypeORM jest biblioteką mapowania obiektowo-relacyjnego (ORM), która upraszcza interakcję z bazą danych w aplikacjach TypeScript, mapując obiekty JavaScript na tabele baz danych.

    Tworząc encję użytkownika z TypeORM, definiujesz strukturę i atrybuty danych użytkownika w bazie danych PostgreSQL.

    W katalogu `users` utwórz nowy plik `models/user.entity.ts` i dodaj następujący kod:

     import { Entity, PrimaryGeneratedColumn, Column, } from "typeorm";

    @Entity()
    export class User {
        @PrimaryGeneratedColumn()
        id: number;

        @Column()
        name: string;

        @Column()
        email: string;
    }

    Encja `User` opisuje strukturę danych użytkownika przechowywanych w bazie danych. Zawiera ona identyfikator jako kolumnę klucza podstawowego oraz kolumny `name` i `email` z odpowiadającymi im typami danych.

    Utworzenie usługi API CRUD

    Teraz, utwórz usługę API, która będzie zarządzać logiką operacji CRUD, używając następującego polecenia:

     nest g service users 

    Otwórz plik `user-auth.service.ts` i dodaj ten kod:

     import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import {User} from './models/user.entity';

    @Injectable()
    export class UsersService {
      constructor(
        @InjectRepository(User)
        private userRepository: Repository<User>,
      ) {}

      async findAll(): Promise<User[]> {
        return this.userRepository.find();
      }

      async findOne(id: number): Promise<User> {
        return this.userRepository.findOne({ where: { id } });
      }

      async create(user: Partial<User>): Promise<User> {
        const newuser = this.userRepository.create(user);
        return this.userRepository.save(newuser);
      }

      async update(id: number, user: Partial<User>): Promise<User> {
        await this.userRepository.update(id, user);
        return this.userRepository.findOne({ where: { id } });
      }

      async delete(id: number): Promise<void> {
        await this.userRepository.delete(id);
      }
    }

    Klasa `UsersService` definiuje różne metody API dedykowane obsłudze operacji CRUD. Metody te obejmują pobieranie wszystkich użytkowników, wyszukiwanie użytkownika po identyfikatorze, tworzenie nowego użytkownika, aktualizację istniejącego użytkownika oraz usuwanie użytkownika z bazy danych.

    Zdefiniowanie kontrolera dla API

    Utwórz kontroler, który będzie zarządzał punktami końcowymi API dla operacji związanych z użytkownikami:

     nest g controller users 

    Dodaj poniższy kod do pliku `users.controller.ts`:

     import { Controller, Get, Post, Body, Put, Param, Delete, NotFoundException, HttpCode } from '@nestjs/common';
    import { UsersService } from './users.service';
    import { User } from './models/user.entity';

    @Controller('api/users')
    export class UsersController {
      constructor(private readonly usersService: UsersService) {}

      @Get()
      async findAll(): Promise<User[]> {
        return this.usersService.findAll();
      }

      @Post()
      @HttpCode(201)
      async create(@Body() user: User): Promise<User> {
        const createdUser = await this.usersService.create(user);
        return createdUser;
      }

      @Put(':id')
      async update (@Param('id') id: number, @Body() user: User): Promise<any> {
        await this.usersService.update(id, user);
        return { message: 'User updated successfully' };
      }

      @Delete(':id')
      async delete(@Param('id') id: number): Promise<any> {
        const user = await this.usersService.findOne(id);

        if (!user) {
          throw new NotFoundException('User does not exist!');
        }

        await this.usersService.delete(id);
        return { message: 'User deleted successfully' };
      }
    }

    Kontroler zarządza punktami końcowymi API dla operacji użytkownika. Obsługuje żądania GET do pobierania wszystkich użytkowników, żądania POST do tworzenia nowych użytkowników, PUT do aktualizacji istniejących i DELETE do usuwania użytkowników.

    Wykorzystując `UsersService` i interakcję z encją `User`, kontroler ten zapewnia kompletne API do zarządzania operacjami użytkownika na danych przechowywanych w bazie danych.

    Aktualizacja pliku users.module.ts

    Na koniec, zaktualizuj plik `users.module.ts`, jak pokazano poniżej, aby upewnić się, że zawiera encję `User` i moduł TypeORM, który ustanawia połączenie z bazą danych.

     import { Module } from '@nestjs/common';
    import { UsersController } from './users.controller';
    import { UsersService } from './users.service';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { User } from './models/user.entity';

    @Module({
      imports: [TypeOrmModule.forFeature([User])],
      controllers: [UsersController],
      providers: [UsersService]
    })

    export class UsersModule {}

    Uruchom serwer deweloperski, aby przetestować operacje CRUD za pomocą Postmana:

     npm run start 

    Serwer powinien być dostępny na porcie 3000, a Ty możesz wysyłać zapytania API na adres http://localhost:3000/api/users.

    Tworzenie aplikacji backendowych z Nest.js

    Niezależnie od tego, czy tworzysz proste API REST, czy złożoną aplikację internetową, Nest.js oferuje szeroki zakres funkcji i możliwości do budowania niezawodnego backendu.

    Nest.js oferuje bardziej ustrukturyzowane podejście do tworzenia projektów w porównaniu do Express.js. Gwarantuje to, że możesz efektywnie tworzyć, skalować i utrzymywać skomplikowane aplikacje dzięki zorganizowanemu i modułowemu podejściu projektowemu.


    newsblog.pl