Mongoose oferuje zaawansowane możliwości, które sprawiają, że zarządzanie danymi w MongoDB jest nie tylko wygodne, ale i bardziej kontrolowane. Dziś zajmiemy się dwoma potężnymi narzędziami Mongoose: walidacją oraz middleware. Dzięki walidacji możemy wymuszać poprawność danych przed ich zapisaniem, a middleware pozwala wykonywać dodatkowe operacje przed i po określonych akcjach na dokumentach.
Walidacja w Mongoose
Walidacja w Mongoose pozwala nam na narzucenie dodatkowych wymagań na dane przed ich zapisaniem do bazy. Dzięki temu mamy większą kontrolę nad tym, co trafia do bazy danych i możemy uniknąć błędów.
Dodawanie walidacji do schematów
Mongoose oferuje kilka podstawowych opcji walidacji, które możemy dodać bezpośrednio do schematu. Oto kilka z nich:
- required: Pole musi mieć wartość.
- min / max: Określa minimalną i maksymalną wartość dla liczby.
- minLength / maxLength: Określa minimalną i maksymalną długość dla stringów.
- enum: Ogranicza pole do określonej listy wartości.
Przykładowy schemat z podstawową walidacją:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
age: { type: Number, min: 18, max: 100 },
role: { type: String, enum: ['user', 'admin', 'moderator'] }
});
const User = mongoose.model('User', userSchema);
W powyższym przykładzie:
namejest wymagane.emailmusi być unikalne i również wymagane.agema określony zakres między 18 a 100.rolemoże przyjmować tylko wartościuser,adminlubmoderator.
Customowe walidatory
Mongoose pozwala na tworzenie niestandardowych walidatorów, które możemy dostosować do specyficznych wymagań naszej aplikacji. Aby dodać własny walidator, możemy użyć opcji validate i zdefiniować funkcję sprawdzającą.
Przykład: Dodajmy walidator, który sprawdzi, czy adres e-mail pochodzi z domeny „example.com”.
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: {
type: String,
required: true,
unique: true,
validate: {
validator: function (email) {
return email.endsWith('@example.com');
},
message: 'Email musi pochodzić z domeny example.com'
}
},
age: { type: Number, min: 18 }
});
const User = mongoose.model('User', userSchema);
W powyższym przykładzie funkcja walidująca validator zwraca true tylko wtedy, gdy e-mail kończy się na „@example.com”. Jeśli walidacja zakończy się niepowodzeniem, zwrócony zostanie komunikat o błędzie: „Email musi pochodzić z domeny example.com”.
Asynchroniczne walidatory
Możemy również tworzyć asynchroniczne walidatory, które wykonują operacje asynchroniczne, np. sprawdzenie dostępności zasobu online.
Przykład asynchronicznego walidatora:
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
username: {
type: String,
required: true,
validate: {
validator: async function (username) {
const user = await User.findOne({ username });
return !user; // Walidacja pomyślna, jeśli nazwa użytkownika nie istnieje
},
message: 'Nazwa użytkownika jest zajęta'
}
}
});
const User = mongoose.model('User', userSchema);
W tym przykładzie funkcja walidująca asynchronicznie sprawdza, czy użytkownik o podanej nazwie już istnieje.
Middleware w Mongoose
Middleware to funkcje, które wykonują się przed lub po pewnych operacjach na dokumentach. Mongoose oferuje dwa typy middleware:
- pre – wykonuje się przed daną akcją.
- post – wykonuje się po zakończeniu akcji.
Pre Middleware
Pre middleware pozwala wykonać operacje przed zapisaniem, usunięciem lub aktualizacją dokumentu. Jest to szczególnie przydatne, gdy chcemy np. zaszyfrować hasło przed zapisaniem go do bazy.
Przykład pre middleware, który uruchamia się przed zapisem dokumentu:
userSchema.pre('save', function (next) {
if (this.isModified('password')) {
// Zakładamy, że mamy funkcję hashującą hashPassword
this.password = hashPassword(this.password);
}
next();
});
W powyższym przykładzie middleware sprawdza, czy pole password zostało zmodyfikowane. Jeśli tak, hasło jest hashowane, a następnie zapisane.
Post Middleware
Post middleware wykonuje się po zakończeniu określonej akcji. Może być używane do operacji, które nie wpływają bezpośrednio na dane, ale np. logują zakończenie akcji lub wywołują inne usługi.
Przykład post middleware, który uruchamia się po usunięciu dokumentu:
userSchema.post('remove', function (doc) {
console.log(`Usunięto użytkownika: ${doc.name}`);
});
Po usunięciu użytkownika, post middleware wyświetla informację o tym w konsoli.
Middleware na innych operacjach
Mongoose pozwala na stosowanie middleware na różnych etapach cyklu życia dokumentu. Możemy np. użyć pre lub post middleware dla operacji takich jak findOneAndUpdate, deleteOne i innych.
Przykład: pre middleware dla findOneAndUpdate, aby aktualizować datę modyfikacji dokumentu:
userSchema.pre('findOneAndUpdate', function (next) {
this.set({ updatedAt: new Date() });
next();
});
W powyższym kodzie każdorazowa aktualizacja dokumentu spowoduje automatyczne ustawienie pola updatedAt na aktualną datę.
Podsumowanie
Mongoose zapewnia zaawansowane możliwości zarządzania danymi dzięki walidacji i middleware. Podstawowe zasady:
- Walidacja: Można dodawać podstawowe walidatory bezpośrednio w schemacie, korzystać z niestandardowych funkcji lub tworzyć asynchroniczne walidatory.
- Middleware:
preipostmiddleware pozwalają wykonywać operacje przed i po zapisie, aktualizacji czy usunięciu dokumentów.
Dzięki walidacji i middleware nasze aplikacje stają się bardziej spójne i łatwiejsze do zarządzania. Te funkcje są kluczowe, gdy aplikacja rośnie i wymaga większej kontroli nad danymi i procesami.