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!
CRUD to akronim od czterech podstawowych operacji na danych:
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() |
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}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)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.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}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.
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 |
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}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/1Brawo, budowniczy! Teraz znasz fundamenty REST API - drogi, ktore lacza wszystkie prowincje naszego imperium NestJS!