Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Bazy danych i TypeORM - skarbiec imperium

Witaj z powrotem w systemie, młody legionisto! Consul Caesar.js ma dla Ciebie nowe wyzwanie. Dotychczas nauczyłeś się budować potężne fortyfikacje z NestJS, ale każde prawdziwe imperium potrzebuje miejsca do przechowywania swoich tributów. Czas nauczyć się zarządzania skarbcem - bazami danych!

Czym jest baza danych dla imperium?

Podobnie jak Rzymianie przechowują swoje tributy w bezpiecznych skarbcach w Forum Romanum, aplikacje muszą gdzieś bezpiecznie przechowywać swoje dane. Baza danych to nasz cyfrowy skarbiec, który:

  • Przechowuje tributy - dane aplikacji jak informacje o obywatelach, zamówienia, produkty
  • Zabezpiecza przed stratą - zapewnia trwałość i integralność danych
  • Umożliwia szybkie wyszukiwanie - jak rejestr skarbca pokazujący dokładne lokalizacje
  • Kontroluje dostęp - tylko uprawnieni legionariusze mogą sięgać po tributy

Rodzaje baz danych - różne typy skarbców

Relacyjne bazy danych (SQL)

To jak zorganizowane magazyny z dokładnie opisanymi przegródkami:

1-- Tabela legionariuszy
2CREATE TABLE legionaries (
3 id SERIAL PRIMARY KEY,
4 name VARCHAR(100) NOT NULL,
5 cohort VARCHAR(100),
6 tribute_count INTEGER DEFAULT 0
7);
8
9-- Tabela tributów
10CREATE TABLE tributes (
11 id SERIAL PRIMARY KEY,
12 name VARCHAR(100) NOT NULL,
13 value DECIMAL(10,2),
14 legionary_id INTEGER REFERENCES legionaries(id)
15);

NoSQL bazy danych

To jak elastyczne worki na tributy, gdzie można trzymać różne rzeczy:

1// MongoDB document
2{
3 _id: ObjectId("..."),
4 name: "Marcus Aurelius",
5 cohort: "Legio X Equestris",
6 tributes: [
7 { name: "Golden Coin", value: 100 },
8 { name: "Diamond Ring", value: 500 }
9 ],
10 officers: {
11 optio: "Gaius",
12 signifer: "Marcus"
13 }
14}

TypeORM - magiczny system zarządzania skarbcem

TypeORM to potężne narzędzie, które pozwala nam łatwo zarządzać bazami danych w TypeScript. To jak system zarządzania skarbcem imperium, który automatycznie tłumaczy nasze żądania na język zrozumiały dla bazy danych.

Instalacja TypeORM w aplikacji

1npm install @nestjs/typeorm typeorm pg
2npm install --save-dev @types/pg

Konfiguracja połączenia ze skarbcem

1// app.module.ts
2import { Module } from '@nestjs/common';
3import { TypeOrmModule } from '@nestjs/typeorm';
4
5@Module({
6 imports: [
7 TypeOrmModule.forRoot({
8 type: 'postgres',
9 host: 'localhost',
10 port: 5432,
11 username: 'consul',
12 password: 'imperium123',
13 database: 'roman_empire',
14 entities: [__dirname + '/**/*.entity{.ts,.js}'],
15 synchronize: true, // Tylko podczas rozwoju!
16 }),
17 ],
18})
19export class AppModule {}

Pierwsza entity - rejestr tributów

Entity to jak szczegółowy rejestr pokazujący, jak wygląda konkretny tribut w naszym skarbcu:

1// tribute.entity.ts
2import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';
3
4@Entity('tributes')
5export class Tribute {
6 @PrimaryGeneratedColumn()
7 id: number;
8
9 @Column({ length: 100 })
10 name: string;
11
12 @Column('decimal', { precision: 10, scale: 2 })
13 value: number;
14
15 @Column({ nullable: true })
16 description: string;
17
18 @Column({ default: 'unknown' })
19 location: string;
20
21 @CreateDateColumn()
22 collectedAt: Date;
23
24 @Column({ default: false })
25 isCursed: boolean;
26}

Dekoratory TypeORM - instrukcje dla skarbnika

  • @Entity() - oznacza, że to jest rejestr tributów (tabela)
  • @PrimaryGeneratedColumn() - unikalny numer tributu (klucz główny)
  • @Column() - opis właściwości tributu (kolumna)
  • @CreateDateColumn() - automatycznie zapisuje datę pobrania
  • @UpdateDateColumn() - automatycznie aktualizuje datę modyfikacji

Podstawowe operacje na tributach

Tworzenie nowego tributu

1// tribute.service.ts
2import { Injectable } from '@nestjs/common';
3import { InjectRepository } from '@nestjs/typeorm';
4import { Repository } from 'typeorm';
5import { Tribute } from './tribute.entity';
6
7@Injectable()
8export class TributeService {
9 constructor(
10 @InjectRepository(Tribute)
11 private tributeRepository: Repository<Tribute>,
12 ) {}
13
14 async addTribute(tributeData: Partial<Tribute>): Promise<Tribute> {
15 const newTribute = this.tributeRepository.create(tributeData);
16 return await this.tributeRepository.save(newTribute);
17 }
18
19 async findAllTributes(): Promise<Tribute[]> {
20 return await this.tributeRepository.find();
21 }
22
23 async findTributeById(id: number): Promise<Tribute> {
24 return await this.tributeRepository.findOne({ where: { id } });
25 }
26
27 async updateTribute(id: number, updateData: Partial<Tribute>): Promise<Tribute> {
28 await this.tributeRepository.update(id, updateData);
29 return this.findTributeById(id);
30 }
31
32 async removeTribute(id: number): Promise<void> {
33 await this.tributeRepository.delete(id);
34 }
35}

Typy kolumn - różne rodzaje tributów

1@Entity()
2export class DetailedTribute {
3 @PrimaryGeneratedColumn()
4 id: number;
5
6 // Tekst - nazwa tributu
7 @Column('varchar', { length: 200 })
8 name: string;
9
10 // Liczby całkowite - ilość
11 @Column('int')
12 quantity: number;
13
14 // Liczby dziesiętne - wartość
15 @Column('decimal', { precision: 10, scale: 2 })
16 value: number;
17
18 // Wartości logiczne - czy przeklęty
19 @Column('boolean', { default: false })
20 isCursed: boolean;
21
22 // Data - kiedy pobrany
23 @Column('date')
24 collectionDate: Date;
25
26 // Tekst długi - historia tributu
27 @Column('text')
28 history: string;
29
30 // JSON - szczegóły
31 @Column('json')
32 details: {
33 material: string;
34 origin: string;
35 previousOwners: string[];
36 };
37}

Walidacja danych - sprawdzanie jakości tributów

1import { IsNotEmpty, IsPositive, IsOptional, Length } from 'class-validator';
2
3export class CreateTributeDto {
4 @IsNotEmpty({ message: 'Każdy tribut musi mieć nazwę!' })
5 @Length(3, 100, { message: 'Nazwa musi mieć od 3 do 100 znaków' })
6 name: string;
7
8 @IsPositive({ message: 'Wartość tributu musi być większa od zera' })
9 value: number;
10
11 @IsOptional()
12 @Length(0, 500)
13 description?: string;
14
15 @IsOptional()
16 location?: string;
17}

Konfiguracja modułu tributów

1// tribute.module.ts
2import { Module } from '@nestjs/common';
3import { TypeOrmModule } from '@nestjs/typeorm';
4import { TributeService } from './tribute.service';
5import { TributesController } from './tributes.controller';
6import { Tribute } from './tribute.entity';
7
8@Module({
9 imports: [TypeOrmModule.forFeature([Tribute])],
10 controllers: [TributesController],
11 providers: [TributeService],
12 exports: [TributeService],
13})
14export class TributeModule {}

Kontroler zasóbw

1// tributes.controller.ts
2import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
3import { TributeService } from './tribute.service';
4import { CreateTributeDto } from './dto/create-tribute.dto';
5
6@Controller('tributes')
7export class TributesController {
8 constructor(private readonly tributeService: TributeService) {}
9
10 @Post()
11 async addTribute(@Body() createTributeDto: CreateTributeDto) {
12 return await this.tributeService.addTribute(createTributeDto);
13 }
14
15 @Get()
16 async getAllTributes() {
17 return await this.tributeService.findAllTributes();
18 }
19
20 @Get(':id')
21 async getTribute(@Param('id') id: string) {
22 return await this.tributeService.findTributeById(+id);
23 }
24
25 @Put(':id')
26 async updateTribute(
27 @Param('id') id: string,
28 @Body() updateData: Partial<CreateTributeDto>
29 ) {
30 return await this.tributeService.updateTribute(+id, updateData);
31 }
32
33 @Delete(':id')
34 async removeTribute(@Param('id') id: string) {
35 await this.tributeService.removeTribute(+id);
36 return { message: 'Tribut został usunięty ze skarbca!' };
37 }
38}

Obsługa błędów - ochrona przed klątwami

1import { NotFoundException, BadRequestException } from '@nestjs/common';
2
3@Injectable()
4export class TributeService {
5 //
6 async findTributeById(id: number): Promise<Tribute> {
7 if (id <= 0) {
8 throw new BadRequestException('ID tributu musi być większe od zera');
9 }
10
11 const tribute = await this.tributeRepository.findOne({ where: { id } });
12 
13 if (!tribute) {
14 throw new NotFoundException(`Tribut o ID ${id} nie został znaleziony w skarbcu`);
15 }
16
17 return tribute;
18 }
19
20 async removeTribute(id: number): Promise<void> {
21 const tribute = await this.findTributeById(id);
22 
23 if (tribute.isCursed) {
24 throw new BadRequestException('Nie można usunąć przeklętego tributu!');
25 }
26
27 await this.tributeRepository.delete(id);
28 }
29}

Praktyczne ćwiczenie

Teraz gdy już znasz podstawy, czas na własne eksperymenty! Spróbuj stworzyć własną entity dla legionariuszy:

1// Twój kod tutaj
2@Entity('legionaries')
3export class Legionary {
4 // Dodaj właściwości:
5 // - id (auto-generowane)
6 // - name (tekst, wymagane)
7 // - cohort (tekst, opcjonalne)
8 // - tributeCount (liczba, domyślnie 0)
9 // - joinedAt (data dołączenia)
10 // - isActive (czy aktywny, domyślnie true)
11}

Podsumowanie

Gratulacje! Nauczyłeś się podstaw zarządzania skarbcem danych. Teraz umiesz:

Konfigurować TypeORM w aplikacji NestJS ✓ Tworzyć entity - rejestry swoich tributów ✓ Wykonywać podstawowe operacje CRUD na danych ✓ Walidować dane przed zapisem do skarbca ✓ Obsługiwać błędy i chronić przed klątwami

W następnych lekcjach poznasz zaawansowane techniki jak relacje między tabelami, migracje i zaawansowane zapytania. Consul Caesar.js jest dumny z Twoich postępów!

Ir a CodeWorlds