Cómo usar MongoDB + Mongoose con Node.js - Mejores prácticas para desarrolladores de back-end

MongoDB es sin duda una de las opciones de bases de datos NoSQL más populares en la actualidad. Y tiene una gran comunidad y ecosistema.

En este artículo, revisaremos algunas de las mejores prácticas a seguir al configurar MongoDB y Mongoose con Node.js.

Requisitos previos para este artículo

Este artículo es una parte de la ruta de aprendizaje de backend de codedamn, donde partimos de los conceptos básicos del backend y los cubrimos en detalle. Por lo tanto, supongo que ya tiene algo de experiencia con JavaScript (y Node.js).

Actualmente estamos aquí:

Si tiene muy poca experiencia con Node.js / JavaScript o el back-end en general, probablemente este sea un buen lugar para comenzar. También puede encontrar un curso gratuito sobre Mongoose + MongoDB + Node.js aquí. Vamos a sumergirnos.

¿Por qué necesitas Mongoose?

Para entender por qué necesitamos Mongoose, entendamos cómo funciona MongoDB (y una base de datos) a nivel de arquitectura.

  • Tiene un servidor de base de datos (servidor de la comunidad MongoDB, por ejemplo)
  • Tiene un script de Node.js ejecutándose (como un proceso)

El servidor MongoDB escucha en un socket TCP (generalmente), y su proceso Node.js puede conectarse a él mediante una conexión TCP.

Pero además de TCP, MongoDB también tiene su propio protocolo para comprender qué es exactamente lo que el cliente (nuestro proceso Node.js) quiere que haga la base de datos.

Para esta comunicación, en lugar de aprender los mensajes que tenemos que enviar en la capa TCP, lo abstraemos con la ayuda de un software "controlador", llamado controlador MongoDB en este caso. El controlador MongoDB está disponible como un paquete npm aquí.

Ahora recuerde, el controlador MongoDB es responsable de conectar y abstraer las solicitudes / respuestas de comunicación de bajo nivel de usted, pero esto solo lo lleva tan lejos como desarrollador.

Debido a que MongoDB es una base de datos sin esquema, le brinda mucha más potencia de la que necesita como principiante. Más potencia significa más superficie para hacer las cosas mal. Necesita reducir su área de superficie de errores y errores que puede hacer en su código. Necesitas algo más.

Conoce a Mongoose. Mongoose es una abstracción sobre el controlador nativo de MongoDB (el paquete npm que mencioné anteriormente).

La regla general con las abstracciones (según tengo entendido) es que con cada abstracción se pierde algo de poder de operación de bajo nivel. Pero eso no significa necesariamente que sea malo. A veces, aumenta la productividad 1000x + porque de todos modos nunca necesita tener acceso completo a la API subyacente.

Una buena forma de pensarlo es crear técnicamente una aplicación de chat en tiempo real tanto en C como en Python.

El ejemplo de Python sería mucho más fácil y rápido para usted como desarrollador de implementar con mayor productividad.

C podría ser más eficiente, pero tendrá un costo enorme en productividad / velocidad de desarrollo / errores / fallas. Además, en su mayor parte, no necesita tener la potencia que le brinda C para implementar websockets.

De manera similar, con Mongoose, puede limitar su área de superficie de acceso a API de nivel inferior, pero desbloquear muchas ganancias potenciales y buen DX.

Cómo conectar Mongoose + MongoDB

En primer lugar, veamos rápidamente cómo debe conectarse a su base de datos MongoDB en 2020 con Mongoose:

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

Este formato de conexión garantiza que esté utilizando el nuevo analizador de URL de Mongoose y que no esté utilizando ninguna práctica obsoleta. Puede leer en profundidad sobre todos estos mensajes de desaprobación aquí si lo desea.

Cómo realizar operaciones de Mongoose

Ahora sigamos adelante y analicemos rápidamente las operaciones con Mongoose y cómo debe realizarlas.

Mongoose te ofrece opciones para dos cosas:

  1. Consultas basadas en cursor
  2. Consulta de búsqueda completa

Consultas basadas en cursor

Las consultas basadas en cursores significan que trabaja con un solo registro a la vez mientras busca uno o un lote de documentos a la vez de la base de datos. Esta es una forma eficiente de trabajar con grandes cantidades de datos en un entorno de memoria limitada.

Imagine que tiene que analizar documentos de 10 GB de tamaño total en un servidor en la nube de 1 GB / 1 núcleo. No puede recuperar toda la colección porque no cabe en su sistema. El cursor es una buena (¿y la única?) Opción aquí.

Consulta de búsqueda completa

Este es el tipo de consulta en la que obtiene la respuesta completa de su consulta de una vez. En su mayor parte, esto es lo que usará. Por lo tanto, aquí nos centraremos principalmente en este método.

Cómo utilizar los modelos Mongoose

Los modelos son la superpotencia de Mongoose. Le ayudan a hacer cumplir las reglas de "esquema" y proporcionan una integración perfecta de su código de nodo en las llamadas a la base de datos.

El primer paso es definir un buen modelo:

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 

Este es un ejemplo reducido directamente del código base de codedamn. Algunas cosas interesantes que debe tener en cuenta aquí:

  1. Trate de mantener required: truetodos los campos obligatorios. Esto puede ser un gran ahorro para usted si no usa un sistema de verificación de tipo estático como TypeScript para ayudarlo con los nombres correctos de las propiedades mientras crea un objeto. Además, la validación gratuita también es genial.
  2. Defina índices y campos únicos. uniqueLa propiedad también se puede agregar dentro de un esquema. Los índices son un tema amplio, por lo que no entraré en profundidad aquí. Pero a gran escala, realmente pueden ayudarlo a acelerar mucho sus consultas.
  3. Defina un nombre de colección explícitamente. Aunque Mongoose puede dar automáticamente un nombre de colección basado en el nombre del modelo ( Completedaquí, por ejemplo), en mi opinión, esto es demasiada abstracción. Debería al menos conocer los nombres y colecciones de su base de datos en su base de código.
  4. Restrinja los valores si puede, utilizando enumeraciones.

Cómo realizar operaciones CRUD

CRUD significa C reate, R ead, U pdate y D elete. Estas son las cuatro opciones fundamentales con las que se puede realizar cualquier tipo de manipulación de datos en una base de datos. Veamos rápidamente algunos ejemplos de estas operaciones.

La operación Create

Esto simplemente significa crear un nuevo registro en una base de datos. Usemos el modelo que definimos anteriormente para crear un registro:

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

Una vez más, algunos consejos aquí:

  1. Use async-await en lugar de devoluciones de llamada (agradable a la vista, sin un beneficio de rendimiento innovador como tal)
  2. Use bloques try-catch alrededor de las consultas porque su consulta puede fallar por varias razones (registro duplicado, valor incorrecto, etc.)

La operación de lectura

Esto significa leer los valores existentes de la base de datos. es simple como suena, pero hay un par de trampas que debes conocer con Mongoose:

const res = await CompletedSchema.find(info).lean()
  1. ¿Puedes ver la lean()llamada a la función allí? Es muy útil para el rendimiento. De forma predeterminada, Mongoose procesa los documentos devueltos de la base de datos y agrega sus métodos mágicos (por ejemplo .save)
  2. When you use .lean(), Mongoose returns plain JSON objects instead of memory and resource heavy documents. Makes queries faster and less expensive on your CPU, too.
  3. However, you can omit .lean() if you are actually thinking of updating data (we'll see that next)

The Update Operation

If you already have a Mongoose document with you (without firing with .lean()), you can simply go ahead and modify the object property, and save it using object.save():

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

Remember that here, there are two database calls made. The first one is on findOne and the second one is on doc.save.

If you can, you should always reduce the number of requests hitting the database (because if you're comparing memory, network, and disk, network is almost always the slowest).

In the other case, you can use a query like this:

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!