First of all, let me show you the final look of the application that we will have at the end of the article.
1. Scenario
- John wants to use our application, and opens it in the browser.
- On the first page, he selects a nickname use during chat, and logs in to chat.
- In the text area he writes something and presses Enter.
- The text is sent to a RESTful service (Express) and this text is written to MongoDB.
- Before writing in MongoDB, the same text will be broadcast to the users that are currently logged in to the chat app.
2. Project Design From Scratch
I will try to explain the small pieces of the project first and combine them at the end. I will start from the back end to the front end. So, let's start with the domain objects (MongoDB models).
2.1. Model
For database abstraction, we will use Mongoose. In this project, we have only one model called Message. This message model only contains
text
, createDate
, and author. There is no model for the author like User, because we will not fully implement a user registration/login system. There will be a simple nickname-providing page, and this nickname will be saved to a cookie. This will be used in the Message model as text in the author field. You can see an example JSON model below:- {
- text: "Hi, is there any Full Stack Developer here?"
- author: "john_the_full_stack",
- createDate: "2015.05.15"
- }
- var mongoose = require('mongoose')
- var Message = new mongoose.Schema({
- author: String,
- message: String,
- createDate: {
- type: Date,
- default: Date.now
- }
- });
- mongoose.model('Message', Message)
Maybe you have a question about why we are storing the message in the database, when we already broadcast this message to the user in the same channel. It's true that you do not have to store chat messages, but I just wanted to explain the database integration layer. Anyway, we will use this model in our project inside the controllers. Controllers?
2.2. Controller
As I said earlier, we will use Express for the MVC part. And C here stands for the Controller. For our projects, there will be only two endpoints for messaging. One of them is for loading recent chat messages, and the second one is for handling sent chat messages to store in the database, and then broadcast into the channel.
- .....
- 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);
- });
- });
- .....
I will not dive into Mongoose very much—you can have a look at the documentation for further details. You can provide a callback function for the save function to check whether there is any problem or not. If it is successful, we have fetched the last five records sorted in descending order by createDate, and have broadcast five messages to the clients in the channel.
Ok, we have finished MC. Let's switch to the View part.
2.3. View
In general, a template engine like Jade, EJS, Handlebars, etc., can be used within Express. However, we have only one page, and that is a chat message, so I will serve this statically. Actually, as I said above, there are two more controllers to serve this static HTML page. You can see the following for serving a static HTML page.
- app.get('/chat', function(req, res){
- res.sendFile(__dirname + '/index.html');
- });
- app.get('/login', function(req, res){
- res.sendFile(__dirname + '/login.html');
- });
2.4. Front End
In the front-end page, I have used Bootstrap and there is no need to explain how I managed to do that. Simply, I have bound a function to a text box, and whenever you press the Enter key or Send button, the message will be sent to the back-end service.
This page also has a required js file of Socket.IO to listen to the channel called message. The Socket.IO module is already imported in the back end, and when you use this module in the server side, it automatically adds an endpoint for serving the Socket.IO js file, but we use the one that is served from cdn <script src="//cdn.socket.io/socket.io-1.3.5.js"></script>. Whenever a new message comes in to this channel, it will automatically be detected and the message list will be refreshed with the last five messages.
- <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>
If not, the last five messages will be fetched by a simple Ajax call to the /messages endpoint. In the same way, whenever you click the Send button or press the Enter key, the text message will be fetched from the text box, and the nickname will be fetched from the cookie, and those values will be sent to the server with a post request. There is no strict check for the nickname here, because I wanted to focus on the real-time part, not the user authentication part.
As you can see, the overall structure of the project is very simple. Let's come to the deployment part. As I said earlier, we will use Modulus, one of the best PaaS for deploying, scaling and monitoring your application in the language of your choice.
(continue)
If you found this post interesting, follow and support us.
Suggest for you:
Complete Node JS Developer Course Building 5 Real World Apps
Node.js Tutorials: The Web Developer Bootcamp
Learn How To Deploy Node.Js App on Google Compute Engine
Learn and Understand NodeJS
Learn Nodejs by Building 12 Projects
No comments:
Post a Comment