Komunikacja z bazą danych mongoDb w node.js
Trzeci temat związany z node.js. Dzisiaj zajmiemy się komunikacją z MongoDb. Czyli troszkę liźniemy model.
W ramach przypomnienia, poniżej macie listę poprzednich wpisów:
- Najważniejszy – jak używać MongoDb?
- Początki w Node.js
- Widoki w Node.js – część pierwsza
- Widoki w Node.js – część druga – Handlebars
W pierwszej części serii artykułów traktujących o Node.js już instalowaliśmy paczkę Mongoose – która odpowiada z komunikację z bazą danych.
Łączymy się z mongoDb
Pierwsze co to należy połączyć się z bazą danych, więc zaczynajmy. W pliku index.ts doklejamy coś takiego:
/** * ### DATABASE */ mongoose.connect('mongodb://localhost:27017/hairstyling').then( () => { console.log('Udało się!'); }, ).catch((err) => { console.log('MongoDB connection error. Please make sure MongoDB is running. ' + err); process.exit(); });
Oraz dodajemy import u góry pliku:
import mongoose from "mongoose";
Po pierwsze – to jest bardzo brzydki sposób na łącznie się z bazą danych – przerobimy go niedługo na plik konfiguracyjny – no ale tak widać wszystko jak na tacy. Interesuje nas parametr funkcji connect – musimy tam podać ścieżkę do naszej bazy danych – czyli host:port/nazwa_bazy.
Dla naszego ułatwienia wrzuciłem tam console.log – aby było wiadomo, że już się połączyliśmy z bazą. No i następnie łapiemy ewentualny błąd. Jakoże bez bazy nic nie zrobimy to zabijamy naszą aplikację – takimi to brutalami będziemy 😉
Jeśli się nie połączysz – to sprawdź czy masz uruchomione mongoDb i skonfigurowane tak jak podałem w pierwszym linku. Jeśli postępowałeś od samego początku wg moich wskazówek to na 99% wystarczy, aby uruchomić mongo:
sudo service mongod start sudo service mongod status
Tworzymy pierwszą kolekcje.
Jak pamiętasz – do kolekcji wrzucamy dokumenty – obiekty json, które mogą mieć dowolną zawartość. Jednak, wypadałoby jakąś strukturę zachować i modele nam w tym pomogą. Dokładniej mówiąc schema i interface. Także stwórz sobie dwa nowe foldery w src: schemas oraz interfaces.
Czym jest interface?
Interfejs jest zbiorem nazw, których istnienia wymagamy od implementującej go klasy. Brzmi skomplikowanie co nie? Tutaj wchodzimy powoli w obiektowość. Zerknij sobie na kod naszych kontrolerów. Mamy tam pojedyncze funkcje index, update, delete i tak dalej. I następnie w naszym głównym pliku index.ts udostępniamy te funkcje w jakiejś przestrzeni nazw np. homeController. Możemy to opakować w klasę (zrobimy to w kolejnych częściach) i upraszczamy sobie kod. Nasza klasa – czyli obiekt zawiera metody (funkcje), które odpowiadają za wyświetlanie odpowiedniej strony.
Wyobraź sobie, że tworzysz sobie panel administratora – taki podstawowy masz w nim zawsze listę, formularz zapisu/edycji, możliwość usuwania bądź podglądu jakiegoś elementu – przykładowo usługi. Oprócz usługi może to być klient i zamówienie. Napisałeś sobie kontroler w ten sposób, że masz jedną akcje do listy, drugą do zapisu, edycji i kolejne do usuwania i podglądu – tak jak wyżej napisałem.
I zajmijmy się (abstrakcyjnie) listą. Każda lista potrzebuje od nas 3 rzeczy: kolumn, elementów i akcji. No to tworzymy sobie odpowiednio klasy: service, order oraz contractor. Każda z nich ma w sobie funkcje: getColumns, getForm, save, edit, delete. No i nasz kontroler na podstawie adresu wyszukuje sobie odpowiedniej klasy i pobiera dane z odpowiedniej funkcji.
Co się stanie jak zapomnę w klasie contractor zrobić funkcji getColumns? Przy próbie uruchomienia przez użytkownika strony z listą kontrahentów będzie błąd. Jak go uniknąć? Stworzyć interfejs.
W tym interfejsie będziesz wymuszał na klasie jakie ma mieć w sobie metody/funkcje oraz w jakim typie one mają być (tekst/liczba/tablica obiektów i tak dalej) – wtedy jak zapomnisz stworzyć funkcji getColumns to edytor od razu Ci o tym przypomni i nawet nie uruchomisz aplikacji.
Czym jest schema?
Schema – schemat. Tutaj informujemy mongoose jak będzie wyglądać każdy nasz dokument (rekord) w kolekcji. Także to akurat jest proste. Więc do dzieła.
Nasze usługi
Zastanów się jakie pola powinny mieć usługi przez nas oferowane. Moim zdaniem usługa będzie zawierać: cenę (najlepiej netto i brutto – ale póki co się ograniczymy tylko do zwykłej ceny), nazwę usługi i jej opis. Także stwórzmy sobie interfejs:
export interface IService { name?: string; price?: number; description?: string; }
Przyjęło się, że interfejsy zaczynają się od literki I. Informacyjnie dodam, że znak zapytania oznacza tutaj, że dany parametr jest opcjonalny. U nas teoretycznie wszystkie są wymagane, no ale potem sobie poprawimy – póki co w ten sposób prościej, łatwiej i przyjemniej do nauki.
Jak za każdym razem powtarzam, sporo rzeczy nie robimy tak jak należy- po to, aby prościej było zrozumieć.
Plik oczywiście zapisz w src/interfaces/IService.ts
Tworzymy schemat
import {Document, Model, model, Schema } from 'mongoose'; import {IService} from '../interfaces/IService'; type ServiceDocument = IService & Document; export const ServiceSchema = new Schema({ name: String, price: Number, description: String, }); export const ServiceModel: Model= model ('services', ServiceSchema);
Tworzymy nową schemę z naszymi polami, następnie tak jak pisałem na podstawie naszego interfejsu tworzymy model (no i typ – ale o tym później) i wskazujemy jak będzie nazywała się nasza kolekcja.
Mówiąc prościej w naszej kolekcji o nazwie services będą dokumenty, które wyglądają jak nasz interfejs IService.
Ten plik zapisz w src/schemas/service.ts
Czas uzupełnić bazę MongoDb
Moglibyśmy to zrobić przez klienta mongoDb, ale w ten sposób mniej rzeczy poznamy. Także stwórz sobie nowy kontroler, a w nim dwie metody. Jedną, która uzupełni nam listę, a druga nam ją wyświetli. Oczywiście, jeszcze to nie będzie obiektowy kontroler(tym zajmiemy się w następnej części).
import {Request, Response} from 'express'; import {ServiceModel} from '../schemas/service'; export let createData = (req: Request, res: Response) => { console.log('create data'); const service = new ServiceModel(); service.name = "Strzyżenie włosów - mężczyźni"; service.description = "Cena obejmuje strzyżenie oraz ułożenie włosów krótkich"; service.price = 20; service.save(); const service2 = new ServiceModel(); service2.name = "Scinanie końcówek"; service2.description = "Cena obejmuje umycie oraz ścięcie zniszczonych końcówek włosów"; service2.price = 100; service2.save(); const service3 = new ServiceModel(); service3.name = "Farbowanie"; service3.description = "Cena obejmuje pofarbowanie włosów wraz z jednym opakowaniem farby. Za dodatkową farbę należy dopłacić."; service3.price = 75; service3.save(); res.send('Zapisano usługi'); return; };
Plik nazwij: src/controllers/serviceController.ts
Dodaj w odpowiednich miejscach poniższe linijki w pliku index.ts i uruchom aplikacje pod linkiem: http://localhost:3000/service/create
import * as serviceController from './controllers/serviceController'; //service app.get('/service/create', serviceController.createData);
Mam nadzieję, że nie zapomniałeś uruchomić aplikacji 😉 Teraz sprawdź czy dodały się zmiany – ale to już zrobimy przez terminal:
mongo > use hairstyling; > db.services.find();
W odpowiedzi dostaniesz 3 rekordy.
Czas na wyjaśnienia:
Kodu było dużo, ale jest prosty do zrozumienia. W naszej funkcji createData stworzyliśmy sobie 3 modele: service, service2, service3. To jest zrozumiałe – w końcu przed chwilą stworzyliśmy sobie model w folderze schema na podstawie interfejsu.
Następnie każdy element sobie uzupełniamy odpowiednimi danymi czyli nazwa, opis i cena. Też proste.
Kolejny krok to zapis. Zapis odbywa się asynchronicznie. Nie czekamy, aż funkcja save() zwróci wynik. Ważne jest, że save() jest tak zwanym Promise (obietnicą). Co w dużym skrócie oznacza, że kiedyś zwróci wynik (lub błąd). Przykład użycia promise masz chociażby przy próbie łączenia się z mongoDb.
Jednak promise nie jest tematem tej serii artykułów – to polecam samemu zapoznać się z tym tematem, bo jeszcze nie raz on się nam pojawi.
Podsumowując stworzenie nowego dokumentu w bazie to:
- Stworzenie obiektu na podstawie modelu
- Uzupełnienie wartości
- Zapis przez wywołanie funkcji save()
Wyświetlamy nasze usługi – pobieranie danych z MongoDb
Zapisaliśmy dane w bazie MongoDb – teraz trzeba je wyświetlić. Chyba prostszej rzeczy nie ma (okej zapis jest prostszy). Stwórz sobie w kontrolerze funkcje index – która wyświetli listę usług jakie oferujemy:
export let index = (req: Request, res: Response) => { let query = ServiceModel.find(); let items = query.exec(); console.log(items); let renderData = { items: items, }; return res.render('service/index', renderData); };
// in index.ts app.get('/services', serviceController.index);
oraz widok:
<h1>Witaj w naszym salonie kosmetycznym!</h1>
<p>Zobacz co możemy Tobie zaoferować</p>
<ul class="people_list">
{{#each items}}
<li>
<div>
<h3> {{this.name}} za jedyne {{this.price}} zł</h3>
<em>{{this.description}}</em>
</div>
</li>
{{/each}}
</ul>
Przebuduj aplikację i odpal odpowiednią stronę. Ups… nic się nie wyświetliło, natomiast w konsoli dostałem:
Promise { <pending> }
Znowu trafiliśmy na Promise. Napisałem – poczytaj 😉 Można to załatwić na dwa sposoby – pierwszy znasz i polega na wykorzystaniu then – przyjrzyj się jak to wygląda w wypadku łączenia się z bazą danych mongoDb. Drugi polega na wykorzystaniu słówka await, które informuje aplikacje, że musimy poczekać za wynikiem. Pierwszą opcje znasz, także pokaże jak użyć tej drugiej:
export let index = async (req: Request, res: Response) => { let query = ServiceModel.find(); let items = await query.exec(); console.log(items); let renderData = { items: items, }; return res.render('service/index', renderData); };
No i teraz wszystko działa 😉 Proste, prawda?
Kilka słów wyjaśnienia – czyli co znajdziemy w modelu?
Jak zauważyłeś wyciągniecie wszystkich danych polega na wykorzystaniu metody find(). i następnie wykonania zapytania. Możemy dorzucić tutaj obiekt z naszymi warunkami, sortować, ustawić limit, usunąć dane albo znaleźć i edytować. Opcji jest mnóstwo. Zresztą sam zerknij:

Sporo co nie? I jak widzisz to nie ma tutaj wszystkiego, bo wspominałem chociażby o limicie, który dopiero zobaczysz gdy wywołasz go na find().
Na zakończenie
Podsumowując. Dzisiaj połączyłeś się z bazą danych, zobaczyłeś czym jest Model – czyli kolejną literkę z MVC poznałeś. Oraz wiesz jak pokazać wszystkie dane. Teraz masz zadanie domowe: zapoznaj się z wszystkimi powyższymi metodami i spróbuj ich użyć. Dodatkowo zrób podstronę dla jednej konkretnej usługi😉
Wszystko to co pokazałem, staram się pokazywać w najprostszy sposób – nie zawsze zgodny z ogólnymi praktykami. Także pamiętaj, aby uzupełniać wiedzę, którą Ci przekazuje w moich artykułach. Jeśli będziesz miał z czymś problem to zachęcam do komentowania.