This sponsored post features a product relevant to our readers while meeting our editorial guidelines for being objective and educational.
Russian (Pусский) translation by Nadia Gonzales (you can also view the original English article)
В данном руководстве я покажу вам, как реализовать веб-приложение для чата при помощи Node.js, Socket.IO (* библиотека JavaScript для приложений, работающих в режиме реального времени; благодаря ей возможно реализовать двунаправленную передачу данных в любое время. Здесь и далее прим. пер.) и MongoDB, и затем мы вместе развернем (* выполним дистанционную инсталляцию) его на Modulus (* PaaS (Platform as a Service) – платформа, предоставляемая в качестве услуги для разработчиков на Node.js. С 2017 стала Xervo, которая недоступна).
Для начала позвольте показать вам конечный вид приложения, которое получим к концу руководства.

Node.js будет ядром нашего приложения, Express будем использовать в качестве MVC (* Model-View-Controller – Модель-Представление-Контроллер. Архитектурный паттерн программного обеспечения, обычно используемый для разработки пользовательских интерфейсов, при котором приложение разделяется на три взаимодействующие части), базой данных будет MongoDB и Socket.IO будет использоваться для обеспечения коммуникации в режиме реального времени. Когда мы закончим с разработкой, то развернем наше приложение на Modulus. Фактически MongoDB находится на Modulus.
Сценарий (* пример использования системы)
- Джон хочет воспользоваться нашим приложением и открывает его в браузере.
- На первой странице он выбирает ник (* псевдоним), используемый во время чата, и входит в чат.
- В текстовом поле он что-то пишет и нажимает Enter.
- Текст отсылается RESTful веб-службе (* веб-служба, построенная с учётом REST (Representational State Transfer – передача состояния представления), то есть не нарушающая накладываемых им ограничений) и этот текст сохраняется в MongoDB.
- Перед сохранением в MongoDB тот же самый текст будет передан получателям, которые в тот момент также находятся в чате.
Как вы видите, это будет очень простое приложение, но при его создании мы рассмотрим почти все, что необходимо для создания веб-приложения. В данном приложении не будет системы каналов (* группа сообщений на определенную тематику), но вы можете форкнуть (* создать собственную копию, создание ветвления проекта) исходный код и реализовать модуль для каналов, чтобы попрактиковаться.
Разработка приложения с нуля
Я попробую объяснить небольшие части проекта и соединю их к концу руководства. Начнем с разработки серверной части приложения, затем перейдем к клиентской. Что же, давайте начнем с определения объектов доменов (* метаданные, абстрактно описывающие столбец таблицы, включая проверки и ограничения (т. е. определяющие все значения, которые может принимать соответствующий элемент)) модели MongoDB.
2.1. Модель
Для облегчения работы с базой данных мы будем использовать Mongoose (* ODM (Object Document Mapper - объектно-документный отобразитель), инструмент для моделирования в Node.js). В этом руководстве у нас имеется только одна модель – Message
. Эта модель сообщений содержит только поля text
, createDate
и author. У нас отсутствует модель для автора (такая как User
), поскольку мы реализуем систему регистрации/входа не полностью. В приложении будет простая страница, на которой отображаются ники участников, и такой ник будет сохранен в куки (* поддерживаемая протоколом НТТР текстовая запись размером до 4 Кбайт с данными о пользователе, возвращаемая Web-сервером при регистрации пользователя и хранящаяся на его ПК.). Ник будет использоваться в модели Message
для наполнения поля author
. Вы можете посмотреть на пример образца модели в формате JSON ниже:
{ text: "Hi, is there any Full Stack Developer here?" author: "john_the_full_stack", createDate: "2015.05.15" }
Для создания подобных документов вы можете создать модель при помощи функций Mongoose, представленных ниже:
var mongoose = require('mongoose') var Message = new mongoose.Schema({ author: String, message: String, createDate: { type: Date, default: Date.now } }); mongoose.model('Message', Message)
Просто импортируйте модуль Mongoose, определите поля и их атрибуты вашей модели в формате JSON и создайте модель под названием Message
. Эта модель будет использоваться на страницах где необходимо.
Возможно у вас возник вопрос, зачем мы сохраняем сообщения в базу данных, если мы уже передали это сообщение получателю в том же самом канале. Действительно, вам не нужно сохранять сообщения из чата, но я хотел бы объяснить, как взаимодействовать с базой данных. Как бы там ни было, мы будем использовать эту модель в нашем проекте внутри контроллеров. Контроллеров?
2.2. Контроллер
Как я упомянул выше, мы будем использовать Express для реализации MVC. И С
означает контроллер
. В нашем проекте будет только две конечные точки (* отдельные функции API) для работы с сообщениями. Одна из них служит для загрузки недавних сообщений чата, а другая – для сохранения присланных сообщений в базу данных и дальнейшей передачи их в канал.
..... app.get('/chat', function(req, res){ res.sendFile(__dirname + '/index.html'); }); app.get('/login', function(req, res){ res.sendFile(__dirname + '/login.html'); }); app.post('/messages', function(req, res, next) { var message = req.body.message; var author = req.body.author; var messageModel = new Message(); messageModel.author = author; messageModel.message = message; messageModel.save(function (err, result) { if (!err) { Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) { io.emit("message", messages); }); res.send("Message Sent!"); } else { res.send("Technical error occurred!"); } }); }); app.get('/messages', function(req, res, next) { Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) { res.json(messages); }); }); .....
Первый и второй контроллеры служат просто для предоставления статических файлов HTML для страниц входа и чата. Третий обрабатывает POST-запросы к конечной точке /messages,
где создаются и сохраняются новые сообщения. В этом контроллере сначала создается образец модели Message из тела запроса, и затем он сохраняется в базу данных при помощи функции Mongoose save
.
Я не буду углубляться в подробности работы с Mongoose. Вы можете ознакомиться с ними в документации. Вы можете предоставить функцию обратного вызова для функции save для проверки наличия каких-либо проблем. Если все успешно, то мы извлекаем последние пять записей, отсортированных в убывающем порядке за счет createDate
(* дата создания), и передаем пять сообщений участникам канала.
Что же, мы закончили с MC
. Теперь давайте перейдем к представлению
.
2.3. Представление
Как правило, в Express могут использоваться шаблонизаторы, например: Jade, EJS, Handlebars и т. д. Однако у нас есть только одна страница, и это – страница с сообщениями, поэтому мы обработаем запросы к ней статически. Собственно, как я сказал выше, имеется еще два контроллера для обслуживания этой статической страницы. Вот что мы используем для обработки запросов к статической странице HTML:
app.get('/chat', function(req, res){ res.sendFile(__dirname + '/index.html'); }); app.get('/login', function(req, res){ res.sendFile(__dirname + '/login.html'); });
В ответ на запросы к этой конечной точке отсылаются index.html и login.html при помощи res.sendFile.
Поскольку оба index.html и login.html находятся в той же папке, что и server.js, то мы использовали __dirname перед именем файла HTML.
2.4. Клиентская часть
На странице клиентской стороны я использовал Bootstrap (* веб-фреймворк для создания дизайна веб-сайтов и веб-приложений), и нет необходимости объяснять, как я это сделал. Я просто подключил к текстовому окну функцию, и при каждом нажатии клавиши Enter или кнопки Send сообщение отсылается к веб-службе на стороне сервера.
Для этой страницы также необходим файл Javascript, где реализуется функционал Socket.IO – прослушивание канала под названием message
. Модуль Socket.IO уже подключен на стороне сервера, и и когда вы его используете там, то автоматически добавляется конечная точка для обслуживания файла Javascript Socket.IO (* на клиентской части), и мы получаем его c CDN (* content delivery network – сеть доставки (распространения) контента) – <script src="//cdn.socket.io/socket.io-1.3.5.js">
.
<script> var socket = io(); socket.on("message", function (messages) { refreshMessages(messages); }); function refreshMessages(messages) { $(".media-list").html(""); $.each(messages.reverse(), function(i, message) { $(".media-list").append('<li class="media"><div class="media-body"><div class="media"><div class="media-body">' + message.message + '<br/><small class="text-muted">' + message.author + ' | ' + message.createDate + '</small><hr/></div></div></div></li>'); }); } $(function(){ if (typeof $.cookie("realtime-chat-nickname") === 'undefined') { window.location = "/login" } else { $.get("/messages", function (messages) { refreshMessages(messages) }); $("#sendMessage").on("click", function() { sendMessage() }); $('#messageText').keyup(function(e){ if(e.keyCode == 13) { sendMessage(); } }); } function sendMessage() { $container = $('.media-list'); $container[0].scrollTop = $container[0].scrollHeight; var message = $("#messageText").val(); var author = $.cookie("realtime-chat-nickname"); $.post( "/messages", {message: message, author: author}, function( data ) { $("#messageText").val("") }); $container.animate({ scrollTop: $container[0].scrollHeight }, "slow"); } }) </script>
Происходит еще одна проверка в коде выше – проверка куки-файла. Если вы не выбрали никакого ника для чата, то это означает, что в куки не указан ник, и вы будете перенаправлены на страницу входа.
В ином случае будут запрошены пять последних сообщений при помощи простого запроса AJAX к конечно точке /messages
. Подобным образом каждый раз при нажатии кнопки Send или клавиши Enter текстовое сообщение будет извлечено из текстового поля и ник будет извлечен из куки, а далее эти значения будут отосланы на сервер по методу POST. В приложении не осуществляется строгой проверки ника, поскольку я хотел бы сосредоточиться на реализации части, посвященной коммуникации в режиме реального времени, а не на разборе механизма аутентификации.
Как вы видите, в целом структура проекта очень проста. Давайте перейдем к развертыванию приложения. Как я сказал выше мы будем использовать Modulus, одну из лучших PaaS для размещения, масштабирования и мониторинга вашего приложения на языке вашего выбора.
3. Развертывание
3.1. Предварительная подготовка
Первое, что приходит в голову – это показать вам, как развертывать приложение, однако нам необходимо для начала разобраться с базой данных. Давайте посмотрим, как создавать базу данных на Modulus и затем осуществим развертывание.
Перейдите в инструментальную панель после создания аккаунта. Кликните по меню Databases и далее – по Create Database.

Заполните необходимые поля в выскочившей форме как показано ниже.

После заполнения необходимых полей и нажатия кнопки Create для вас будет создана база данных MongoDB и вы увидите URL вашей база данных на экране. Мы будем использовать MONGO URI, поэтому скопируйте этот URI.

В нашем проекте Mongo URI получаем из переменной окружения MONGO_URI
, и вам необходимо задать значение этой переменной окружения в инструментальной панели. Перейдите в инструментальную панель, кликните меню Projects, выберите ваш проект в списке и щелкните Administration в левом меню. На этой странице вы увидите раздел для переменных окружения после прокрутки страницы как показано ниже:

Вы можете развернуть проект на Modulus двумя способами:
- закачать проект в виде ZIP-файла при помощи инструментальной панели
- развернуть при помощи консоли, используя CLI (* command-line interface – интерфейс командной строки) Modulus
Я выбрал второй вариант, поскольку первым легко воспользоваться. Для начала установите CLI Modulus:
npm install -g modulus
Перейдите в папку проекта и выполните следующую команду для входа в Modulus.
modulus login
После выполнения вышеуказанной команды вас попросят ввести username и password:

Если вы создали аккаунт при помощи GitHub, то можете использовать опцию --github.
modulus login --github
Теперь вы вошли в Modulus, и пришло время создать проект. Используйте следующую команду для создания проекта:
modulus project create "Realtime Chat"
После этого вас попросят выбрать среду исполнения. Выберите первый вариант, то есть Node.js, и далее вам будет предложено ввести размер servo, который можете оставить по умолчанию.

Мы создали проект, и теперь мы развернем наш текущий проект на Modulus. Выполните следующую команду, чтобы передать текущий проект в проект Realtime Chat на стороне Modulus.
modulus deploy
В результате ваш проект будет развернут на Modulus и вы получите URL вашего запущенного проекта в конце сообщения об успешном размещении:
Realtime Chat running at realtime-chat-46792.onmodulus.net
Как видите, развернуть проект на Modulus очень просто!
CLI Modulus предлагает очень полезные команды для использования во время развертывания проекта или в период работы. Например, чтобы присоединить записи журнала регистрации вашего запущенного проекта, вы можете воспользоваться командой modulus project logs tail
, для создания базы данных MongoDB используйте modulus mongo create <db-name>,
чтобы задать значение переменной окружения воспользуйтесь modulus env set <key><value>
и т. д. Вы можете ознакомиться со всем списком команд при помощи modulus help.
Заключение
Главной целью этого руководства было показать вам, как создавать приложение для чата при помощи Node.js, Socket.IO и MongoDB. Для запуска проекта для публичного доступа мы использовали в качестве провайдера PaaS Modulus. Развернуть проект на Modulus очень просто, также она предоставляет собственную базу данных (MongoDB) для наших проектов. Кроме этого вы можете использовать очень полезные инструменты инструментальной панели Modulus, например: Logs (журнал регистрации [событий]), Notifications (извещения), Auto-Scaling (авто-масштабирование), Database Administration (администрирование базой данных) и т. д.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post