Cómo instalar MongoDB con su aplicación de Node

Introducción

Al trabajar con Node.js, es posible que se encuentre desarrollando un proyecto que almacene y consulte datos. En este caso, deberá elegir una solución de base de datos que tenga sentido para los datos y los tipos de consulta de su aplicación.

A través de este tutorial, integrará una base de datos de MongoDB con una aplicación de Node existente. Las bases de datos de NoSQL como MongoDB pueden ser útiles si entre los requisitos de sus datos se incluyen la escalabilidad y la flexibilidad. MongoDB también se integra bien con Node, ya que está diseñado para funcionar de forma asíncrona con objetos JSON.

Para integrar MongoDB en su proyecto utilizará Mongoose, el asignador de documento objeto (ODM), a fin de crear esquemas y modelos para los datos de su aplicación. Esto le permitirá organizar el código de su aplicación siguiendo el patrón de arquitectura de modelo-vista-controlador (MVC), lo cual le permitirá separar la lógica de cómo su aplicación gestiona la entradas del usuario de la forma en que sus datos se estructuran y se muestran al usuario. El uso de este patrón puede facilitar las pruebas y el desarrollo futuro introduciendo una separación de problemas en su base de código.

Al completar el tutorial, contará con una aplicación de información sobre tiburones que funcionará y recopilará entradas del usuario sobre sus tiburones favoritos y mostrará los resultados en el navegador:

Resultados de tiburones

Requisitos previos

  • Una máquina para desarrollo local que funcione con Ubuntu 18.04, junto con un usuario no root con privilegios sudo y un firewall activo. Para obtener orientación sobre cómo configurarlos en un servidor de Ubuntu 18.04 consulte esta guía de configuración inicial para servidores.
  • Node.js y npm instalados en su equipo o servidor, siguiendo estas instrucciones para realizar la instalación con el PPA administrado por NodeSource.
  • MongoDB instalado en su equipo o servidor siguiendo el paso 1 de Cómo instalar MongoDB en Ubuntu 18.04.

Paso 1: Crear un usuario de Mongo

Antes de comenzar a trabajar con el código de la aplicación, crearemos un usuario administrativo que tendrá acceso a la base de datos de nuestra aplicación. Este usuario tendrá privilegios administrativos en cualquier base de datos, lo que le brindará la flexibilidad para cambiar y crear nuevas bases de datos según sea necesario.

Primero, compruebe que MongoDB esté en ejecución en su servidor:

  • sudo systemctl status mongodb

El siguiente resultado indica que MongoDB se encuentra en ejecución:

Output● mongodb.service - An object/document-oriented database    Loaded: loaded (/lib/systemd/system/mongodb.service; enabled; vendor preset: enabled)    Active: active (running) since Thu 2019-01-31 21:07:25 UTC; 21min ago ... 

A continuación, abra el shell de Mongo para crear su usuario:

  • mongo

Esto lo situará en un shell administrativo:

OutputMongoDB shell version v3.6.3 connecting to: mongodb://127.0.0.1:27017 MongoDB server version: 3.6.3 ... > 

Verá algunas advertencias administrativas cuando abra el shell debido a su acceso sin restricciones a la base de datos de admin. Puede obtener más información sobre la restricción de este acceso leyendo Cómo instalar y proteger MongoDB en Ubuntu 16.04, para cuando realice una transición a una configuración de producción.

Por ahora, puede usar su acceso a la base de datos de admin para crear un usuario con privilegios de userAdminAnyDatabase, lo cual permitirá el acceso protegido por contraseña a las bases de datos de su aplicación.

En el shell, especifique que desea usar la base de datos de admin para crear su usuario:

  • use admin

A continuación, cree un rol y una contraseña agregando un nombre de usuario y una contraseña con el comando db.createUser. Una vez que escriba este comando, el shell antepondrá tres puntos antes de cada línea hasta que el comando se complete. Asegúrese de sustituir el usuario y la contraseña que se proporcionan aquí por su propio nombre de usuario y su contraseña:

  • db.createUser(
  • {
  • user: "sammy",
  • pwd: "your_password",
  • roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
  • }
  • )

Con esto se crea una entrada para el usuario sammy en la base de datos de admin. El nombre de usuario que seleccione y la base de datos de admin servirán como identificadores para su usuario.

El resultado de todo el proceso tendrá el aspecto que se muestra a continuación. Se incluye el mensaje que indica que la entrada se realizó de manera correcta:

Output> db.createUser( ...  { ...    user: "sammy", ...    pwd: "your_password", ...    roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] ...  } ...) Successfully added user: {         "user" : "sammy",         "roles" : [                 {                         "role" : "userAdminAnyDatabase",                         "db" : "admin"                 }         ] } 

Una vez creados su usuario y contraseña, podrá cerrar el shell de Mongo:

  • exit

Ahora que creó su usuario de base de datos, puede proceder con la clonación del código del proyecto de inicio y añadir la biblioteca de Mongoose, que le permitirá implementar esquemas y modelos para las colecciones de sus bases de datos.

Paso 2: Agregar información sobre Mongoose y bases de datos al proyecto

Nuestros próximos pasos serán clonar el código de inicio de la aplicación y añadir información sobre Mongoose y nuestra base de datos de MongoDB al proyecto.

En el directorio principal de su usuario no root, clone el repositorio nodejs-image-demo de la cuenta de GitHub de la comunidad de DigitalOcean. Este repositorio incluye el código de la configuración descrita en Cómo crear una aplicación de Node.js con Docker.

Clone el repositorio en un directorio llamado node_project:

  • git clone https://github.com/do-community/nodejs-image-demo.git node_project

Diríjase al directorio <^>node_project<^>:

  • cd node_project

Antes de modificar el código del proyecto, observaremos la estructura este último usando el comando tree.

Sugerencia: tree es un comando útil que permite visualizar estructuras de archivos y directorios desde la línea de comandos. Puede instalarlo con el siguiente comando:

  • sudo apt install tree

Para emplearlo, use el comando cd para pasar a un directorio determinado y escriba tree. También puede proporcionar la ruta al punto de partida con un comando como el siguiente:

  • tree /home/sammy/sammys-project

Escriba lo siguiente para ver el directorio node_project:

  • tree

La estructura del proyecto actual tiene el siguiente aspecto:

Output├── Dockerfile ├── README.md ├── app.js ├── package-lock.json ├── package.json └── views     ├── css     │   └── styles.css     ├── index.html     └── sharks.html 

Añadiremos directorios a este proyecto a medida que avancemos en el tutorial,y el comando tree será útil para ayudarnos a monitorear nuestro progreso.

A continuación, añada el paquete de npm de moongoose al proyecto con el comando npm install:

  • npm install mongoose

Con este comando se creará un directorio node_modules en el directorio de su proyecto, se usarán las dependencias enumeradas en el archivo package.json del proyecto y se agregará mongoose a ese directorio. También se agregará mongoose a las dependencias enumeradas en su archivo package.json. Para hallar un análisis más detallado sobre package.json, consulte el paso 1 de Cómo crear una aplicación de Node.js con Docker.

Antes de crear cualquier esquema o modelo de Mongoose, añadiremos la información de conexión de nuestra base de datos para que nuestra aplicación pueda conectarse a nuestra base de datos.

A fin de separar al máximo los aspectos de su aplicación que sean motivo de inquietud, cree un archivo por separado para la información de conexión de su base de datos llamada db.js. Puede abrir este archivo con nano o con su editor favorito:

  • nano db.js

Primero, importe el módulo de mongoose usando la función require:

~/node_project/db.js

const mongoose = require('mongoose'); 

Esto le brindará acceso a los métodos incorporados de Mongoose que usará para crear la conexión a su base de datos.

A continuación, agregue las siguientes constantes a fin de definir información para la conexión de URI de Mongo. Aunque el nombre de usuario y la contraseña son opcionales, los incluiremos para poder exigir la autenticación para nuestra base de datos. Asegúrese de sustituir el nombre de usuario y la contraseña que se muestran a continuación por su propia información, y podrá dar a la base de datos un nombre que no sea 'sharkinfo' si así lo prefiere:

~/node_project/db.js

const mongoose = require('mongoose');  const MONGO_USERNAME = 'sammy'; const MONGO_PASSWORD = 'your_password'; const MONGO_HOSTNAME = '127.0.0.1'; const MONGO_PORT = '27017'; const MONGO_DB = 'sharkinfo'; 

Debido a que ejecutamos nuestra base de datos a nivel local, usamos 127.0.0.1 como nombre de host. Esto cambiará en otros contextos de desarrollo: por ejemplo, si utiliza un servidor de bases de datos independiente o si trabaja con varios nodos en un flujo de trabajo en contenedores.

Por último, defina una constante para la URI y cree la conexión usando el método mongoose.connect():

~/node_project/db.js

... const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;  mongoose.connect(url, {useNewUrlParser: true}); 

Tenga en cuenta que en la URI especificamos el authSource de nuestro usuario como base de datos de admin. Esto es necesario, ya que especificamos un nombre de usuario en nuestra cadena de conexión. El uso del indicador useNewUrlParser con mongoose.connect() especifica que deseamos usar el nuevo analizador de URL de Mongo.

Guarde y cierre el archivo cuando concluya la edición.

Como paso final, añada la información de conexión de la base de datos al archivo app.js para que la aplicación pueda utilizarla. Abra app.js:

  • nano app.js

Las primeras líneas del archivo tendrán este aspecto:

~/node_project/app.js

const express = require('express'); const app = express(); const router = express.Router();  const path = __dirname + '/views/'; ... 

Debajo de la definición de la constante router, situada cerca de la parte superior del archivo, agregue la siguiente línea:

~/node_project/app.js

... const router = express.Router(); const db = require('./db');  const path = __dirname + '/views/'; ... 

Esto indica a la aplicación que utilice la información de conexión de la base de datos especificada en db.js.

Guarde y cierre el archivo cuando concluya la edición.

Una vez que se implemente la información de su base de datos y se añada Mongoose a su proyecto, estará listo para crear los esquemas y los modelos que configurarán los datos de su colección sharks.

Paso 3: Crear esquemas y modelos de Mongoose

Nuestro siguiente paso será pensar en la estructura de la colección sharks que los usuarios crearán en la base de datos sharkinfo con sus datos. ¿Qué estructura queremos que tengan estos documentos creados? En la página de información sobre tiburones de nuestra aplicación actual se incluyen algunos detalles sobre diferentes tiburones y sus comportamientos:

Página de información sobre tiburones

En línea con este tema, podemos disponer que los usuarios añadan nuevos tiburones con detalles sobre su carácter general. Este objetivo dará forma a la manera en que creemos nuestro esquema.

Para mantener sus esquemas y modelos diferenciados de las demás partes de su aplicación, cree un directorio models en el directorio de proyectos actual:

  • mkdir models

A continuación, abra un archivo llamado sharks.js para crear su esquema y modelo:

  • nano models/sharks.js

Importe el módulo mongoose en la parte superior del archivo:

~/node_project/models/sharks.js

const mongoose = require('mongoose'); 

Debajo de esto, defina un objeto de Schema que se utilice como base para su esquema de tiburones:

~/node_project/models/sharks.js

const mongoose = require('mongoose'); const Schema = mongoose.Schema; 

Ahora podrá definir los campos que desee incluir en su esquema. Debido a que deseamos crear una colección con tiburones individuales e información sobre sus comportamientos, incluiremos una clave name y una clave character. Añada el siguiente esquema Shark debajo de sus definiciones de constantes:

~/node_project/models/sharks.js

... const Shark = new Schema ({         name: { type: String, required: true },         character: { type: String, required: true }, }); 

En esta definición se incluye información sobre el tipo de entrada que esperamos de los usuarios; en este caso, una secuencia de comandos y si es necesaria o no la entrada.

Por último, cree el modelo Shark usando la función model() de Mongoose. Este modelo le permitirá consultar documentos de su colección y validar nuevos documentos. Añada la siguiente línea en la parte inferior del archivo:

~/node_project/models/sharks.js

... module.exports = mongoose.model('Shark', Shark) 

Esta última línea hace que nuestro modelo Shark esté disponible como un módulo usando la propiedad module.exports. Esta propiedad define los valores que el módulo exportará y los pondrá a disposición para su uso en cualquier lugar de la aplicación.

El archivo completado de models/sharks.js tiene este aspecto:

~/node_project/models/sharks.js

const mongoose = require('mongoose'); const Schema = mongoose.Schema;  const Shark = new Schema ({         name: { type: String, required: true },         character: { type: String, required: true }, });  module.exports = mongoose.model('Shark', Shark) 

Guarde y cierre el archivo cuando concluya la edición.

Una vez implementados el esquema y el modelo de Shark, puede comenzar a trabajar en la lógica que determinará la forma en que su aplicación manejará las entradas del usuario.

Paso 4: Crear los controladores

Nuestro siguiente paso será crear el componente del controlador que determinará la manera en que las entradas del usuario se guarden en nuestra base de datos y se devuelvan a este.

Primero, cree un directorio para el controlador:

  • mkdir controllers

A continuación, abra un archivo llamado sharks.js en esa carpeta:

  • nano controllers/sharks.js

En la parte superior del archivo, importaremos el módulo con nuestro modelo Shark para poder utilizarlo en la lógica de nuestro controlador. También importaremos el módulo path para acceder a las utilidades que nos permitirán establecer la ruta en el formulario en el que los usuarios ingresarán sus tiburones.

Añada las siguientes funciones require al inicio del archivo:

~/node_project/controllers/sharks.js

const path = require('path'); const Shark = require('../models/sharks'); 

A continuación, escribiremos una secuencia de funciones que exportaremos con el módulo de controlador usando el acceso rápido exports de Node. Estas funciones incluirán las tres tareas relacionadas con los datos de tiburones de nuestro usuario:

  • Enviar a los usuarios el formulario de entrada de tiburones
  • Crear una nueva entrada sobre tiburones
  • Mostrar los tiburones de vuelta a los usuarios

Para comenzar, cree una función index para mostrar la página de tiburones con el formulario de entrada. Añada esta función debajo de sus importaciones:

~/node_project/controllers/sharks.js

... exports.index = function (req, res) {     res.sendFile(path.resolve('views/sharks.html')); }; 

A continuación, debajo de la función index, agregue una función llamada create para crear una nueva entrada sobre tiburones en su colección sharks:

~/node_project/controllers/sharks.js

... exports.create = function (req, res) {     var newShark = new Shark(req.body);     console.log(req.body);     newShark.save(function (err) {             if(err) {             res.status(400).send('Unable to save shark to database');         } else {             res.redirect('/sharks/getshark');         }   });                }; 

Esta función se mostrará cuando un usuario publique datos de tiburones en el formulario en la página sharks.html. Crearemos la ruta con este extremo de POST más adelante en el tutorial cuando creemos las rutas de nuestra aplicación. Con el body de la solicitud de POST, nuestra función create creará un nuevo objeto de documentos de tiburones, aquí llamado newShark, usando el modelo Shark que importamos. Hemos añadido un método de console.log a fin de mostrar la entrada sobre el tiburón en la consola para comprobar que nuestro método POST funciona como está previsto, pero puede optar por omitir esto si así lo prefiere.

Mediante el objeto newShark, la función create llamará al método model.save() de Mongoose para crear un nuevo documento sobre tiburones usando las claves que definió en el modelo Shark. Esta función de devolución de llamada sigue el patrón estándar de devolución de llamada de Node: callback(error, results). Si se produce un error, enviaremos un mensaje que notificará el error a nuestros usuarios. Si la operación tiene éxito, utilizaremos el método res.redirect() para enviar a los usuarios al extremo que les devolverá su información sobre tiburones en el navegador.

Por último, la función de list mostrará el contenido de la colección de vuelta al usuario. Añada el siguiente código debajo de la función create:

~/node_project/controllers/sharks.js

... exports.list = function (req, res) {         Shark.find({}).exec(function (err, sharks) {                 if (err) {                         return res.send(500, err);                 }                 res.render('getshark', {                         sharks: sharks              });         }); }; 

Esta función utiliza el modelo Shark con el método model.find() de Mongoose para devolver los tiburones que se introdujeron en la colección sharks. Hace esto devolviendo el objeto de consulta; en este caso, todas las entradas de la colección sharks, como un compromiso, usando la función exec() de Mongoose. Si se produce un error, la función de devolución de llamada mostrará un error de 500.

El objeto de consulta devuelto con la colección sharks se mostrará en una página getshark que crearemos en el siguiente paso usando el lenguaje de plantillas EJS.

El archivo terminado tendrá este aspecto:

~/node_project/controllers/sharks.js

const path = require('path'); const Shark = require('../models/sharks');  exports.index = function (req, res) {     res.sendFile(path.resolve('views/sharks.html')); };  exports.create = function (req, res) {     var newShark = new Shark(req.body);     console.log(req.body);     newShark.save(function (err) {             if(err) {             res.status(400).send('Unable to save shark to database');         } else {             res.redirect('/sharks/getshark');         }   });                };  exports.list = function (req, res) {         Shark.find({}).exec(function (err, sharks) {                 if (err) {                         return res.send(500, err);                 }                 res.render('getshark', {                         sharks: sharks              });         }); }; 

Tenga en cuenta que, aunque no usamos funciones de flecha aquí, quizá desee incluirlas conforme realice repeticiones en este código en su propio proceso de desarrollo.

Guarde y cierre el archivo cuando concluya la edición.

Antes de continuar con el siguiente paso, puede ejecutar tree de nuevo desde su directorio node_project para ver la estructura del proyecto en este punto. Esta vez, para evitar extendernos, le indicaremos a tree que omita el directorio node_modules usando la opción -I:

  • tree -I node_modules

Con las adiciones que realizó, la estructura de su proyecto tendrá este aspecto:

Output├── Dockerfile ├── README.md ├── app.js ├── controllers │   └── sharks.js ├── db.js ├── models │   └── sharks.js ├── package-lock.json ├── package.json └── views     ├── css     │   └── styles.css     ├── index.html     └── sharks.html 

Ahora que tiene un componente de controlador que dirija la forma de guardar datos y devolverlos al usuario, puede proceder a crear las vistas que implementarán la lógica de su controlador.

Paso 5: Usar EJS y Express Middleware para recopilar y representar datos

Para habilitar nuestra aplicación para trabajar con datos de usuario, realizaremos dos cosas: primero, incluiremos una función de middleware Express integrada, urlencoded(), que permitirá que nuestra aplicación analice los datos introducidos por nuestro usuario. En segundo lugar, añadiremos etiquetas de plantillas a nuestras vistas para habilitar la interacción dinámica con los datos del usuario en nuestro código.

Para trabajar con la función urlencoded() de Express, primero abra su archivo app.js:

  • nano app.js

Encima de su función express.static() agregue la siguiente línea:

~/node_project/app.js

... app.use(express.urlencoded({ extended: true })); app.use(express.static(path)); ... 

La adición de esta función permitirá acceder a los datos analizados de POST de nuestro formulario de información sobre tiburones. Estamos especificando true con la opción extended para permitir una mayor flexibilidad en el tipo de datos que nuestra aplicación analizará (incluidos los elementos como los objetos anidados). Consulte la documentación de funciones para obtener más información sobre opciones.

Guarde y cierre el archivo cuando concluya la edición.

A continuación, añadiremos la funcionalidad de plantillas a nuestras vistas. Primero, instale el paquete ejs con npm install:

  • npm install ejs

A continuación, abra el archivo sharks.html en la carpeta de views:

  • nano views/sharks.html

En el paso 3, examinamos esta página para determinar cómo debemos escribir nuestro esquema y modelo de Mongoose:

Página de información de tiburones

Ahora, en lugar de tener un diseño de dos columnas, introduciremos una tercera columna con un formulario en el que los usuarios puedan agregar información sobre tiburones.

Como primer paso, cambie las dimensiones de las columnas existentes a 4 para crear tres columnas de igual tamaño. Tenga en cuenta que deberá realizar este cambio en las dos líneas que actualmente contienen el texto <div class="col-lg-6". Ambas se convertirán en <div class="col-lg-4>>:

~/node_project/views/sharks.html

... <div class="container">     <div class="row">         <div class="col-lg-4">             <p>                 <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.                 </div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">             </p>         </div>         <div class="col-lg-4">             <p>                 <div class="caption">Other sharks are known to be friendly and welcoming!</div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">             </p>         </div>     </div>   </div>   </html> 

Para obtener una introducción al sistema de cuádriculas de Bootstrap, incluidos sus diseños de fila y columna, consulte esta introducción a Bootstrap.

A continuación, añada otra columna que incluya el extremo con nombre para la solicitud de POST con los datos de tiburones del usuario y las etiquetas de la plantilla EJS que capturarán esos datos. Esta columna se colocará debajo de las etiquetas de cierre </p> y </div> de la columna anterior y encima de las etiquetas de cierre para la fila, el contenedor y el documento HTML. Estas etiquetas de cierre ya están implementadas en su código; también se muestran a continuación con comentarios. Déjelos implementados al añadir el siguiente código para crear la nueva columna:

~/node_project/views/sharks.html

...        </p> <!-- closing p from previous column -->    </div> <!-- closing div from previous column --> <div class="col-lg-4">             <p>                 <form action="/sharks/addshark" method="post">                     <div class="caption">Enter Your Shark</div>                     <input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>                     <input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>                     <button type="submit">Submit</button>                 </form>             </p>         </div>     </div> <!-- closing div for row --> </div> <!-- closing div for container -->  </html> <!-- closing html tag --> 

En la etiqueta form, está añadiendo un extremo "/sharks/addshark" para los datos sobre tiburones del usuario y especificando el método de POST para enviarlo. En los campos de entrada, está especificando campos para "Sharks Name" y "Shark Character", los cuales corresponden al modelo de Shark que definió previamente.

Para añadir las entradas del usuario a su colección sharks, utiliza etiquetas de la plantilla EJS (<%=, %>) junto con la sintaxis de JavaScript para asignar las entradas del usuario a los campos correspondientes en el documento recién creado. Para obtener más información sobre los objetos de JavaScript, consulte nuestro artículo Información sobre objetos de JavaScript. Para obtener más información sobre etiquetas de plantillas de EJS, consulte la documentación de EJS.

Al finalizar, el contenedor completo con las tres columnas, incluida la columna con su formulario de entrada de tiburones, tendrá este aspecto:

~/node_project/views/sharks.html

... <div class="container">     <div class="row">         <div class="col-lg-4">             <p>                 <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.                 </div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">             </p>         </div>         <div class="col-lg-4">             <p>                 <div class="caption">Other sharks are known to be friendly and welcoming!</div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">             </p>         </div>     <div class="col-lg-4">             <p>                 <form action="/sharks/addshark" method="post">                     <div class="caption">Enter Your Shark</div>                     <input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>                     <input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>                     <button type="submit">Submit</button>                 </form>             </p>         </div>     </div>   </div>  </html> 

Guarde y cierre el archivo cuando concluya la edición.

Ahora que tiene una forma de recopilar las entradas de su usuario, puede crear un extremo para mostrar los tiburones mostrados y la información asociada sobre su carácter.

Copie el archivo sharks.html recién modificado a un archivo llamado getshark.html:

  • cp views/sharks.html views/getshark.html

Abra getshark.html:

  • nano views/getshark.html

Dentro del archivo, modificaremos la columna que usamos para crear nuestro formulario de entrada de tiburones sustituyéndola por una columna que mostrará los tiburones en nuestra colección sharks. Una vez más, su código se aplicará entre las etiquetas existentes </p> y </div> de la columna anterior y las etiquetas de cierre de la fila, del contenedor y del documento HTML. Recuerde dejar estas etiquetas en su lugar al añadir el siguiente código para crear la columna:

~/node_project/views/getshark.html

...        </p> <!-- closing p from previous column -->    </div> <!-- closing div from previous column --> <div class="col-lg-4">            <p>               <div class="caption">Your Sharks</div>                   <ul>                      <% sharks.forEach(function(shark) { %>                         <p>Name: <%= shark.name %></p>                         <p>Character: <%= shark.character %></p>                      <% }); %>                   </ul>             </p>         </div>     </div> <!-- closing div for row --> </div> <!-- closing div for container -->  </html> <!-- closing html tag --> 

Aquí utiliza etiquetas de plantilla EJS y el método forEach() para mostrar cada valor de su colección sharks, incluida información sobre el tiburón que se añadió.

Al finalizar, el contenedor completo con las tres columnas, incluida la columna con la colección sharks, tendrá este aspecto:

~/node_project/views/getshark.html

... <div class="container">     <div class="row">         <div class="col-lg-4">             <p>                 <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.                 </div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">             </p>         </div>         <div class="col-lg-4">             <p>                 <div class="caption">Other sharks are known to be friendly and welcoming!</div>                 <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">             </p>         </div>     <div class="col-lg-4">             <p>               <div class="caption">Your Sharks</div>                   <ul>                      <% sharks.forEach(function(shark) { %>                         <p>Name: <%= shark.name %></p>                         <p>Character: <%= shark.character %></p>                      <% }); %>                   </ul>             </p>         </div>     </div>   </div>  </html> 

Guarde y cierre el archivo cuando concluya la edición.

Para que la aplicación utilice las plantillas que creó, deberá añadir algunas líneas a su archivo app.js. Ábralo de nuevo:

  • nano app.js

Encima del punto en que agregó la función express.urlencoded(), agregue las siguientes líneas:

~/node_project/app.js

... app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.use(express.urlencoded({ extended: true })); app.use(express.static(path));  ... 

El método app.engine indica a la aplicación que asigne el motor de la plantilla EJS a los archivos HTML, mientras que app.set define el motor de vista predeterminado.

Ahora, su archivo app.js tendrá el siguiente aspecto:

~/node_project/app.js

const express = require('express'); const app = express(); const router = express.Router(); const db = require('./db');  const path = __dirname + '/views/'; const port = 8080;  router.use(function (req,res,next) {   console.log('/' + req.method);   next(); });  router.get('/',function(req,res){   res.sendFile(path + 'index.html'); });  router.get('/sharks',function(req,res){   res.sendFile(path + 'sharks.html'); });  app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.use(express.urlencoded({ extended: true })); app.use(express.static(path)); app.use('/', router);  app.listen(port, function () {   console.log('Example app listening on port 8080!') }) 

Ahora que creó vistas que pueden funcionar de forma dinámica con datos de usuarios, es el momento de crear las rutas de su proyecto para unir sus vistas y la lógica del controlador.

Paso 6: Crear rutas

El paso final para establecer los componentes de la aplicación será crear rutas. Separaremos nuestras rutas por funciones incluidas una ruta a la página de destino de nuestra aplicación y otra ruta a nuestra página de tiburones. En nuestra ruta sharks integraremos la lógica de nuestro controlador con las vistas que creamos en el paso anterior.

Primero, cree un directorio routes:

  • mkdir routes

A continuación, abra un archivo llamado index.js en este directorio:

  • nano routes/index.js

Este archivo primero importará los objetos express, router y path, lo que nos permitirá definir las rutas que deseamos exportar con el objeto router y permitir que funcione de forma dinámica con rutas de archivo. Añada el siguiente código en la parte superior del archivo:

~/node_project/routes/index.js

const express = require('express'); const router = express.Router(); const path = require('path'); 

A continuación agregue la función router.use, la cual carga una función de middleware que registrará las solicitudes del router y las transmitirá a las rutas de la aplicación.

~/node_project/routes/index.js

...  router.use (function (req,res,next) {   console.log('/' + req.method);   next(); }); 

Las solicitudes al root de nuestra aplicación se dirigirán aquí primero, y desde este punto los usuarios accederán a la página de inicio de nuestra aplicación, la ruta que definiremos a continuación. Añada el siguiente código debajo de la función router.use para definir la ruta a la página de destino:

~/node_project/routes/index.js

...  router.get('/',function(req,res){   res.sendFile(path.resolve('views/index.html')); }); 

Cuando los usuarios visitan nuestra aplicación, el primer lugar al que queremos enviarlos es la página de inicio index.html que tenemos en nuestro directorio views.

Por último, para que sea posible acceder a estas rutas como módulos importables en otros puntos de la aplicación, agregue una expresión de cierre al final del archivo para exportar el objeto router:

~/node_project/routes/index.js

...  module.exports = router; 

El archivo terminado tendrá este aspecto:

~/node_project/routes/index.js

const express = require('express'); const router = express.Router(); const path = require('path');  router.use (function (req,res,next) {   console.log('/' + req.method);   next(); });  router.get('/',function(req,res){   res.sendFile(path.resolve('views/index.html')); });  module.exports = router; 

Guarde y cierre este archivo cuando concluya la edición.

A continuación, abra un archivo llamado sharks.js para definir la forma en que la aplicación debería usar los diferentes extremos y vistas que creamos para trabajar con la entrada sobre tiburones de nuestro usuario:

  • nano routes/sharks.js

En la parte superior del archivo, importe los objetos express y router:

~/node_project/routes/sharks.js

const express = require('express'); const router = express.Router(); 

A continuación, importe un módulo llamado shark que le permitirá trabajar con las funciones exportadas que definió con su controlador:

~/node_project/routes/sharks.js

const express = require('express'); const router = express.Router(); const shark = require('../controllers/sharks'); 

Ahora puede crear rutas usando las funciones index, create y list que definó en su archivo de controlador sharks. Cada ruta se asociará al método HTTP adecuado: GET para la representación de la principal página de inicio de información sobre tiburones y la presentación de la lista de tiburones al usuario, y POST para la creación de una nueva entrada sobre tiburones:

~/node_project/routes/sharks.js

...  router.get('/', function(req, res){     shark.index(req,res); });  router.post('/addshark', function(req, res) {     shark.create(req,res); });  router.get('/getshark', function(req, res) {     shark.list(req,res); }); 

Cada ruta utiliza la función relacionada en controllers/sharks.js, ya que hicimos posible el acceso a ese módulo al importarlo en la parte superior de este archivo.

Por último, cierre el archivo vinculando estas rutas al objeto router y exportándolas:

~/node_project/routes/index.js

...  module.exports = router; 

El archivo terminado tendrá este aspecto:

~/node_project/routes/sharks.js

const express = require('express'); const router = express.Router(); const shark = require('../controllers/sharks');  router.get('/', function(req, res){     shark.index(req,res); });  router.post('/addshark', function(req, res) {     shark.create(req,res); });  router.get('/getshark', function(req, res) {     shark.list(req,res); });  module.exports = router; 

Guarde y cierre el archivo cuando concluya la edición.

El último paso para que su aplicación pueda acceder a estas rutas será añadirlas a app.js. Abra el archivo de nuevo:

  • nano app.js

Debajo de su constante db, agregue la siguiente importación para sus rutas:

~/node_project/app.js

... const db = require('./db'); const sharks = require('./routes/sharks'); 

A continuación, sustituya la función app.use que actualmente monta su objeto router por la siguiente línea, que montará el módulo router de sharks:

~/node_project/app.js

... app.use(express.static(path)); app.use('/sharks', sharks);  app.listen(port, function () {         console.log("Example app listening on port 8080!") }) 

Ahora puede eliminar las rutas que se definieron previamente en este archivo, ya que importará las rutas de su aplicación usando el módulo router de sharks.

La versión final de su archivo app.js tendrá este aspecto:

~/node_project/app.js

const express = require('express'); const app = express(); const router = express.Router(); const db = require('./db'); const sharks = require('./routes/sharks');  const path = __dirname + '/views/'; const port = 8080;  app.engine('html', require('ejs').renderFile); app.set('view engine', 'html'); app.use(express.urlencoded({ extended: true })); app.use(express.static(path)); app.use('/sharks', sharks);  app.listen(port, function () {   console.log('Example app listening on port 8080!') }) 

Guarde y cierre el archivo cuando concluya la edición.

Ahora puede ejecutar tree de nuevo para ver la estructura final de su proyecto:

  • tree -I node_modules

Así, la estructura del proyecto tendrá el siguiente aspecto:

Output├── Dockerfile ├── README.md ├── app.js ├── controllers │   └── sharks.js ├── db.js ├── models │   └── sharks.js ├── package-lock.json ├── package.json ├── routes │   ├── index.js │   └── sharks.js └── views     ├── css     │   └── styles.css     ├── getshark.html     ├── index.html     └── sharks.html 

Una vez creados e implementados todos los componentes de su aplicación, estará listo para añadir un tiburón de prueba a su base de datos.

Si siguió el tutorial de configuración inicial para servidores de los requisitos previos, deberá modificar su firewall, ya que actualmente solo permite el tráfico SSH. Para permitir el tráfico al puerto 8080 ejecute lo siguiente:

  • sudo ufw allow 8080

Inicie la aplicación:

  • node app.js

A continuación, visite la dirección http://your_server_ip:8080 en su navegador. Visualizará la siguiente página de inicio:

Página de inicio de la aplicación

Haga clic en el botón Get Shark Info. Verá la siguiente página de información, con el formulario de entrada de tiburones añadido:

Formulario de Shark Info

En el formulario, agregue un tiburón que elija. A los efectos de esta demostración, añadiremos Megalodon Shark en el campo Shark Name y Ancient en el campo Shark Character:

Formulario de Shark completado

Haga clic en el botón Submit. Visualizará una página con esta información sobre tiburones que se le mostrará de nuevo:

Resultado de tiburones

También verá en su consola resultados que indican que el tiburón se agregó a su colección:

OutputExample app listening on port 8080! { name: 'Megalodon Shark', character: 'Ancient' } 

Si desea crear una nueva entrada de tiburones, regrese a la página Sharks y repita el proceso de adición de un tiburón.

Con esto, dispondrá de una aplicación de información sobre tiburones activa que permite a los usuarios añadir información sobre sus tiburones favoritos.

Conclusión

A través de este tutorial, creó una aplicación de Node integrando una base de datos de MongoDB y reescribiendo la lógica de la aplicación con el patrón de arquitectura de MVC. Esta aplicación puede ser un buen punto de partida para una aplicación CRUD completa.

Para acceder a más recursos sobre el patrón de MVC en otros contextos, consulte nuestra Serie de desarrollo de Django o Cómo crear una aplicación web moderna para administrar información de clientes con Django y React en Ubuntu 18.04.

Para obtener más información sobre cómo trabajar con MongoDB, consulte nuestra biblioteca de tutoriales de MongoDB.