Site icon 8HOST.COM

Интеграция MongoDB в приложение Node.js: создание модели и контроллера

При разработке проекта в Node.js вам может понадобиться инструмент для хранения и запроса данных. В таком случае вам нужно будет выбрать систему управления базами данных, которая лучше всего подойдет для работы с вашим типом данных.

Данный мануал поможет интегрировать MongoDB в существующее приложение Node. Базы данных NoSQL (к которым относится MongoDB) подходят для управления данными, которым нужна высокая масштабируемость и гибкость. Также MongoDB хорошо интегрируется с Node, поскольку она разработана для асинхронной работы с объектами JSON.

Читайте также: Основы работы с JSON

Чтобы интегрировать MongoDB в ваш проект, вы будете использовать ODM Mongoose (Object Document Mapper, объектно-документное отображение) для создания схем и моделей данных вашего приложения. Это позволит вам организовать код приложения в соответствии с архитектурным шаблоном модель-представление-контроллер (MVC), который позволяет отделить логику обработки пользовательского ввода от структурирования данных и представления их пользователю. Этот шаблон может облегчить дальнейшее тестирование и разработку путем разделения элементов.

В результате у вас будет рабочее приложение, которое будет принимать пользовательские данные и отображать результаты в браузере.

Требования

1: Создание пользователя MongoDB

Прежде чем начать работу с кодом приложения, мы создадим администратора, который будет иметь доступ к базе данных нашего приложения. Этот пользователь будет иметь административные привилегии для любой базы данных, что даст вам возможность переключаться и создавать новые базы данных по мере необходимости.

Сначала убедитесь, что MongoDB на вашем сервере работает:

sudo systemctl status mongodb

Следующий вывод показывает, что MongoDB работает правильно:

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
...

Затем откройте оболочку Mongo, чтобы создать своего пользователя:

mongo

Это поместит вас в оболочку:

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

При этом вы увидите несколько предупреждений, связанных с вашим неограниченным доступом к базе данных admin. Вы можете узнать больше об ограничении этого доступа в производственной настройке, прочитав мануал Установка и защита MongoDB в Ubuntu 16.04.

На данный момент вы можете использовать свой доступ к базе данных admin, чтобы создать пользователя с привилегиями userAdminAnyDatabase, который обеспечит парольный доступ к базам данных вашего приложения.

В оболочке укажите, что вы хотите использовать базу данных admin для создания пользователя:

use admin

Затем создайте роль и пароль. Для этого добавьте имя пользователя и пароль с помощью команды db.createUser. После ввода этой команды оболочка будет добавлять три точки перед каждой строкой, пока команда не будет выполнена. Обязательно замените указанные здесь имя пользователя и пароль своими данными:

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

Это создает запись для пользователя 8host в базе данных admin. Выбранное вами имя и база данных admin будут служить идентификаторами вашего пользователя.

Вывод для всего процесса (включая сообщение о том, что запись прошла успешно) будет выглядеть следующим образом:

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

Создав своего пользователя и пароль, вы можете выйти из оболочки Mongo:

exit

Теперь, когда вы создали пользователя для своей базы данных, вы можете клонировать код простого тестового приложения и добавить библиотеку Mongoose, которая позволит вам использовать схемы и модели для коллекций в ваших базах данных.

2: Добавление информации о Mongoose и базе данных в проект

Нашим следующим шагом будет клонирование исходного кода приложения и добавление в проект данных о Mongoose и нашей БД MongoDB.

В домашний каталог вашего пользователя sudo клонируйте GitHub репозиторий nodejs-image-demo. Этот репозиторий содержит код, который мы также использовали в мануале Сборка приложения Node.js с помощью Docker.

Клонируйте репозиторий в каталог node_project:

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

Перейдите в каталог node_project:

cd  node_project

Прежде чем изменять код проекта, давайте взглянем на структуру проекта с помощью команды tree.

Примечание: tree – полезная команда для просмотра структур файлов и каталогов из командной строки. Вы можете установить ее с помощью:

sudo apt install tree

Чтобы использовать его, перейдите в заданный каталог и введите tree. Вы также можете указать путь к начальной точке с помощью такой команды:

tree /home/8host/8hosts-project

Введите следующую команду, чтобы просмотреть каталог node_project:

tree

Структура текущего проекта выглядит следующим образом:

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

По мере продвижения мы будем добавлять в этот проект каталоги, и tree будет помогать нам отслеживать прогресс.

Затем добавьте в проект npm пакет mongoose с помощью команды npm install:

npm install mongoose

Эта команда создаст в каталоге вашего проекта каталог node_modules, используя зависимости, перечисленные в файле package.json, и добавит mongoose в этот каталог. Она также добавит mongoose к зависимостям, перечисленным в файле package.json. Более подробно о файле package.json рассказывается в разделе 1 мануала Сборка приложения Node.js с помощью Docker.

Перед созданием любых схем или моделей Mongoose нужно добавить информацию о подключении к базе данных, чтобы приложение могло подключиться к ней.

Чтобы максимально разделить части вашего приложения, создайте отдельный файл для информации о подключении к базе данных, который будет называться db.js. Вы можете открыть этот файл с помощью nano или другого редактора:

nano db.js

Сначала импортируйте модуль mongoose, используя функцию require:

const mongoose = require('mongoose');

Это даст вам доступ к встроенным методам Mongoose, которые нужно использовать для создания соединения с вашей базой данных.

Затем добавьте следующие константы, чтобы определить информацию для URI подключения Mongo. Хотя имя пользователя и пароль указывать необязательно, мы включим их, чтобы иметь возможность включить обязательную аутентификацию для базы данных. Не забудьте заменить имя пользователя и пароль, указанные ниже, собственными данными. Вы также можете выбрать другое имя БД вместо ‘sharkinfo’:

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

Поскольку мы работаем с базой данных локально, мы использовали в качестве имени хоста 127.0.0.1. В других контекстах разработки это не будет работать: например, если вы используете отдельный сервер базы данных или работаете с несколькими нодами в контейнеризованной среде.

Наконец, определите константу для URI и создайте соединение, используя метод mongoose.connect():

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

Обратите внимание, что в URI в качестве authSource пользователя указана БД admin. Это необходимо, поскольку ранее мы указали имя пользователя в строке подключения. Флаг useNewUrlParser вместе с mongoose.connect () позволяет использовать новый URL парсер Mongo.

Сохраните и закройте файл, когда закончите редактирование.

Теперь только добавьте информацию о соединении с базой данных в файл app.js, чтобы приложение могло ее использовать. Откройте app.js:

nano app.js

Первые строки файла будут выглядеть так:

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

После определения константы маршрутизатора в верхней части файла добавьте следующую строку:

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

Это позволяет приложению использовать информацию о соединении с базой данных, указанную в db.js.

Сохраните и закройте файл.

Получив информацию о вашей базе данных и добавив Mongoose в проект, вы можете создавать схемы и модели, которые будут систематизировать данные в вашей коллекции sharks.

3: Создание схем и моделей Mongoose

Теперь нужно подумать о структуре коллекции sharks, которую пользователи будут создавать в БД sharkinfo с помощью своих входных данных. Какую структуру будут иметь эти созданные документы? Страница текущего приложения содержит некоторые сведения о различных акулах и их поведении.

Соответственно мы можем предложить пользователям добавлять новых акул с подробной информацией об их общем характере. Это поможет определить, как мы будем созавать нашу схему.

Чтобы схемы и модели отличались от других частей вашего приложения, создайте каталог models в каталоге текущего проекта:

mkdir models

Затем откройте файл sharks.js, чтобы создать схему и модель:

nano models/sharks.js

Импортируйте модуль mongoose в верхней части файла:

const mongoose = require('mongoose');

Ниже определите объект Schema, который будет основой для вашей схемы:

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

Теперь вы можете определить поля, которые вы хотели бы включить в свою схему. Поскольку мы хотим создать коллекцию с отдельными акулами и информацией об их поведении, давайте добавим ключи name  и character. Добавьте следующую схему Shark после других определений:

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

Читайте также: Типы данных в JavaScript

Это определение включает в себя информацию о типе входных данных, которые мы ожидаем получить от пользователей (в данном случае это строки), и о том, требуются ли эти данные.

Наконец, создайте модель Shark, используя функцию Mongoose model(). Эта модель позволит вам запрашивать документы из коллекции и проверять новые документы. Добавьте следующую строку в конец файла:

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

Эта последняя строка сделает модель Shark доступной в виде модуля, используя свойство module.exports. Это свойство определяет значения, которые модуль будет экспортировать, делая их доступными для использования в других частях приложения.

Готовый файл 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)

Сохраните и закройте файл.

Имея схему и модель Shark, вы можете начать работать над логикой, которая будет определять обработку пользовательского ввода.

4: Создание контроллеров

Следующим шагом будет создание компонента контроллера, который определяет, как вводимые пользователем данные сохраняются в БД и возвращаются пользователю.

Сначала создайте каталог для контроллера:

mkdir controllers

Затем откройте в этой папке файл sharks.js:

nano controllers/sharks.js

В верхней части файла импортируйте модуль с моделью Shark, чтобы использовать его в логике контроллера. Мы также импортируем модуль path для доступа к утилитам, которые позволят нам указать путь к форме для пользовательского ввода.

Добавьте следующие функции в начало файла:

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

Далее мы напишем последовательность функций, которые нужно экспортировать с помощью модуля контроллера и ярлыка Node exports. Эти функции будут выполнять три задачи, связанные с данными пользователя:

Для начала создайте функцию index для отображения страницы sharks с формой ввода. Добавьте эту функцию после импорта:

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

Затем, под функцией index, добавьте функцию create, чтобы сделать новую запись об акуле в вашей коллекции sharks:

...
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');
}
});
};

Эта функция вызывается, когда пользователь публикует данные об акуле в вашей форме на странице sharks.html. Мы создадим маршрут с этой конечной точкой POST позже, когда создадим маршруты приложения. В теле POST запроса функция create создаст новый объект документа shark (здесь он называется newShark), используя импортированную модель Shark. Мы добавили метод console.log для вывода записей на консоль, чтобы убедиться, что метод POST работает как задумано, но вы можете его не использовать, если не хотите.

Используя объект newShark, функция create затем вызовет метод Mongoose model.save() для создания нового документа с использованием ключей, которые вы определили в модели Shark. Эта функция обратного вызова следует стандартному шаблону Node: обратный вызов (ошибка, результаты). В случае ошибки приложение отправит нашим пользователям сообщение об ошибке, а в случае успеха оно использует метод res.redirect() для отправки пользователей в конечную точку, которая будет отображать введенную ими информацию обратно в их браузере.

Наконец, функция list отобразит содержимое коллекции на экране пользователя. Добавьте следующий код после функции create:

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

Эта функция использует модель Shark с методом model.find() для возврата информации, которая была введена в коллекцию. Для этого она возвращает объект запроса – в данном случае все записи в коллекции – используя функцию exec() Mongoose. В случае ошибки функция обратного вызова отправит ошибку 500.

Возвращенный объект запроса с коллекцией sharks будет отображен на странице getshark, которую мы создадим на следующем этапе с помощью языка шаблонов EJS.

Готовый файл будет выглядеть так:

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
});
});
};

Имейте в виду: здесь мы не используем стрелочные функции, но вы в своем процессе разработки можете включить их при итерации этого кода.

Читайте также: Определение функций в JavaScript

Сохраните и закройте файл.

Прежде чем перейти к следующему этапу, вы можете снова запустить tree из каталога node_project, чтобы просмотреть структуру проекта на этом этапе. На этот раз для краткости опустите каталог node_modules, используя опцию -I:

tree -I node_modules

С внесенными дополнениями структура вашего проекта будет выглядеть так:

├── 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

Теперь, когда у вас есть контроллер, который управляет сохранением и выводом пользовательских данных, вы можете перейти к созданию представлений, которые будут реализовывать логику контроллера. Об этом речь пойдет в следующем мануале.