Verwendung von MongoDB + Mongoose mit Node.js - Best Practices für Back-End-Entwickler

MongoDB ist zweifellos eine der beliebtesten NoSQL-Datenbankoptionen. Und es hat eine großartige Gemeinschaft und ein großartiges Ökosystem.

In diesem Artikel werden einige der Best Practices beschrieben, die beim Einrichten von MongoDB und Mongoose mit Node.js zu befolgen sind.

Voraussetzungen für diesen Artikel

Dieser Artikel ist Teil des Backend-Lernpfads von codedamn, in dem wir von den Backend-Grundlagen ausgehen und diese ausführlich behandeln. Daher gehe ich davon aus, dass Sie bereits Erfahrung mit JavaScript (und Node.js) haben.

Derzeit sind wir hier:

Wenn Sie nur sehr wenig Erfahrung mit Node.js / JavaScript oder dem Backend im Allgemeinen haben, ist dies wahrscheinlich ein guter Anfang. Hier finden Sie auch einen kostenlosen Kurs zu Mongoose + MongoDB + Node.js. Lass uns eintauchen.

Warum brauchst du Mungo?

Um zu verstehen, warum wir Mongoose benötigen, wollen wir verstehen, wie MongoDB (und eine Datenbank) auf Architekturebene funktionieren.

  • Sie haben einen Datenbankserver (z. B. MongoDB-Community-Server)
  • Sie haben ein Node.js-Skript ausgeführt (als Prozess)

Der MongoDB-Server überwacht (normalerweise) einen TCP-Socket, und Ihr Node.js-Prozess kann über eine TCP-Verbindung eine Verbindung zu ihm herstellen.

Zusätzlich zu TCP verfügt MongoDB über ein eigenes Protokoll, um zu verstehen, was genau der Client (unser Node.js-Prozess) von der Datenbank erwartet.

Anstatt die Nachrichten zu lernen, die wir auf der TCP-Ebene senden müssen, abstrahieren wir diese für diese Kommunikation mithilfe einer "Treiber" -Software, in diesem Fall MongoDB-Treiber. Der MongoDB-Treiber ist hier als npm-Paket verfügbar.

Denken Sie jetzt daran, dass der MongoDB-Treiber für das Verbinden und Abstrahieren der Kommunikationsanfragen / -antworten auf niedriger Ebene von Ihnen verantwortlich ist - dies bringt Sie jedoch nur so weit wie ein Entwickler.

Da MongoDB eine schemenlose Datenbank ist, erhalten Sie viel mehr Leistung, als Sie als Anfänger benötigen. Mehr Leistung bedeutet mehr Oberfläche, um Fehler zu machen. Sie müssen die Oberfläche der Fehler und Fehler reduzieren, die Sie in Ihrem Code machen können. Du brauchst etwas mehr.

Treffen Sie Mungo. Mongoose ist eine Abstraktion über den nativen MongoDB-Treiber (das oben erwähnte npm-Paket).

Die allgemeine Faustregel bei Abstraktionen (so wie ich es verstehe) lautet, dass Sie mit jeder Abstraktion eine geringe Betriebsleistung verlieren. Das heißt aber nicht unbedingt, dass es schlecht ist. Manchmal steigert es die Produktivität um das 1000-fache, da Sie ohnehin nie wirklich vollen Zugriff auf die zugrunde liegende API haben müssen.

Eine gute Möglichkeit, darüber nachzudenken, besteht darin, eine Echtzeit-Chat-App sowohl in C als auch in Python zu erstellen.

Das Python-Beispiel wäre für Sie als Entwickler viel einfacher und schneller mit höherer Produktivität zu implementieren.

C mag effizienter sein, aber es wird enorme Kosten für Produktivität / Entwicklungsgeschwindigkeit / Fehler / Abstürze verursachen. Außerdem benötigen Sie zum größten Teil nicht die Leistung, die C Ihnen zur Implementierung von Websockets bietet.

In ähnlicher Weise können Sie mit Mongoose die Oberfläche des API-Zugriffs auf niedrigerer Ebene begrenzen, aber viele potenzielle Gewinne und einen guten DX freischalten.

So verbinden Sie Mongoose + MongoDB

Lassen Sie uns zunächst kurz sehen, wie Sie 2020 mit Mongoose eine Verbindung zu Ihrer MongoDB-Datenbank herstellen sollten:

mongoose.connect(DB_CONNECTION_STRING, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, useFindAndModify: false })

Dieses Verbindungsformat stellt sicher, dass Sie den neuen URL-Parser von Mongoose verwenden und keine veralteten Praktiken verwenden. Wenn Sie möchten, können Sie hier ausführlich über all diese Verfallsmeldungen lesen.

Wie man Mungo-Operationen durchführt

Lassen Sie uns nun schnell die Operationen mit Mongoose besprechen und wie Sie sie ausführen sollten.

Mungo bietet Ihnen Optionen für zwei Dinge:

  1. Cursor-basierte Abfrage
  2. Vollständige Abrufabfrage

Cursor-basierte Abfrage

Cursor-basiertes Abfragen bedeutet, dass Sie jeweils mit einem einzelnen Datensatz arbeiten, während Sie jeweils einen einzelnen oder einen Stapel von Dokumenten aus der Datenbank abrufen. Dies ist eine effiziente Methode, um mit großen Datenmengen in einer Umgebung mit begrenztem Speicher zu arbeiten.

Stellen Sie sich vor, Sie müssen Dokumente mit einer Gesamtgröße von 10 GB auf einem Cloud-Server mit 1 GB / 1 Kern analysieren. Sie können nicht die gesamte Sammlung abrufen, da dies nicht auf Ihr System passt. Cursor ist hier eine gute (und die einzige?) Option.

Vollständige Abfrage

Dies ist die Art der Abfrage, bei der Sie die vollständige Antwort Ihrer Abfrage auf einmal erhalten. Zum größten Teil ist dies das, was Sie verwenden werden. Daher konzentrieren wir uns hier hauptsächlich auf diese Methode.

Verwendung von Mungomodellen

Modelle sind die Supermacht des Mungos. Sie helfen Ihnen bei der Durchsetzung von "Schema" -Regeln und ermöglichen eine nahtlose Integration Ihres Knotencodes in Datenbankaufrufe.

Der allererste Schritt besteht darin, ein gutes Modell zu definieren:

import mongoose from 'mongoose' const CompletedSchema = new mongoose.Schema( { type: { type: String, enum: ['course', 'classroom'], required: true }, parentslug: { type: String, required: true }, slug: { type: String, required: true }, userid: { type: String, required: true } }, { collection: 'completed' } ) CompletedSchema.index({ slug: 1, userid: 1 }, { unique: true }) const model = mongoose.model('Completed', CompletedSchema) export default model 

Dies ist ein reduziertes Beispiel direkt aus der Codebasis von codedamn. Einige interessante Dinge, die Sie hier beachten sollten:

  1. Versuchen Sie, required: truealle erforderlichen Felder beizubehalten. Dies kann eine enorme Schmerzersparnis für Sie sein, wenn Sie kein statisches Typprüfungssystem wie TypeScript verwenden, um Sie beim Erstellen eines Objekts mit den richtigen Eigenschaftsnamen zu unterstützen. Außerdem ist die kostenlose Validierung auch super cool.
  2. Definieren Sie Indizes und eindeutige Felder. uniqueEigenschaft kann auch innerhalb eines Schemas hinzugefügt werden. Indizes sind ein breites Thema, daher werde ich hier nicht näher darauf eingehen. Aber im großen Stil können sie Ihnen wirklich helfen, Ihre Anfragen erheblich zu beschleunigen.
  3. Definieren Sie einen Sammlungsnamen explizit. Obwohl Mongoose automatisch einen Sammlungsnamen basierend auf dem Namen des Modells vergeben kann ( Completedhier zum Beispiel), ist dies meiner Meinung nach viel zu viel Abstraktion. Sie sollten zumindest Ihre Datenbanknamen und Sammlungen in Ihrer Codebasis kennen.
  4. Beschränken Sie Werte, wenn Sie können, mithilfe von Aufzählungen.

Ausführen von CRUD-Operationen

CRUD bedeutet C Reate, R ead, U pdate und D elete. Dies sind die vier grundlegenden Optionen, mit denen Sie jede Art von Datenmanipulation in einer Datenbank durchführen können. Lassen Sie uns schnell einige Beispiele für diese Operationen sehen.

Die Erstellungsoperation

Dies bedeutet einfach, einen neuen Datensatz in einer Datenbank zu erstellen. Verwenden wir das oben definierte Modell, um einen Datensatz zu erstellen:

try { const res = await CompletedSchema.create(record) } catch(error) { console.error(error) // handle the error }

Nochmals ein paar Hinweise hier:

  1. Verwenden Sie async-await anstelle von Rückrufen (schön für die Augen, kein bahnbrechender Leistungsvorteil als solcher)
  2. Verwenden Sie Try-Catch-Blöcke für Abfragen, da Ihre Abfrage aus einer Reihe von Gründen fehlschlagen kann (doppelter Datensatz, falscher Wert usw.).

Der Lesevorgang

Dies bedeutet, dass vorhandene Werte aus der Datenbank gelesen werden. Es ist einfach, so wie es sich anhört, aber es gibt ein paar Fallstricke, die Sie mit Mongoose kennen sollten:

const res = await CompletedSchema.find(info).lean()
  1. Können Sie dort den lean()Funktionsaufruf sehen? Es ist sehr nützlich für die Leistung. Standardmäßig verarbeitet Mongoose die zurückgegebenen Dokumente aus der Datenbank und fügt ihre magischen Methoden hinzu (zum Beispiel .save).
  2. Wenn Sie verwenden .lean(), gibt Mongoose einfache JSON-Objekte anstelle von speicher- und ressourcenintensiven Dokumenten zurück. Macht Abfragen auch auf Ihrer CPU schneller und kostengünstiger.
  3. Sie können jedoch weglassen, .lean()wenn Sie tatsächlich daran denken, Daten zu aktualisieren (wir werden das als nächstes sehen).

Der Update-Vorgang

Wenn Sie bereits ein Mongoose-Dokument bei sich haben (ohne mit zu feuern .lean()), können Sie einfach die Objekteigenschaft ändern und sie speichern mit object.save():

const doc = await CompletedSchema.findOne(info) doc.slug = 'something-else' await doc.save()

Denken Sie daran, dass hier zwei Datenbankaufrufe getätigt werden. Der erste ist eingeschaltet findOneund der zweite ist eingeschaltet doc.save.

Wenn Sie können, sollten Sie immer die Anzahl der Anforderungen reduzieren, die an die Datenbank gesendet werden (denn wenn Sie Speicher, Netzwerk und Festplatte vergleichen, ist das Netzwerk fast immer am langsamsten).

Im anderen Fall können Sie eine Abfrage wie die folgende verwenden:

const res = await CompletedSchema.updateOne(, ).lean()

and it will only make a single call to the database.

The Delete Operation

Delete is also straightforward with Mongoose. Let's see how you can delete a single document:

const res = await CompletedSchema.deleteOne()

Just like updateOne, deleteOne also accepts the first argument as the matching condition for the document.

There is also another method called deleteMany which should be used only when you know you want to delete multiple documents.

In any other case, always use deleteOne to avoid accidental multiple deletes, especially when you're trying to execute queries yourself.

Conclusion

This article was a simple introduction to the Mongoose and MongoDB world for Node.js developers.

If you enjoyed this article, you can step up your game even more as a developer by following the codedamn backend learning path. Please feel free to reach out to me on Twitter for any feedback!