Sådan bruges MongoDB + Mongoose med Node.js - bedste fremgangsmåder til backend-devs

MongoDB er utvivlsomt et af de mest populære NoSQL-databasevalg i dag. Og det har et godt samfund og økosystem.

I denne artikel gennemgår vi nogle af de bedste fremgangsmåder, der skal følges, når du konfigurerer MongoDB og Mongoose med Node.js.

Forudsætninger for denne artikel

Denne artikel er en af ​​de delkodedeAMNs backend læringsveje, hvor vi starter fra backend basics og dækker dem i detaljer. Derfor antager jeg, at du allerede har erfaring med JavaScript (og Node.js).

I øjeblikket er vi her:

Hvis du har meget lidt erfaring med Node.js / JavaScript eller backend generelt, er dette sandsynligvis et godt sted at starte. Du kan også finde et gratis kursus på Mongoose + MongoDB + Node.js her. Lad os dykke ind.

Hvorfor har du brug for Mongoose?

For at forstå hvorfor vi har brug for Mongoose, lad os forstå, hvordan MongoDB (og en database) fungerer på arkitekturniveau.

  • Du har en databaseserver (f.eks. MongoDB-community-server)
  • Du har et Node.js-script, der kører (som en proces)

MongoDB-server lytter på et TCP-stik (normalt), og din Node.js-proces kan oprette forbindelse til den ved hjælp af en TCP-forbindelse.

Men på toppen af ​​TCP har MongoDB også sin egen protokol til at forstå, hvad klienten (vores Node.js-proces) ønsker, at databasen skal gøre.

I stedet for at lære de meddelelser, vi skal sende på TCP-laget, uddrager vi det til denne kommunikation ved hjælp af en "driver" -software, kaldet MongoDB-driver i dette tilfælde. MongoDB-driveren er tilgængelig som en npm-pakke her.

Husk nu, at MongoDB-driveren er ansvarlig for at oprette forbindelse og abstrahere kommunikationsanmodningen / svarene fra lavt niveau fra dig - men dette får dig kun så langt som udvikler.

Fordi MongoDB er en skemaløs database, giver den dig meget mere magt, end du har brug for som begynder. Mere magt betyder mere overfladeareal for at få tingene forkert. Du skal reducere dit overfladeareal af fejl og skruer, du kan lave i din kode. Du har brug for noget mere.

Mød Mongoose. Mongoose er en abstraktion over den oprindelige MongoDB-driver (den npm-pakke, jeg nævnte ovenfor).

Den generelle tommelfingerregel med abstraktioner (sådan som jeg forstår) er, at du ved enhver abstraktion mister noget lavt niveau operationskraft. Men det betyder ikke nødvendigvis, at det er dårligt. Nogle gange øger det produktiviteten 1000x +, fordi du aldrig rigtig behøver at have fuld adgang til den underliggende API alligevel.

En god måde at tænke på det er, at du teknisk opretter en chat-app i realtid både i C og i Python.

Python-eksemplet ville være meget lettere og hurtigere for dig som udvikler at implementere med højere produktivitet.

C kan være mere effektiv, men det koster enorme omkostninger i produktivitet / udviklingshastighed / bugs / crash. Plus, for det meste behøver du ikke at have magten C giver dig mulighed for at implementere websockets.

Tilsvarende kan du med Mongoose begrænse dit overfladeareal med lavere niveau API-adgang, men låse op for en masse potentielle gevinster og god DX.

Sådan tilsluttes Mongoose + MongoDB

Lad os først se, hvordan du skal oprette forbindelse til din MongoDB-database i 2020 med Mongoose:

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

Dette forbindelsesformat sørger for, at du bruger den nye URL-parser fra Mongoose, og at du ikke bruger nogen forældet praksis. Du kan læse i dybden om alle disse afskedigelsesmeddelelser her, hvis du vil.

Sådan udføres Mongoose-operationer

Lad os nu gå videre og hurtigt diskutere operationer med Mongoose, og hvordan du skal udføre dem.

Mongoose giver dig muligheder for to ting:

  1. Markørbaseret forespørgsel
  2. Fuld hentning af forespørgsel

Markørbaseret forespørgsel

Markørbaseret forespørgsel betyder, at du arbejder med en enkelt post ad gangen, mens du henter en enkelt eller et batch af dokumenter ad gangen fra databasen. Dette er en effektiv måde at arbejde med enorme mængder data på i et begrænset hukommelsesmiljø.

Forestil dig, at du skal analysere dokumenter på 10 GB i total størrelse på en 1GB / 1core cloud-server. Du kan ikke hente hele samlingen, fordi den ikke passer på dit system. Markør er en god (og den eneste?) Mulighed her.

Fuld hentning af forespørgsel

Dette er den type forespørgsel, hvor du får det fulde svar på din forespørgsel på én gang. For det meste er dette, hvad du bruger. Derfor fokuserer vi mest på denne metode her.

Sådan bruges Mongoose Modeller

Modeller er Mongoose superkraft. De hjælper dig med at håndhæve "skema" -regler og giver en problemfri integration af din Node-kode i databaseopkald.

Det allerførste trin er at definere en god model:

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 

Dette er et trimmet eksempel direkte fra codedamns codebase. Et par interessante ting, du skal bemærke her:

  1. Prøv at holde required: truepå alle felter, der kræves. Dette kan være en enorm smertebesparende for dig, hvis du ikke bruger et system til kontrol af statisk type som TypeScript til at hjælpe dig med korrekte ejendomsnavne, mens du opretter et objekt. Plus den gratis validering er også super cool.
  2. Definer indekser og unikke felter. uniqueegenskab kan også tilføjes inden for et skema. Indekser er et bredt emne, så jeg vil ikke gå i dybden her. Men i stor skala kan de virkelig hjælpe dig med at fremskynde dine forespørgsler meget.
  3. Definer et samlingsnavn eksplicit. Selvom Mongoose automatisk kan give et samlingsnavn baseret på modelnavnet ( Completedfor eksempel her), er dette efter min mening alt for meget abstraktion. Du skal i det mindste vide om dine databasenavne og samlinger i din codebase.
  4. Begræns værdier, hvis du kan, ved hjælp af enums.

Sådan udføres CRUD-operationer

CRUD betyder C reate, R ead, U pdatér og D DELETE. Dette er de fire grundlæggende muligheder, som du kan udføre enhver form for databehandling i en database. Lad os hurtigt se nogle eksempler på disse operationer.

Opret operationen

Dette betyder simpelthen at oprette en ny post i en database. Lad os bruge den model, vi definerede ovenfor, til at oprette en post:

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

Igen, nogle få tip her:

  1. Brug asynk-afvent i stedet for tilbagekald (pæn i øjnene, ingen banebrydende ydelsesfordel som sådan)
  2. Brug prøvefangsblokke omkring forespørgsler, fordi din forespørgsel kan mislykkes af en række årsager (duplikatoptegnelse, forkert værdi osv.)

Læs-operationen

Dette betyder at læse eksisterende værdier fra databasen. det er simpelt ligesom det lyder, men der er et par gotchas, du bør kende med Mongoose:

const res = await CompletedSchema.find(info).lean()
  1. Kan du se lean()funktionen kalde der? Det er super nyttigt til ydeevne. Som standard behandler Mongoose det eller de returnerede dokumenter fra databasen og tilføjer sine magiske metoder på det (for eksempel .save)
  2. Når du bruger .lean(), returnerer Mongoose almindelige JSON-objekter i stedet for hukommelse og ressourcetunge dokumenter. Gør forespørgsler hurtigere og billigere på din CPU også.
  3. Du kan dog udelade, .lean()hvis du rent faktisk overvejer at opdatere data (vi ser det næste)

Opdateringsfunktionen

Hvis du allerede har et Mongoose-dokument med dig (uden at skyde med .lean()), kan du blot gå videre og ændre objektegenskaben og gemme det ved hjælp af object.save():

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

Husk, at der her er foretaget to databaseopkald. Den første er tændt, findOneog den anden er tændt doc.save.

Hvis du kan, skal du altid reducere antallet af anmodninger, der rammer databasen (for hvis du sammenligner hukommelse, netværk og disk, er netværk næsten altid det langsomste).

I det andet tilfælde kan du bruge en forespørgsel som denne:

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.

Hvis du kunne lide denne artikel, kan du intensivere dit spil endnu mere som udvikler ved at følge codedamn backend-læringsstien. Du er velkommen til at kontakte mig på Twitter for enhver feedback!