This sponsored post features a product relevant to our readers while meeting our editorial guidelines for being objective and educational.
() translation by (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.
1. Сценарій (* приклад використання системи)
- Джон хоче скористатися нашим додатком і відкриває його в браузері.
- На першій сторінці він обирає нік (* псевдонім), який використовує під чат чату, і входить до чату.
- У текстовому полі він щось пише та натискає Enter.
- Текст відсилається RESTful веб-сервіс (* веб-сервіс, побудований згідно з REST (Representational State Transfer – передача репрезентативного стану)) і цей текст зберігається до MongoDB.
- Перед зберіганням до MongoDB тий же самий текст буде передано одержувачам, що в той час також знаходяться в чаті.
Як ви бачите, це буде дуже простий додаток, але при його створенні ми розглянемо майже все, що необхідно для створення веб-застосунків. У цьому застосуванні не буде системи каналів (* група повідомлень певною тематики), але ви можете форкнути (* створити власну копію) початковий код і реалізувати модуль для каналів, щоб попрактикуватися.
2. Розробка додатка з нуля
Я спробую пояснити невеликі частини проекту та об'єднаю їх наприкінці посібника. Почнемо з розробки серверної частини додатка, потім перейдемо до клієнтської. Що ж, давайте почнемо з визначення об'єктів доменів (* метедані, що абстрактно описують стовбець таблиці, включаючи перевірки та обмеження) моделі MongoDB.
2.1. Модель
Для полегшення роботи з базою даних ми будемо використовувати Mongoose (* ODM (Object Document Mapper – об'єктно-документний відображувач), інструмент для моделювання в Node.js). У цьому посібнику у нас є тільки одна модель – Message
. Ця модель повідомлень містить тільки поля text
, createDate
і author. У нас відсутня модель для автора (така як User
), оскільки ми реалізуємо систему реєстрації/входу не повністю. У додатку буде проста сторінка, на якій відображуються ніки учасників, і такий нік буде збережено до кукі (* підтримуваний протоколом HTTP текстовий запис розміром до 4 Кбайт із даними про користувача, що повертає веб-сервер під час реєстрації користувача, який зберігається на його ПК. У цей рядок потрапляє інформація, зібрана сервером про користувача). Нік буде використовуватися в моделі Message
для наповнення поля author
. Ви можете подивитися на приклад зразка моделі в форматі JSON нижче:
1 |
{
|
2 |
text: "Hi, is there any Full Stack Developer here?" |
3 |
author: "john_the_full_stack", |
4 |
createDate: "2015.05.15" |
5 |
}
|
Для створення подібних документів ви можете створити модель за допомогою функцій Mongoose, наведених нижче:
1 |
var mongoose = require('mongoose') |
2 |
|
3 |
var Message = new mongoose.Schema({ |
4 |
author: String, |
5 |
message: String, |
6 |
createDate: { |
7 |
type: Date, |
8 |
default: Date.now |
9 |
}
|
10 |
});
|
11 |
|
12 |
mongoose.model('Message', Message) |
Просто імпортуйте модуль Mongoose, визначте поля (та їх атрибути) вашої моделі у форматі JSON та створіть модель під назвою Message
. Ця модель буде використовуватися на сторінках де необхідно.
Можливо у вас виникло питання, навіщо ми зберігаємо повідомлення до бази даних, якщо ми вже передали це повідомлення адресату в тому ж каналі. Дійсно, вам не потрібно зберігати повідомлення з чату, проте я хотів би пояснити, як взаємодіяти з базою даних. Щоб там не було, ми будемо використовувати цю модель у нашому проекті всередині контролерів. Контролерів?
2.2. Контролер
Як я згадував вище, ми будемо використовувати Express для реалізації MVC. І С
означає контролер
. У нашому проекті буде тільки дві кінцеві точки (* окремі функції API) для роботи з повідомленнями. В одній із них відбувається завантаження недавніх повідомлень чату, а в іншій – зберігання надісланих повідомлень до бази даних та подальша передача їх до каналу.
1 |
.....
|
2 |
app.get('/chat', function(req, res){ |
3 |
res.sendFile(__dirname + '/index.html'); |
4 |
});
|
5 |
|
6 |
app.get('/login', function(req, res){ |
7 |
res.sendFile(__dirname + '/login.html'); |
8 |
});
|
9 |
|
10 |
app.post('/messages', function(req, res, next) { |
11 |
var message = req.body.message; |
12 |
var author = req.body.author; |
13 |
var messageModel = new Message(); |
14 |
messageModel.author = author; |
15 |
messageModel.message = message; |
16 |
messageModel.save(function (err, result) { |
17 |
if (!err) { |
18 |
Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) { |
19 |
io.emit("message", messages); |
20 |
});
|
21 |
res.send("Message Sent!"); |
22 |
} else { |
23 |
res.send("Technical error occurred!"); |
24 |
}
|
25 |
});
|
26 |
});
|
27 |
|
28 |
app.get('/messages', function(req, res, next) { |
29 |
Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) { |
30 |
res.json(messages); |
31 |
});
|
32 |
});
|
33 |
.....
|
Завдяки першому та другому контролерам надаються статичні файли HTML для сторінок входу та чату. Третій оброблює POST–запити до кінцевої точки /messages,
де створюються та зберігаються нові повідомлення. У цьому контролері спочатку створюється зразок моделі Message з тіла запиту, і потім він зберігається до бази даних за допомогою функції Mongoose save
.
Я не буду вдаватися в подробиці роботи з Mongoose. Ви можете з ними ознайомитися в документації. Ви можете додати функцію зворотного виклику до функції save для перевірки наявності яких-небудь проблем. Якщо все вдало, то ми добуваємо останні п'ять записів, які відсортовано за зменшенням за допомогою createDate
(* дата створення), и передамо п'ять повідомлень учасникам каналу.
Що ж, ми закінчили з MC
. Тепер давайте перейдемо до представлення
.
2.3. Представлення
Як правило, в Express можуть використовуватися шаблонізатори, наприклад: Jade, EJS, Handlebars тощо. Проте у нас є тільки одна сторінка, і це – сторінка з повідомленнями, тому ми обробляємо запити до неї статично. Власне, як я сказав раніше, мається ще два контролери для обслуговування цієї статичної сторінки. Ось що ми використовуємо для обробки запитів до статичної сторінки HTML:
1 |
app.get('/chat', function(req, res){ |
2 |
res.sendFile(__dirname + '/index.html'); |
3 |
});
|
4 |
|
5 |
app.get('/login', function(req, res){ |
6 |
res.sendFile(__dirname + '/login.html'); |
7 |
});
|
У відповідь на запити до цієї кінцевої точки відсилаються 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 (* на клієнтській частині), і ми отримуємо його з CDN (* content delivery network – мережа доставки (розповсюдження) контенту) – <script src="//cdn.socket.io/socket.io-1.3.5.js"></script>. При кожному надходженні повідомлення до цього каналу воно буде автоматично відстежено, і список повідомлень буде оновлено за рахунок останніх п'яти повідомлень.
1 |
<script> |
2 |
var socket = io(); |
3 |
socket.on("message", function (messages) { |
4 |
refreshMessages(messages); |
5 |
});
|
6 |
|
7 |
function refreshMessages(messages) { |
8 |
$(".media-list").html(""); |
9 |
$.each(messages.reverse(), function(i, message) { |
10 |
$(".media-list").append('<li class="media"><div class="media-body"><div class="media"><div class="media-body">' |
11 |
+ message.message + '<br/><small class="text-muted">' + message.author + ' | ' + message.createDate + '</small><hr/></div></div></div></li>'); |
12 |
});
|
13 |
}
|
14 |
|
15 |
$(function(){ |
16 |
|
17 |
if (typeof $.cookie("realtime-chat-nickname") === 'undefined') { |
18 |
window.location = "/login" |
19 |
} else { |
20 |
$.get("/messages", function (messages) { |
21 |
refreshMessages(messages) |
22 |
});
|
23 |
|
24 |
$("#sendMessage").on("click", function() { |
25 |
sendMessage() |
26 |
});
|
27 |
|
28 |
$('#messageText').keyup(function(e){ |
29 |
if(e.keyCode == 13) |
30 |
{
|
31 |
sendMessage(); |
32 |
}
|
33 |
});
|
34 |
}
|
35 |
|
36 |
function sendMessage() { |
37 |
$container = $('.media-list'); |
38 |
$container[0].scrollTop = $container[0].scrollHeight; |
39 |
var message = $("#messageText").val(); |
40 |
var author = $.cookie("realtime-chat-nickname"); |
41 |
$.post( "/messages", {message: message, author: author}, function( data ) { |
42 |
$("#messageText").val("") |
43 |
});
|
44 |
$container.animate({ scrollTop: $container[0].scrollHeight }, "slow"); |
45 |
}
|
46 |
})
|
47 |
</script> |
Відбувається це одна перевірка в коді вище – перевірка кукі-файлу. Якщо ви не вибрали ніякого ніку для чату, то це означає, що в кукі не вказано нік, і ви будете перенаправлені на сторінку входу.
Інакше буде запитано п'ять останніх повідомлень за допомогою простого запиту AJAX до кінцевої точки /messages.
Подібним чином кожного разу при натисненні кнопки Send чи клавіши Enter текстове повідомлення буде добуто з текстового поля і нік буде добуто з кукі, а потім ці значення будуть відправлено до сервера методом POST. У додатку не відбувається строгої перевірки ніку, оскільки я хотів би зосередитися на реалізації частини, що присвячена комунікації в режимі реального часу, а не на розробці механізму автентифікації.
Як бачите, в цілому структура проекту дуже проста. Давайте перейдемо до розгортання додатка. Як я сказав вище ми будемо використовувати Modulus, одну з найкращих PaaS для розміщення, масштабування та моніторингу вашого додатка на мові вашого вибору.
3. Розгортання
3.1. Попередні підготування
Перше, що спадає на думку – це показати вам, як розгортати додаток, проте нам необхідно для початку розібратися з базою даних. Давайте подивимося, як створити базу даних на Modulus і потім здійснимо розгортання.
Перейдіть до інструментальної панелі після створення акаунта. Клацніть по меню Databases і далі – по Create Database.



Заповніть необхідні поля у формі, що з'явилася як показано нижче.



Після заповнення необхідних полів та натиснення кнопки Create для вас буде створено базу даних MongoDB і ви побачите URL-адресу вашої бази даних на екрані. Ми будемо використовувати MONGO URI, тому скопіюйте цей URI.1



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



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



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



Ми створили проект, і тепер ми розгорнемо наш поточний проект на Modulus. Виконайте наступну команду, щоб передати поточний проект до проекту Realtime Chat на стороні Modulus.
1 |
modulus deploy |
У результаті ваш проект біде розгорнуто на Modulus і ви отримаєте URL вашого запущеного проекту наприкінці повідомлення про вдале розміщення.
1 |
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. Ви можете ознайомитися з повним списком за допомогою modulus help.
Завершення
Головна ціль цього посібника – показати вам, як створювати додатки для чату за допомогою Node.js, Socket.IO і MongoDB. Для запуску проекту до публічного доступу ми використовували в якості провайдера PaaS Modulus. Розгорнути проект на Modulus дуже просто, також вона надає власну базу даних (MongoDB) для наших проектів. Окрім цього ви можете використовувати дуже корисні інструменти інструментальної панелі Modulus, наприклад: Logs (журнал реєстрації [подій]), Notifications (повідомлення), Auto-Scaling (авто-масштабування), Database Administration (адміністрування базою даних) тощо.