Tworząc aplikację, bardzo często zachodzi potrzeba pracy na danych. Nie zawsze istnieje możliwość pracy na realnych danych. Oczywiście nic nie stoi na przeszkodzie, abyśmy uruchomili pokłady swojej wyobraźni i wpisywali do bazy danych testowe rekordy. Niemniej jednak jest to mało produktywne zajęcie. Nie wspominam nawet o tym, że takich rekordów często potrzeba dziesiątki, setki, a nawet i czasem tysiące. W tym miejscu z pomocą przychodzą seedery, czyli specjalne funkcje pozwalające nam na generowanie testowych danych.
Po raz pierwszy z seederami zetknąłem się podczas pracy z Laravelem. Tam cały proces generowania oraz uruchamiania seedera jest mocno zautomatyzowany i wbudowany w sam framework. Do utworzenia pliku oraz samego seedowania mamy gotowe polecenia i w praktyce jedyne co musimy podać to schemat danych, na którego podstawie zostaną one wygenerowane.
A jak jest w Node?
W Node.js trzeba ogarnąć ten temat na własną rękę. Postanowiłem więc napisać własne rozwiązanie i przedstawić je w tym artykule, tak aby jednocześnie pochwalić się tym, co stworzyłem, ale także poddać to ocenie i weryfikacji. Do przygotowania rozwiązania wykorzystałem bibliotekę mongoose oraz bazę dokumentową MongoDB.
Krok po kroku
Pierwszym krokiem będzie stworzenie modelu. Na jego podstawie będę tworzył testowe dane i zamieszczał je w bazie danych. Drugim krokiem jest stworzenie wcześniej omawianego seedera. Na początku tworzę wykonać połączenie z bazą danych. Sam seeder w naszym przypadku nie jest integralną częścią aplikacji, dlatego zdecydowałem się na osobne połączenie. Następnie tworzę obiekt będący reprezentacją naszych danych testowych. Kolejnym krokiem jest użycie na obiekcie z danymi metody save
oraz zamknięcie połączenia z bazą danych.
import mongoose from 'mongoose';
import Message from '../models/Message';
mongoose.connect( 'mongodb://localhost:27017/db-url', { useNewUrlParser: true } );
const message = new Message({
message: 'Hello World!'
});
message.save().then( () => mongoose.disconnect() );
Jest to rozwiązanie działające, jednak można dodać w nim kilka usprawnień.
Optymalizacja
Przede wszystkim warto dodać funkcję, która przed seedowaniem wyczyści kolekcję z pozostałości po poprzednim seedowaniu.
Kolejnym bardzo ułatwiającym życie elementem jest skorzystanie z Fakera. Faker to biblioteka służąca do generowania losowych, fałszywych danych. Za pomocą Fakera można wygenerować takie dane jak imiona, nazwiska, numery telefonu, adresy email itd.
Aby skorzystać z Fakera, należy go najpierw oczywiście dodać do projektu i zaimportować. Całą, obszerną listę możliwych do wygenerowania danych oraz sposób użycia samej biblioteki znajdziesz w dokumentacji. Po wykonaniu seedowania powinniśmy otrzymać komunikat o powodzeniu lub o błędzie. Poniższy przykład zawiera automatyczne seedowanie z wykorzystaniem Fakera.
import faker from 'faker';
import mongoose from 'mongoose';
import Message from '../models/Message';
mongoose.connect('mongodb://localhost:27017/db-url', { useNewUrlParser: true });
Message.deleteMany({}, err=> console.log(err));
const message = new Message({
message: faker.lorem.sentence()
});
message.save()
.then(() => mongoose.disconnect())
.then(() => console.log('Message seed succesfully'))
.catch(err => console.log(err));
Skalowalność
Ze skalowaniem ilości seedowanych danych nie ma żadnego problemu. Wystarczy stworzyć pętlę, która wykona się żądaną ilość razy.
Schody zaczynają się w momencie tworzenia kolejnych seederów. Nie ma problemu w ręcznym wykonaniu polecenia dla jednego bądź kilku seederów. Problem zaczyna się pojawiać w momencie, w którym tych seederów pojawi się znaczna ilość. Warto byłoby zoptymalizować również i ten proces. Do tego zadania posłuży nowy plik, w którym zamieszczę skrypt odpowiadający za uruchomienie wszystkich plików w zadanym katalogu.
import fs from 'fs';
import async from 'async';
const exec = require('child_process').exec
const scriptsFolder = './src/seed/';
const files = fs.readdirSync(scriptsFolder);
const funcs = files.map(function(file) {
return exec.bind(null, `babel-node ${scriptsFolder}${file}`);
});
function getResults(err, data) {
if (err) {
return console.log(err);
}
const results = data.map(function(lines){
return lines.join('');
});
console.log(results);
}
async.parallel(funcs, getResults);
Teraz jedyne co pozostaje to stworzyć odpowiednie polecenie w pliku package.json
, które uruchomi nasz plik startujący nasze seedery. Po uruchomieniu, jeżeli wszystko zostało zrobione jak należy, w bazie danych powinny pojawić się nowe dokumenty.
Podsumowanie
Rozwiązanie przedstawione w tym wpisie nie jest w 100% idealne. Występuje np. problem z referencjami do zasobów. Niemniej jednak jest to świetny punkt wyjścia do dalszych prac. Serdecznie zachęcam do pozostawienia opinii, pytań oraz propozycji na kolejne wpisy w komentarzu.
Fajne zdjęcie