Kurs NestJS · Moduł 1: Podstawy NestJS
Witaj w Imperium! Wprowadzenie do NestJS
W tej lekcji8
Salve, młody legioniście! Witaj w systemie rzymskich technologii! Jestem Konsul Caesar.js, a ty zostaniesz moim młodym legionistą w kampanii przez rozległe prowincje NestJS - najbardziej potężnego frameworka Node.js w Imperium internetu!
Podobnie jak Imperium Romanum potrzebuje solidnej konstrukcji, doświadczonego legionu i dobrze zorganizowanych kohort, tak aplikacja NestJS wymaga przemyślanej architektury, modularności i profesjonalnego podejścia do budowania skalowalnych aplikacji serwerowych.
Czym jest NestJS?
NestJS to progresywny framework Node.js do budowania wydajnych i skalowalnych aplikacji serwerowych. Został zbudowany w TypeScript i wykorzystuje najlepsze wzorce z ekosystemu JavaScript, łącząc je z mocnymi dekoratorami i systemem dependency injection znanym z Angulara.
Wyobraź sobie, że NestJS to nasze potężne Imperium Romanum:
1// Nasze rzymskie kohorty (moduły)
2import { Module } from '@nestjs/common';
3import { LegionModule } from './legion/legion.module';
4import { TributesModule } from './tributes/tributes.module';
5import { ForumModule } from './forum/forum.module';
6
7@Module({
8 imports: [LegionModule, TributesModule, ForumModule],
9})
10export class ImperiumModule {}Fundamenty NestJS
1. Dekoratory - nasze rzymskie oznaki rangowe
W NestJS dekoratory to jak rzymskie oznaki rangowe - oznaczają rangę i funkcję każdego elementu legionu:
1// Konsul kontroluje całą operację
2@Controller('consul')
3export class ConsulController {
4 // Obsługuje rozkazy dotyczące trybutów
5 @Get('tributes')
6 findTributes() {
7 return 'Trybuty ze wszystkich prowincji!';
8 }
9
10 // Przyjmuje nowych legionistów
11 @Post('legion')
12 recruitLegionary(@Body() newLegionary: CreateLegionaryDto) {
13 return 'Nowy legionista zwerbowany!';
14 }
15}2. Moduły - struktury naszego Imperium
Każdy moduł to osobna struktura Imperium z określonym przeznaczeniem:
1// Struktura odpowiedzialna za legion
2@Module({
3 controllers: [LegionController],
4 providers: [LegionService],
5 exports: [LegionService],
6})
7export class LegionModule {}
8
9// Struktura odpowiedzialna za trybuty
10@Module({
11 controllers: [TributesController],
12 providers: [TributesService],
13 imports: [DatabaseModule],
14})
15export class TributesModule {}3. Dependency Injection - łańcuch dowodzenia
System DI w NestJS to jak hierarchia w rzymskim legionie - każdy wie, od kogo ma brać rozkazy:
1@Injectable()
2export class TributesService {
3 constructor(
4 private readonly mapService: MapService,
5 private readonly collectorService: CollectorService,
6 ) {}
7
8 async findTributes(province: string) {
9 const location = await this.mapService.getProvince(province);
10 return this.collectorService.collect(location);
11 }
12}Instalacja i pierwsze kroki
Aby rozpocząć naszą kampanię w Imperium, potrzebujesz odpowiedniego wyposażenia. Podstawą jest Node.js: generator projektów nest new w NestJS 12 wymaga wersji 22.22.3 lub nowszej z linii 22 albo 24.15 lub nowszej. Sprawdzisz ją poleceniem node -v. Potem wystarczą cztery polecenia:
1# Instalacja NestJS CLI - naszych inżynieryjnych narzędzi
2npm install -g @nestjs/cli
3
4# Stworzenie nowego Imperium (projektu)
5nest new roman-imperium
6
7# Wejście do systemu
8cd roman-imperium
9
10# Podniesienie sztandarów legionu (uruchomienie serwera)
11npm run start:devStruktura rzymskiego Imperium (projektu)
1roman-imperium/
2├── src/
3│ ├── main.ts # Forum Romanum - punkt wejścia
4│ ├── app.module.ts # Główny Senat
5│ ├── app.controller.ts # Konsul - główny kontroler
6│ ├── app.service.ts # Primus Centurio
7│ ├── legion/ # Struktura legionu
8│ │ ├── legion.module.ts
9│ │ ├── legion.controller.ts
10│ │ └── legion.service.ts
11│ ├── tributes/ # Struktura trybutów
12│ └── forum/ # Struktura Forum
13├── test/ # Poligon treningowy
14├── package.json # Rejestr imperialny
15└── nest-cli.json # Instrukcje inżynieryjnePierwszy endpoint - sygnał imperialny
Stwórzmy nasz pierwszy endpoint, który będzie sygnalizować obecność Imperium:
1// app.controller.ts
2import { Controller, Get } from '@nestjs/common';
3import { AppService } from './app.service';
4
5@Controller()
6export class AppController {
7 constructor(private readonly appService: AppService) {}
8
9 @Get()
10 getSignal(): string {
11 return 'Imperium gotowe do kampanii!';
12 }
13
14 @Get('status')
15 getImperiumStatus() {
16 return {
17 legion: 'Gotowy do kampanii',
18 tributes: 'Zbieranie trwa...',
19 destination: 'Nowe prowincje',
20 consul: 'Caesar.js do usług!',
21 };
22 }
23}Zalety NestJS dla Imperium
- Modularność - każda struktura Imperium ma swoje zadanie
- TypeScript - precyzja jak w rzymskim inżynierstwie
- Dekoratory - czytelne oznaczenia jak sygnały legionu
- Testowanie - sprawdzamy wszystko zanim wyruszymy na kampanię
- Dokumentacja - automatyczne mapy naszych prowincji (Swagger)
- Skalowalność - od małego castrum po architekturę Imperium
Ćwiczenie praktyczne
Czas na pierwszy test imperialny! Stwórz prosty kontroler, który będzie zarządzał podstawowymi informacjami o legionie:
1// legion.controller.ts
2import { Controller, Get, Post, Body } from '@nestjs/common';
3
4@Controller('legion')
5export class LegionController {
6 private legionaries = [
7 { name: 'Marcus Aurelius', role: 'Centurion', experience: 10 },
8 { name: 'Julius Brutus', role: 'Engineer', experience: 5 },
9 ];
10
11 @Get()
12 getAllLegionaries() {
13 return this.legionaries;
14 }
15
16 @Post()
17 addLegionary(@Body() newLegionary: any) {
18 this.legionaries.push(newLegionary);
19 return { message: 'Nowy legionista dołączył do legionu!', legionary: newLegionary };
20 }
21}Następne kampanie
W kolejnych modułach poznamy:
- Services i Providers - specjalistyczne role w Imperium
- Middleware - systemy obronne
- Guards - straże pretoriańskie
- Interceptors - przechwytywanie sygnałów
- Pipes - przetwarzanie danych
- Database integration - magazyny trybutów
- Authentication - identyfikacja obywateli Imperium
- WebSockets - komunikacja między prowincjami
- Testing - przygotowania przed kampanią
Przygotuj się, młody legioniście! Czeka nas fascynująca kampania przez prowincje nowoczesnego web developmentu. NestJS to nie tylko framework - to potężne narzędzie do budowania aplikacji klasy enterprise, które pomoże ci zdobyć najcenniejsze trybuty w świecie programowania!
Pamiętaj: prawdziwy legionista-programista nigdy nie wyrusza bez mapy (dokumentacji) i kompasu (najlepszych praktyk)!
Kod do tej lekcji: src/app.controller.ts
1// NestJS Imperium Romanum - Pierwszy projekt
2import { Controller, Get, Post, Body } from '@nestjs/common';
3import { AppService } from './app.service';
4
5console.log("Salve! Witaj w Imperium Romanum NestJS!");
6console.log("Poznajemy podstawy frameworka dla prawdziwych budowniczych imperium\n");
7
8// ===========================================
9// 1. Podstawowy kontroler - Cesarz systemu
10// ===========================================
11console.log("=== 1. PODSTAWOWY KONTROLER ===");
12
13@Controller()
14export class AppController {
15 constructor(private readonly appService: AppService) {}
16
17 @Get()
18 getWelcome(): string {
19 return this.appService.getWelcome();
20 }
21
22 @Get('status')
23 getImperiumStatus() {
24 return {
25 imperium: 'Roma Aeterna NestJS',
26 caesar: 'Augustus.js',
27 legiones: 'Gotowe do kodowania!',
28 status: 'Budujemy drogi przez TypeScript',
29 framework: 'NestJS',
30 timestamp: new Date().toISOString(),
31 };
32 }
33}
34
35// ===========================================
36// 2. Kontroler Legionów - Zarządzanie żołnierzami
37// ===========================================
38console.log("=== 2. KONTROLER LEGIONÓW ===");
39
40interface Legionary {
41 id: number;
42 name: string;
43 rank: string;
44 experience: number;
45 legio: string;
46}
47
48@Controller('legiones')
49export class LegionController {
50 private legionaries: Legionary[] = [
51 {
52 id: 1,
53 name: 'Marcus Aurelius',
54 rank: 'Legatus',
55 experience: 10,
56 legio: 'Legio X Gemina'
57 },
58 {
59 id: 2,
60 name: 'Gaius Julius',
61 rank: 'Centurio',
62 experience: 8,
63 legio: 'Legio III Augusta'
64 },
65 {
66 id: 3,
67 name: 'Lucius Scipio',
68 rank: 'Optio',
69 experience: 6,
70 legio: 'Legio IX Hispana'
71 },
72 {
73 id: 4,
74 name: 'Titus Flavius',
75 rank: 'Miles',
76 experience: 5,
77 legio: 'Legio XII Fulminata'
78 }
79 ];
80
81 @Get()
82 getAllLegionaries(): Legionary[] {
83 console.log("Lista wszystkich legionistów");
84 return this.legionaries;
85 }
86
87 @Get('commanders')
88 getCommanders(): Legionary[] {
89 console.log("Lista dowódców");
90 return this.legionaries.filter(l => l.rank === 'Legatus' || l.rank === 'Centurio');
91 }
92
93 @Post()
94 recruitLegionary(@Body() newLegionary: Omit<Legionary, 'id'>): Legionary {
95 const legionary: Legionary = {
96 id: this.getNextId(),
97 ...newLegionary,
98 };
99
100 this.legionaries.push(legionary);
101 console.log(`Nowy legionista: ${legionary.name} jako ${legionary.rank}`);
102
103 return legionary;
104 }
105
106 @Get('stats')
107 getLegionStats() {
108 const totalExperience = this.legionaries.reduce((sum, l) => sum + l.experience, 0);
109 const avgExperience = totalExperience / this.legionaries.length;
110
111 const rankCounts = this.legionaries.reduce((counts, l) => {
112 counts[l.rank] = (counts[l.rank] || 0) + 1;
113 return counts;
114 }, {} as Record<string, number>);
115
116 return {
117 totalMilites: this.legionaries.length,
118 totalExperience,
119 averageExperience: Math.round(avgExperience * 10) / 10,
120 rankDistribution: rankCounts,
121 mostExperienced: this.legionaries.reduce((prev, current) =>
122 prev.experience > current.experience ? prev : current
123 ),
124 };
125 }
126
127 private getNextId(): number {
128 return Math.max(...this.legionaries.map(l => l.id), 0) + 1;
129 }
130}
131
132// ===========================================
133// 3. Kontroler Tributum - Zarządzanie podatkami
134// ===========================================
135console.log("=== 3. KONTROLER TRIBUTUM ===");
136
137interface Tributum {
138 id: number;
139 name: string;
140 value: number;
141 provincia: string;
142 collected: boolean;
143 collectedBy?: string;
144}
145
146@Controller('tributum')
147export class TributumController {
148 private tributes: Tributum[] = [
149 {
150 id: 1,
151 name: 'Aurum Galliae',
152 value: 50000,
153 provincia: 'Gallia',
154 collected: true,
155 collectedBy: 'Marcus Aurelius'
156 },
157 {
158 id: 2,
159 name: 'Argentum Hispaniae',
160 value: 75000,
161 provincia: 'Hispania',
162 collected: false
163 },
164 {
165 id: 3,
166 name: 'Aes Britanniae',
167 value: 100000,
168 provincia: 'Britannia',
169 collected: true,
170 collectedBy: 'Gaius Julius'
171 },
172 {
173 id: 4,
174 name: 'Triticum Aegypti',
175 value: 25000,
176 provincia: 'Aegyptus',
177 collected: false
178 }
179 ];
180
181 @Get()
182 getAllTributes(): Tributum[] {
183 console.log("Lista wszystkich podatków");
184 return this.tributes;
185 }
186
187 @Get('collected')
188 getCollectedTributes(): Tributum[] {
189 console.log("Zebrane podatki");
190 return this.tributes.filter(t => t.collected);
191 }
192
193 @Get('pending')
194 getPendingTributes(): Tributum[] {
195 console.log("Oczekujące podatki");
196 return this.tributes.filter(t => !t.collected);
197 }
198
199 @Post('collect/:id')
200 collectTribute(@Body() collector: { collectedBy: string }) {
201 const tributeId = 2;
202 const tribute = this.tributes.find(t => t.id === tributeId);
203
204 if (!tribute) {
205 return { success: false, message: 'Tributum non inventum!' };
206 }
207
208 if (tribute.collected) {
209 return { success: false, message: 'Tributum iam collectum!' };
210 }
211
212 tribute.collected = true;
213 tribute.collectedBy = collector.collectedBy;
214
215 console.log(`Podatek zebrany: ${tribute.name} przez ${collector.collectedBy}`);
216
217 return {
218 success: true,
219 message: `Gratulacje! ${collector.collectedBy} zebrał ${tribute.name}!`,
220 tribute,
221 reward: tribute.value
222 };
223 }
224
225 @Get('aerarium')
226 getAerarium() {
227 const collectedValue = this.tributes
228 .filter(t => t.collected)
229 .reduce((sum, t) => sum + t.value, 0);
230
231 const pendingValue = this.tributes
232 .filter(t => !t.collected)
233 .reduce((sum, t) => sum + t.value, 0);
234
235 return {
236 aerariumValue: collectedValue,
237 pendingValue,
238 totalValue: collectedValue + pendingValue,
239 collectionPercentage: Math.round((collectedValue / (collectedValue + pendingValue)) * 100)
240 };
241 }
242}
243
244// ===========================================
245// 4. Demonstracja API calls
246// ===========================================
247console.log("=== 4. DEMONSTRACJA API ===");
248
249class APIDemo {
250 static async demonstrateAPICalls() {
251 console.log("Demonstracja wywołań API:");
252
253 console.log("GET /legiones - Pobierz legionistów:");
254 const legionaries = new LegionController().getAllLegionaries();
255 console.log(`Znaleziono ${legionaries.length} legionistów`);
256
257 console.log("\nGET /legiones/stats - Statystyki legionów:");
258 const stats = new LegionController().getLegionStats();
259 console.log(`Łącznie milites: ${stats.totalMilites}, Śr. doświadczenie: ${stats.averageExperience}`);
260
261 console.log("\nGET /tributum - Pobierz podatki:");
262 const tributes = new TributumController().getAllTributes();
263 console.log(`Znaleziono ${tributes.length} podatków`);
264
265 console.log("\nGET /tributum/aerarium - Stan skarbca:");
266 const aerarium = new TributumController().getAerarium();
267 console.log(`Wartość aerarium: ${aerarium.aerariumValue} denarii (${aerarium.collectionPercentage}% zebrane)`);
268
269 return {
270 legionaries: legionaries.length,
271 tributes: tributes.length,
272 aerariumValue: aerarium.aerariumValue
273 };
274 }
275}
276
277// ===========================================
278// 5. Uruchomienie demonstracji
279// ===========================================
280console.log("=== 5. URUCHOMIENIE APLIKACJI ===");
281
282async function runImperiumDemo() {
283 try {
284 console.log("Uruchamianie Imperium Romanum NestJS Demo...");
285
286 const results = await APIDemo.demonstrateAPICalls();
287
288 console.log("\nPODSUMOWANIE IMPERIUM:");
289 console.log(`Legioniści: ${results.legionaries} milites`);
290 console.log(`Podatki: ${results.tributes} tributum`);
291 console.log(`Aerarium: ${results.aerariumValue} denarii`);
292 console.log("\nAplikacja NestJS gotowa do podbojów!");
293
294 return results;
295 } catch (error) {
296 console.error("Błąd w Imperium:", error);
297 throw error;
298 }
299}
300
301runImperiumDemo().then(() => {
302 console.log("\nAve Caesar! Demo ukończone pomyślnie!");
303 console.log("Gratulacje - opanowałeś podstawy NestJS!");
304});
305
306export { AppController, LegionController, TributumController };Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Czym jest NestJS?
2. W jakim języku programowania zbudowany jest NestJS?
Zadania praktyczne w grze
- Edytor kodu
Napisz klasę StatusController z dekoratorem @Controller('status') i metodą getStatus() z dekoratorem @Get(). Metoda ma zwracać obiekt z polem status, np. { status: 'Imperium gotowe do kampanii' }.
- Klikanie w kolejności
Ułóż kroki tworzenia nowego projektu NestJS w prawidłowej kolejności: