Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Pierwsze REST API - operacje CRUD

Ave, budowniczy drog! Konsul Caesar.js powierza Ci teraz jedno z najwazniejszych zadan w imperium - budowe systemu drog REST API. Tak jak rzymskie drogi laczyly wszystkie prowincje imperium, tak REST API laczy nasza aplikacje serwerowa ze swiatem zewnetrznym. Czas nauczyc sie operacji CRUD - fundamentu kazdego REST API!

Czym jest CRUD?

CRUD to akronim od czterech podstawowych operacji na danych:

  • Create (Tworzenie) - rekrutacja nowych legionistow
  • Read (Odczyt) - przegladanie rejestrow imperium
  • Update (Aktualizacja) - awansowanie legionistow
  • Delete (Usuwanie) - wykreslanie z rejestrow

Kazda operacja CRUD odpowiada konkretnej metodzie HTTP i dekoratorowi w NestJS:

| Operacja | Metoda HTTP | Dekorator NestJS | |----------|-------------|------------------| | Create | POST | @Post() | | Read | GET | @Get() | | Update | PUT / PATCH | @Put() / @Patch()| | Delete | DELETE | @Delete() |

Budujemy kontroler CRUD krok po kroku

Zbudujmy kompletny kontroler do zarzadzania legionistami naszego imperium:

1import {
2  Controller, Get, Post, Put, Delete, Patch,
3  Param, Body, Query, HttpCode, HttpStatus
4} from '@nestjs/common';
5
6// Interfejs legionisty
7interface Legionary {
8  id: number;
9  name: string;
10  rank: string;
11  province: string;
12}
13
14@Controller('legionaries')
15export class LegionariesController {
16  // Tymczasowa tablica jako "baza danych"
17  private legionaries: Legionary[] = [
18    { id: 1, name: 'Marcus Aurelius', rank: 'Centurio', province: 'Roma' },
19    { id: 2, name: 'Gaius Julius', rank: 'Miles', province: 'Gallia' },
20  ];
21}

READ - odczyt danych (@Get)

Dekorator

@Get()
obsluguje zadania HTTP GET. Uzywamy go do pobierania danych:

1@Controller('legionaries')
2export class LegionariesController {
3
4  // GET /legionaries - pobierz wszystkich legionistow
5  @Get()
6  findAll(): Legionary[] {
7    return this.legionaries;
8  }
9
10  // GET /legionaries/1 - pobierz jednego po ID
11  @Get(':id')
12  findOne(@Param('id') id: string): Legionary {
13    const legionary = this.legionaries.find(l => l.id === Number(id));
14    if (!legionary) {
15      throw new Error('Legionista nie znaleziony!');
16    }
17    return legionary;
18  }
19
20  // GET /legionaries?province=Roma - filtrowanie po prowincji
21  @Get('search')
22  findByProvince(@Query('province') province: string): Legionary[] {
23    return this.legionaries.filter(l => l.province === province);
24  }
25}

Zauważ trzy kluczowe dekoratory parametrow:

  • @Param('id')
    - wyciaga parametr z URL (np.
    /legionaries/1
    )
  • @Query('province')
    - wyciaga parametr zapytania (np.
    ?province=Roma
    )
  • @Body()
    - wyciaga dane z ciala zadania (dla POST/PUT)

CREATE - tworzenie danych (@Post)

Dekorator

@Post()
obsluguje zadania HTTP POST. Sluzy do tworzenia nowych zasobow:

1// POST /legionaries - rekrutacja nowego legionisty
2@Post()
3@HttpCode(HttpStatus.CREATED) // Zwraca status 201
4create(@Body() newLegionary: { name: string; rank: string; province: string }): Legionary {
5  const legionary: Legionary = {
6    id: this.legionaries.length + 1,
7    ...newLegionary,
8  };
9  this.legionaries.push(legionary);
10  return legionary;
11}

Dekorator

@Body()
wyciaga dane z ciala zadania JSON. Dekorator
@HttpCode()
pozwala ustawic kod odpowiedzi HTTP - dla tworzenia zasobow standardem jest 201 Created.

UPDATE - aktualizacja danych (@Put i @Patch)

Mamy dwa dekoratory do aktualizacji:

  • @Put()
    - zastepuje caly zasob (pelna aktualizacja)
  • @Patch()
    - aktualizuje tylko wybrane pola (czesciowa aktualizacja)
1// PUT /legionaries/1 - pelna aktualizacja legionisty
2@Put(':id')
3update(
4  @Param('id') id: string,
5  @Body() updateData: { name: string; rank: string; province: string },
6): Legionary {
7  const index = this.legionaries.findIndex(l => l.id === Number(id));
8  if (index === -1) {
9    throw new Error('Legionista nie znaleziony!');
10  }
11  this.legionaries[index] = { id: Number(id), ...updateData };
12  return this.legionaries[index];
13}
14
15// PATCH /legionaries/1 - czesciowa aktualizacja (np. tylko ranga)
16@Patch(':id')
17partialUpdate(
18  @Param('id') id: string,
19  @Body() updateData: Partial<Legionary>,
20): Legionary {
21  const index = this.legionaries.findIndex(l => l.id === Number(id));
22  if (index === -1) {
23    throw new Error('Legionista nie znaleziony!');
24  }
25  this.legionaries[index] = { ...this.legionaries[index], ...updateData };
26  return this.legionaries[index];
27}

DELETE - usuwanie danych (@Delete)

Dekorator

@Delete()
obsluguje zadania HTTP DELETE:

1// DELETE /legionaries/1 - wykreslenie legionisty
2@Delete(':id')
3@HttpCode(HttpStatus.NO_CONTENT) // Zwraca status 204
4remove(@Param('id') id: string): void {
5  const index = this.legionaries.findIndex(l => l.id === Number(id));
6  if (index === -1) {
7    throw new Error('Legionista nie znaleziony!');
8  }
9  this.legionaries.splice(index, 1);
10}

Dla operacji usuwania standardowy kod odpowiedzi to 204 No Content - oznacza sukces bez zwracania danych.

Kody statusow HTTP

Znajomosc kodow HTTP to obowiazek kazdego budowniczego API:

| Kod | Nazwa | Kiedy uzywac | |-----|-------|-------------| | 200 | OK | Sukces (domyslny dla GET, PUT, PATCH) | | 201 | Created | Nowy zasob zostal stworzony (POST) | | 204 | No Content | Sukces bez tresci odpowiedzi (DELETE) | | 400 | Bad Request | Nieprawidlowe dane wejsciowe | | 404 | Not Found | Zasob nie istnieje | | 500 | Internal Server Error | Blad serwera |

Kontroler + Serwis - prawidlowy wzorzec

W prawdziwym imperium kontroler nie wykonuje logiki sam - deleguje ja do serwisu:

1// legionaries.service.ts
2@Injectable()
3export class LegionariesService {
4  private legionaries: Legionary[] = [];
5
6  findAll(): Legionary[] {
7    return this.legionaries;
8  }
9
10  findOne(id: number): Legionary {
11    return this.legionaries.find(l => l.id === id);
12  }
13
14  create(data: { name: string; rank: string; province: string }): Legionary {
15    const legionary = { id: this.legionaries.length + 1, ...data };
16    this.legionaries.push(legionary);
17    return legionary;
18  }
19
20  update(id: number, data: Partial<Legionary>): Legionary {
21    const index = this.legionaries.findIndex(l => l.id === id);
22    this.legionaries[index] = { ...this.legionaries[index], ...data };
23    return this.legionaries[index];
24  }
25
26  remove(id: number): void {
27    this.legionaries = this.legionaries.filter(l => l.id !== id);
28  }
29}
30
31// legionaries.controller.ts
32@Controller('legionaries')
33export class LegionariesController {
34  constructor(private readonly legionariesService: LegionariesService) {}
35
36  @Get()
37  findAll() {
38    return this.legionariesService.findAll();
39  }
40
41  @Post()
42  create(@Body() data: { name: string; rank: string; province: string }) {
43    return this.legionariesService.create(data);
44  }
45}

Testowanie z curl

Mozesz przetestowac swoje API uzywajac narzedzia curl w terminalu:

1# GET - pobierz wszystkich
2curl http://localhost:3000/legionaries
3
4# POST - stworz nowego
5curl -X POST http://localhost:3000/legionaries   -H "Content-Type: application/json"   -d '{"name": "Titus Flavius", "rank": "Miles", "province": "Judea"}'
6
7# PUT - pelna aktualizacja
8curl -X PUT http://localhost:3000/legionaries/1   -H "Content-Type: application/json"   -d '{"name": "Marcus Aurelius", "rank": "Legatus", "province": "Roma"}'
9
10# DELETE - usuniecie
11curl -X DELETE http://localhost:3000/legionaries/1

Brawo, budowniczy! Teraz znasz fundamenty REST API - drogi, ktore lacza wszystkie prowincje naszego imperium NestJS!

Przejdź do CodeWorlds