Najważniejszym elementem backendu jest baza danych. Jej stworzenie wcale nie jest proste a zarządzanie nią może być udręką. Na wstępie dodam jeszcze, że ten post nie będzie tak długi jak pierwszy wpis o generowaniu projektu frontendowego.

Baza danych

Od tego należy zacząć. Ja wybrałem bazę danych MySQL ponieważ jestem przyzwyczajony do korzystania z niej. Nie czuję się na siłach jeżeli chodzi o porównywanie różnych silników baz danych. Korzystałem kilka razy z nierelacyjnych baz danych czyli tak zwanego NoSQL ale dla mnie było to trochę trudne i miałem wrażenie (podobno mylne), że to wszystko jest jakieś nielogiczne. Widocznie jestem typem człowieka, który nie rozumie jak można działać bez relacji.

Schemat bazy

Zanim przejdziemy do samego tworzenia bazy dobrze byłoby wiedzieć co tak na prawdę potrzebuję. Zastanówmy się więc wspólnie nad tym. Zgodnie z założeniami projektu chcemy mieć bazę klientów. Każdy klient ma mieć przypisane swoje samochody, które odwiedziły warsztat. Każdy samochód powinien mieć naprawy. Może mieć ich co oczywiste kilka. Chciałbym jeszcze żeby naprawy miały przypisanych mechaników, którzy je wykonywali. To zmiana w stosunku do tego pisałem w tym poście. Podsumujmy więc:

Muszę stworzyć 4 tabele. Będą to: klienci, samochody, naprawy, mechanicy. W takim razie popatrz na poniższy schemat schemat.

schemat bazy danych
Schemat bazy danych

Co dalej?

No więc mam już przygotowany schemat bazy danych. Dodam tylko, że ten schemat wygenerowałem za pomocą aplikacji webowej dbdesigner, która pozwala tworzyć w prosty sposób takie schematy. Teraz nadszedł czas na stworzenie zaprojektowanej bazy. Póki co nie będę korzystał z zewnętrznego serwera dlatego też wykorzystam lokalną bazę danych.

Baza danych – instalacja

Istnieje przynajmniej dwa sposoby na postawienie lokalnego serwera bazy danych MySQL. Można ściągnąć serwer bezpośrednio ze strony producenta lub skorzystać z całej paczki takiej jak xampp, która zawiera oprócz bazy danych także serwer. Osobiście nawet lubię xamppa ale przy pracy nad CarReps będę korzystał z tego pierwszego rozwiązania. Jako, że mam już zainstalowane wszystko czego potrzebuję to nie będę tutaj opisywał całego procesu. Jeżeli masz problem z instalacją to jeżeli korzystasz z MacOSa polecam Ci instrukcję, natomiast jeżeli korzystasz z Windowsa to instrukcję możesz znaleźć tutaj.

Czas przejść do nodejs!

Skoro już mam pod ręką schemat bazy danych, sama baza także już stoi i oczekuje na moje działanie to czas najwyższy zacząć coś kodzić. No może jeszcze nie tak od razu ponieważ najpierw pasowałoby zainstalować kilka niezbędnych paczek i może przygotować się do pracy.

ORM

Zacznijmy może od tego co to jest. ORM (object-relational mapping) to narzędzie pozwalające na tworzenie bazy danych bez pisania kodu sql. Tabele, kolumny i relacje tworzy się za pomocą kodu w którym piszemy aplikację. W moim przypadku będzie to javascript. ORM z którego będę korzystał to TypeORM.

Inicjalizacja projektu

Zanim jednak zainstaluję jakąkolwiek bibliotekę do mojego projektu to muszę go stworzyć. Skorzystam więc z konsoli, przejdę do odpowiedniego projektu i użyję komendy:

npm init

Komenda ta stworzy prawie pusty plik package.json. Teraz możemy przejść do zainstalowania niezbędnych rzeczy dla naszego projektu. Nodejs posiada mnóstwo przydatnych narzędzi do tworzenia backendu. Skorzystam tylko z kilku z nich. Niestety nie mam tutaj żadnego CLI, które wygenerowałoby projekt za mnie więc muszę przygotować strukturę projektu samodzielnie.

struktura plików nodejs
Struktura plików

W dzisiejszym poście zajmę się tylko uzupełnieniem folderu entities tak aby powstał zarys bazy danych. Na końcu objaśnię jeszcze to co znajduje się w pliku ormconfig bo uważam, że to ważne.

Tworzenie bazy danych przez ORM.

Aby utworzyć odpowiednią strukturę bazy danych muszę stworzyć taką samą strukturę plików w folderze entities.

nodejs entities files
Pliki encji

Ok pliki zostały stworzone. Plik Client.ts oraz Mechanic.ts będą wyglądać bardzo podobnie. Patrząc na schemat bazy widać, że najbardziej będzie wyróżniał się plik Car.ts. Dlatego też jego wezmę na warsztat i postaram się wytłumaczyć dlaczego wygląda to właśnie tak.

Plik Car.ts

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from 'typeorm';
import { Length } from 'class-validator';
import { Client } from './Client';
import { Repair } from './Repair';

@Entity('cars')
export class Car {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  @Length(2, 255)
  brand: string;

  @Column()
  @Length(0, 255)
  model: string;

  @Column()
  @Length(0, 255)
  register_number: string;

  @Column()
  @Length(0, 4)
  production_year: number;

  @ManyToOne(type => Client, client => client.cars)
  client: Client;

  @OneToMany(type => Repair, repair => repair.car)
  repairs: Repair[];
}

Na początek pominę importy na początku bo nie ma tam bardzo ciekawych rzeczy a te chociaż troszkę interesujące będą występować później. Do tworzenia encji TypeORM używa dekoratorów takich jak @Entity() który widzimy po raz pierwszy w linii 6. Dzięki temu dekoratorowi typeorm wie, że klasa która nastąpi po tej linii będzie musiała zostać zmapowana na tabelę. W linii 6 widać też, że w parametrze przekazanym do dekoratora można podać jaką nazwę ma mieć nowo utworzona tabela. No dobrze przejdźmy dalej. Następny ciekawy dekorator jest w linii 9. @PrimaryGeneratedColumn oznacza dokładnie tyle co jego nazwa. Tworzy primary key (klucz główny brzmi jakoś nie tak), który jest automatycznie inkrementowany wraz z nowymi rekordami w danej tablicy. Później mamy normalne kolumny oznaczone dekoratorem @Column. W ich przypadku jest jeszcze jeden dekorator @Length(), który pochodzi z zaimportowanej w linii drugiej klasy należącej do biblioteki class-validator. Jest to jak sama nazwa wskazuje walidator dodający ograniczenia do pól w bazie danych.

Na samym końcu pliku, od linii 28 do 32 mamy dwa ciekawe dekoratory tworzące relację pomiędzy zaimportowanymi w liniach 3 i 4 plikami innych encji a encją samochodu. Z tego zapisu widać, że klient może mieć kilka samochodów a samochód kilka napraw.

Co teraz?

Myślę, że wszystko jest póki co dość proste. Zostało mi omówić już tylko jeden plik.

{
    "type": "mysql",
    "host": "localhost",
    "port": 3306,
    "username": "carreps",
    "password": "database",
    "database": "carreps",
    "entities": ["./src/entities/*.ts"],
    "logging": true,
    "synchronize": true,
    "migrations": ["./src/migration/*.ts"],
    "cli": {
        "migrationsDir": "src/migration",
        "entitiesDir": "src/entities"
    }
  }

ormconfig.json bo to właśnie o nim mowa, zawiera całą konfigurację typeorma. Od góry widzimy tutaj jaki mamy rodzaj bazy danych, jej adres, użytkownika i jego hasło, nazwę konkretnej bazy danych, ścieżkę do plików z encjami, ścieżkę do migracji oraz ścieżki do katalogów dla narzędzia cli. Są jeszcze dwa klucze ustawione na true czyli logging i synchronize. Pierwszy służy tylko do debugowania i loguje wszystkie operacje wykonywane na bazie a drugi pozwala synchronizować stan encji z bazą danych za każdym połączeniem. Szczególnie ten drugi jest ważny ponieważ pozwala na stworzenie tabel i kolumn przy połączeniu się z bazą.

Wygenerujmy bazę!

Przykładowo wypełniony plik index.ts znajdziesz w repozytorium a jego omówieniem zajmę się dopiero w następnej części gdzie zajmę się już konkretnym kodem dotyczącym API. Wtedy też pokażę skrypty, które napisałem w pliku package.json. Póki co jednak po prostu skopiuj te dwa pliki z repozytorium i uruchom komendę:

npm run start

Powinieneś zobaczyć coś mniej więcej takiego.

stworzona baza danych kod sql
Kod SQL służący do tworzenia zadeklarowanej bazy

To znak, że wszystko zrobiłeś poprawni a Twoja baza danych istnieje w takim kształcie w jakim istnieć powinna.

Podsumowanie

Ok to tym razem przygotowaliśmy schemat bazy danych na podstawie naszych wymagań co do tego co powinno się znajdować w aplikacji. Później stworzyliśmy strukturę projektu nad którą jeszcze będziemy pracować ale mamy już dobry punkt wyjścia. Na sam koniec napisaliśmy encje do stworzonego wcześniej schematu i wygenerowaliśmy gotową bazę. Myślę, że to całkiem dobry wynik. Następnym razem zajmiemy się napisaniem prostych kontrolerów, które będą przeprowadzały operacje na wygenerowanych tabelach za pomocą API.

To by było na tyle. Dzięki wielkie za poświęcenie czasu na ten materiał. Zapraszam do komentowania postu, wytykania mi błędów lub wskazywania lepszych rozwiązań. Przypominam też, że cały kod jest dostępny w repozytorium pod tym linkiem.

Jeżeli spodobał Ci się ten materiał i nie chcesz aby ominął Cię nowy wpis to zachęcam do zapisania się do newslettera. Gwarantuję, że nie wysyłam spamu. Tylko konkretne informacje o nowych postach i o tym co dzieje się na blogu.

Seria: „Własna aplikacja”

Ten post jest częścią serii o nazwie „Własna aplikacja”. Resztę postów z tej serii możesz znaleźć tutaj:

Autor

2 komentarze

  1. Czemu ‚brand’ ograniczasz od dołu na 4? A co z BMW np?

    • Mateusz Reply

      Racja! Nie pomyślałem o BMW 😀 W sumie ten fragment został potraktowany jako bezmyślne kopiuj wklej. Błąd wyłapałbym dopiero podczas próby wpisania tam jakiegoś auta BMW 😀 Dzięki wielkie za zwrócenie uwagi. Poprawię niezwłocznie! 🙂

Napisz komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.