Вступление
Работая сNode.js, вы можете обнаружить, что разрабатываете проект, который хранит и запрашивает данные. В этом случае вам нужно будет выбрать решение для базы данных, которое имеет смысл для данных вашего приложения и типов запросов.
В этом руководстве вы интегрируете базу данныхMongoDB с существующим приложением Node. NoSQL databases, такие как MongoDB, могут быть полезны, если ваши требования к данным включают масштабируемость и гибкость. MongoDB также хорошо интегрируется с Node, поскольку он предназначен для асинхронной работы с объектамиJSON.
Чтобы интегрировать MongoDB в свой проект, вы будете использоватьObject Document Mapper (ODM)Mongoose для создания схем и моделей для данных вашего приложения. Это позволит вам организовать код вашего приложения в соответствии с архитектурным шаблономmodel-view-controller (MVC), который позволяет вам отделить логику того, как ваше приложение обрабатывает ввод пользователя, от того, как ваши данные структурированы и отображаются для пользователя. Использование этого шаблона может облегчить дальнейшее тестирование и разработку путем введения разделения проблем в вашу кодовую базу.
В конце урока у вас будет работающее информационное приложение об акулах, которое примет пользовательские данные о своих любимых акулах и отобразит результаты в браузере:

Предпосылки
-
Локальная машина или сервер разработки под управлением Ubuntu 18.04 вместе с пользователем без полномочий root с привилегиями
sudoи активным брандмауэром. Инструкции по их настройке на сервере 18.04 см. ВInitial Server Setup guide. -
Node.js and npm installed on your machine or server, following these instructions on installing with the PPA managed by NodeSource.
-
MongoDB, установленный на вашем компьютере или сервере, после шага 1How To Install MongoDB in Ubuntu 18.04.
[[step-1 -—- created-a-mongo-user]] == Шаг 1. Создание пользователя Mongo
Прежде чем мы начнем работать с кодом приложения, мы создадим административного пользователя, который будет иметь доступ к базе данных нашего приложения. Этот пользователь будет иметь административные привилегии для любой базы данных, что даст вам возможность переключаться и создавать новые базы данных по мере необходимости.
Сначала убедитесь, что MongoDB работает на вашем сервере:
sudo systemctl status mongodb
Следующий вывод показывает, что MongoDB работает:
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
...
Затем откройте оболочку Mongo, чтобы создать своего пользователя:
mongo
Это поместит вас в административную оболочку:
OutputMongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.3
...
>
Когда вы откроете оболочку, вы увидите несколько административных предупреждений из-за вашего неограниченного доступа к базе данныхadmin. Вы можете узнать больше об ограничении этого доступа, прочитавHow To Install and Secure MongoDB on Ubuntu 16.04, когда вы переходите к производственной установке.
На данный момент вы можете использовать свой доступ к базе данныхadmin, чтобы создать пользователя с привилегиямиuserAdminAnyDatabase, который позволит защищенный паролем доступ к базам данных вашего приложения.
В оболочке укажите, что вы хотите использовать базу данныхadmin для создания своего пользователя:
use admin
Затем создайте роль и пароль, добавив имя пользователя и пароль с помощью командыdb.createUser. После ввода этой команды оболочка будет добавлять три точки перед каждой строкой, пока команда не будет завершена. Обязательно замените предоставленные здесь имя пользователя и пароль на свое имя пользователя и пароль:
db.createUser(
{
user: "sammy",
pwd: "your_password",
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)
Это создает запись для пользователяsammy в базе данныхadmin. Выбранное вами имя пользователя и база данныхadmin будут служить идентификаторами для вашего пользователя.
Вывод для всего процесса будет выглядеть следующим образом, включая сообщение о том, что запись прошла успешно:
Output> db.createUser(
... {
... user: "sammy",
... pwd: "your_password",
... roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
... }
...)
Successfully added user: {
"user" : "sammy",
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
}
]
}
Создав свой пароль и пользователь, вы можете выйти из оболочки Mongo:
exit
Теперь, когда вы создали пользователя базы данных, вы можете перейти к клонированию кода начального проекта и добавлению библиотеки Mongoose, которая позволит вам реализовывать схемы и модели для коллекций в ваших базах данных.
[[step-2 -—- add-mongoose-and-database-information-to-the-project]] == Шаг 2 - Добавление информации о мангусте и базе данных в проект
Следующими нашими шагами будет клонирование начального кода приложения и добавление в проект данных Mongoose и нашей базы данных MongoDB.
В домашнем каталоге пользователя без полномочий root клонируйтеnodejs-image-demo repository изDigitalOcean Community GitHub account. Этот репозиторий включает код из настройки, описанной вHow To Build a Node.js Application with Docker.
Клонируйте репозиторий в каталог с именемnode_project:
git clone https://github.com/do-community/nodejs-image-demo.git node_project
Перейдите в каталогnode_project:
cd node_project
Прежде чем изменять код проекта, давайте взглянем на структуру проекта с помощью командыtree.
[.Примечание]##
Tip:tree - полезная команда для просмотра структур файлов и каталогов из командной строки. Вы можете установить его с помощью следующей команды:
sudo apt install tree
Чтобы использовать его, введитеcd в указанный каталог и введитеtree. Вы также можете указать путь к начальной точке с помощью такой команды:
tree /home/sammy/sammys-project
Введите следующее, чтобы просмотреть каталогnode_project:
tree
Структура текущего проекта выглядит следующим образом:
Output├── Dockerfile
├── README.md
├── app.js
├── package-lock.json
├── package.json
└── views
├── css
│ └── styles.css
├── index.html
└── sharks.html
Мы будем добавлять каталоги в этот проект по мере продвижения по руководству, иtree будет полезной командой, которая поможет нам отслеживать наш прогресс.
Затем добавьте в проект пакетmongoose npm с помощью командыnpm install:
npm install mongoose
Эта команда создаст каталогnode_modules в каталоге вашего проекта, используя зависимости, перечисленные в файле проектаpackage.json, и добавитmongoose в этот каталог. Он также добавитmongoose к зависимостям, перечисленным в вашем файлеpackage.json. Для более подробного обсужденияpackage.json см.Step 1 вHow To Build a Node.js Application with Docker.
Перед созданием любых схем или моделей Mongoose мы добавим информацию о подключении к нашей базе данных, чтобы наше приложение могло подключиться к нашей базе данных.
Чтобы максимально разделить проблемы вашего приложения, создайте отдельный файл для информации о соединении с базой данных с именемdb.js. Вы можете открыть этот файл с помощьюnano или вашего любимого редактора:
nano db.js
Сначала импортируйтеmongoosemodule с помощью функцииrequire:
~/node_project/db.js
const mongoose = require('mongoose');
Это даст вам доступ к встроенным методам Mongoose, которые вы будете использовать для создания соединения с вашей базой данных.
Затем добавьте следующиеconstants, чтобы определить информацию для URI подключения Mongo. Хотя имя пользователя и пароль необязательны, мы включим их, чтобы мы могли требовать аутентификацию для нашей базы данных. Обязательно замените имя пользователя и пароль, указанные ниже, на свою собственную информацию и не стесняйтесь называть базу данных чем-то другим, кроме'sharkinfo', если вы предпочитаете:
~/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';
Поскольку наша база данных работает локально, мы использовали127.0.0.1 в качестве имени хоста. Это изменится в других контекстах разработки: например, если вы используете отдельный сервер базы данных или работаете с несколькими узлами в контейнерном рабочем процессе.
Наконец, определите константу для URI и создайте соединение с помощью метода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});
Обратите внимание, что в URI мы указалиauthSource для нашего пользователя как базу данныхadmin. Это необходимо, поскольку мы указали имя пользователя в нашей строке подключения. Использование флагаuseNewUrlParser сmongoose.connect() указывает, что мы хотим использовать Mongo'snew URL parser.
Сохраните и закройте файл, когда вы закончите редактирование.
В качестве последнего шага добавьте информацию о подключении к базе данных в файлapp.js, чтобы приложение могло его использовать. Открытьapp.js:
nano app.js
Первые строки файла будут выглядеть так:
~/node_project/app.js
const express = require('express');
const app = express();
const router = express.Router();
const path = __dirname + '/views/';
...
Под определением константыrouter, расположенным в верхней части файла, добавьте следующую строку:
~/node_project/app.js
...
const router = express.Router();
const db = require('./db');
const path = __dirname + '/views/';
...
Это указывает приложению использовать информацию о соединении с базой данных, указанную вdb.js.
Сохраните и закройте файл, когда вы закончите редактирование.
Имея информацию о вашей базе данных и добавив Mongoose в ваш проект, вы готовы создавать схемы и модели, которые будут формировать данные в вашей коллекцииsharks.
[[step-3 -—- created-mongoose-schemas-and-models]] == Шаг 3 - Создание схем и моделей Mongoose
Нашим следующим шагом будет подумать о структуре коллекцииsharks, которую пользователи будут создавать в базе данныхsharkinfo со своими входными данными. Какую структуру мы хотим, чтобы эти созданные документы имели? Страница с информацией об акулах нашего текущего приложения содержит некоторые сведения о различных акулах и их поведении:

В соответствии с этой темой, мы можем предложить пользователям добавлять новых акул с подробной информацией об их общем характере. Эта цель будет определять, как мы создаем нашу схему.
Чтобы ваши схемы и модели отличались от других частей вашего приложения, создайте каталогmodels в каталоге текущего проекта:
mkdir models
Затем откройте файл с именемsharks.js, чтобы создать схему и модель:
nano models/sharks.js
Импортируйте модульmongoose в начало файла:
~/node_project/models/sharks.js
const mongoose = require('mongoose');
Ниже определите объектSchema, который будет использоваться в качестве основы для вашей схемы акулы:
~/node_project/models/sharks.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
Теперь вы можете определить поля, которые вы хотели бы включить в свою схему. Поскольку мы хотим создать коллекцию с отдельными акулами и информацией об их поведении, давайте добавим ключиnamekey иcharacter. Добавьте следующую схемуShark под определениями констант:
~/node_project/models/sharks.js
...
const Shark = new Schema ({
name: { type: String, required: true },
character: { type: String, required: true },
});
Это определение включает информацию о типе ввода, который мы ожидаем от пользователей - в данном случаеstring - и о том, требуется ли этот ввод.
Наконец, создайте модельShark с помощью Mongoosemodel() function. Эта модель позволит вам запрашивать документы из вашей коллекции и проверять новые документы. Добавьте следующую строку внизу файла:
~/node_project/models/sharks.js
...
module.exports = mongoose.model('Shark', Shark)
Эта последняя строка делает нашу модельShark доступной в виде модуля с использованиемmodule.exports property. Это свойство определяет значения, которые модуль будет экспортировать, делая их доступными для использования в другом месте приложения.
Готовый файлmodels/sharks.js выглядит так:
~/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)
Сохраните и закройте файл, когда вы закончите редактирование.
Имея схему и модельShark, вы можете начать работу над логикой, которая будет определять, как ваше приложение будет обрабатывать ввод пользователя.
[[step-4 -—- Creating-controllers]] == Шаг 4 - Создание контроллеров
Нашим следующим шагом будет создание компонента контроллера, который будет определять, как вводимые пользователем данные сохраняются в нашей базе данных и возвращаются пользователю.
Сначала создайте каталог для контроллера:
mkdir controllers
Затем откройте в этой папке файл с именемsharks.js:
nano controllers/sharks.js
В верхней части файла мы импортируем модуль с нашей модельюShark, чтобы мы могли использовать его в логике нашего контроллера. Мы также импортируемpath module для доступа к утилитам, которые позволят нам указать путь к форме, в которую пользователи будут вводить своих акул.
Добавьте в начало файла следующие функцииrequire:
~/node_project/controllers/sharks.js
const path = require('path');
const Shark = require('../models/sharks');
Затем мы напишем последовательность функций, которые мы будем экспортировать с модулем контроллера, используяexports shortcut узла. Эти функции будут включать в себя три задачи, связанные с данными об акулах нашего пользователя:
-
Отправка пользователям формы ввода акулы.
-
Создание новой записи акулы.
-
Отображение акул обратно к пользователям.
Для начала создайте функциюindex для отображения страницы акул с формой ввода. Добавьте эту функцию ниже вашего импорта:
~/node_project/controllers/sharks.js
...
exports.index = function (req, res) {
res.sendFile(path.resolve('views/sharks.html'));
};
Затем под функциейindex добавьте функцию с именемcreate, чтобы сделать новую запись акулы в вашей коллекции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');
}
});
};
Эта функция будет вызываться, когда пользователь отправляет данные об акулах в форму на страницеsharks.html. Мы создадим маршрут с этой конечной точкой POST позже в руководстве, когда создадим маршруты нашего приложения. С помощьюbody запроса POST наша функцияcreate создаст новый объект документа акулы, здесь называемыйnewShark, используя модельShark, которую мы импортировали. Мы добавилиconsole.log method для вывода записи об акуле в консоль, чтобы проверить, работает ли наш метод POST, как задумано, но вы можете пропустить это, если хотите.
Используя объектnewShark, функцияcreate затем вызоветmodel.save() method Mongoose, чтобы создать новый документ акулы, используя ключи, которые вы определили в моделиShark. Этотcallback function следует заstandard Node callback pattern:callback(error, results). В случае ошибки мы отправим сообщение об ошибке нашим пользователям, а в случае успеха мы будем использоватьres.redirect() method для отправки пользователей на конечную точку, которая вернет им информацию об их акулах. в браузере.
Наконец, функцияlist вернет пользователю содержимое коллекции. Добавьте следующий код под функцией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
});
});
};
Эта функция использует модельShark сmodel.find() method Mongoose для возврата акул, которые были введены в коллекциюsharks. Он делает это, возвращая объект запроса - в данном случае все записи в коллекцииsharks - в качестве обещания, используяexec() function Mongoose. В случае ошибки функция обратного вызова отправит ошибку 500.
Возвращенный объект запроса с коллекциейsharks будет отображен на страницеgetshark, которую мы создадим на следующем шаге с использованием языка шаблоновEJS.
Готовый файл будет выглядеть так:
~/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
});
});
};
Имейте в виду, что хотя мы не используем здесьarrow functions, вы можете захотеть включить их при повторении этого кода в своем собственном процессе разработки.
Сохраните и закройте файл, когда вы закончите редактирование.
Перед тем как перейти к следующему шагу, вы можете снова запуститьtree из каталогаnode_project, чтобы просмотреть структуру проекта на этом этапе. На этот раз для краткости мы скажемtree опустить каталогnode_modules, используя параметр-I:
tree -I node_modules
С учетом внесенных вами изменений структура вашего проекта будет выглядеть следующим образом:
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
Теперь, когда у вас есть компонент контроллера, который управляет тем, как пользовательский ввод сохраняется и возвращается пользователю, вы можете перейти к созданию представлений, которые будут реализовывать логику вашего контроллера.
[[step-5 -—- using-ejs-and-express-middleware-to-collect-and-render-data]] == Шаг 5. Использование EJS и Express Middleware для сбора и визуализации данных
Чтобы наше приложение могло работать с пользовательскими данными, мы сделаем две вещи: во-первых, мы включим встроенную функцию промежуточного программного обеспечения Express,urlencoded(), которая позволит нашему приложению анализировать введенные пользователем данные. Во-вторых, мы добавим теги шаблона в наши представления, чтобы обеспечить динамическое взаимодействие с пользовательскими данными в нашем коде.
Чтобы работать с функциейurlencoded() в Express, сначала откройте файлapp.js:
nano app.js
Над функциейexpress.static() добавьте следующую строку:
~/node_project/app.js
...
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
...
Добавление этой функции позволит получить доступ к проанализированным данным POST из нашей формы информации об акулах. Мы указываемtrue с параметромextended, чтобы обеспечить большую гибкость в типе данных, которые наше приложение будет анализировать (включая такие вещи, как вложенные объекты). Пожалуйста, смотритеfunction documentation для получения дополнительной информации о параметрах.
Сохраните и закройте файл, когда вы закончите редактирование.
Далее мы добавим функциональность шаблона к нашим представлениям. Сначала установитеejs package сnpm install:
npm install ejs
Затем откройте файлsharks.html в папкеviews:
nano views/sharks.html
На шаге 3 мы посмотрели на эту страницу, чтобы определить, как мы должны написать нашу схему и модель Mongoose:

Теперь вместо двух столбцовlayout мы представим третий столбец с формой, в которую пользователи могут вводить информацию об акулах.
В качестве первого шага измените размеры существующих столбцов на4, чтобы создать три столбца одинакового размера. Обратите внимание, что вам нужно будет внести это изменение в две строки, которые в настоящее время читают<div class="col-lg-6">. Оба они станут<div class="col-lg-4">:
~/node_project/views/sharks.html
...
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.
Other sharks are known to be friendly and welcoming!
Для ознакомления с сеточной системой Bootstrap, включая макеты строк и столбцов, см. Этотintroduction to Bootstrap.
Затем добавьте еще один столбец, который включает именованную конечную точку для запроса POST с данными акулы пользователя и тегами шаблона EJS, которые будут захватывать эти данные. Этот столбец будет находиться под закрывающими тегами</p> и</div> из предыдущего столбца и над закрывающими тегами для строки, контейнера и HTML-документа. Эти закрывающие теги уже есть в вашем коде; они также отмечены ниже с комментариями. Оставьте их на месте, добавив следующий код для создания нового столбца:
~/node_project/views/sharks.html
...