Comment créer une application en temps réel à l'aide de Socket.io, React, Node et MongoDB

Vous êtes-vous déjà demandé comment les applications en temps réel sont créées? Avez-vous déjà remarqué l'importance et les cas d'utilisation des applications temps réel?

Si vous êtes curieux de connaître les questions ci-dessus et avez besoin d'une réponse, cet article de blog est pour vous.

Tout d'abord, identifions quelques cas d'utilisation nécessitant des applications en temps réel:

  1. Obtenir des mises à jour de l'emplacement de votre taxi sur une carte d'une application de réservation de taxi.
  2. Obtenir de nouveaux messages instantanément sur votre application de chat préférée.
  3. Mise à jour des informations de commande de nourriture dans la cuisine de votre restaurant préféré.

Ce sont tous les scénarios courants de notre vie quotidienne où nous ne pouvons tolérer un retard dans la mise à jour des informations et avons donc besoin d'une communication en temps réel.

Les technologies qui peuvent être utilisées pour la communication en temps réel sont:

  1. Sondage court : AJAX, crée un trafic intense.
  2. Interrogation longue : comme AJAX, mais le serveur maintient la réponse jusqu'à ce qu'il ait une mise à jour. Après l'avoir reçu, le client envoie une autre demande et a besoin d'un en-tête supplémentaire pour être parcouru d'avant en arrière, ce qui entraîne une surcharge supplémentaire.
  3. Web Sockets : permettent d'ouvrir une communication interactive entre le client et le serveur. On peut envoyer une demande au serveur et recevoir des réponses basées sur des événements sans interroger le serveur pour une réponse, ce qui fait des sockets Web un meilleur choix pour notre cas d'utilisation.

Des informations plus détaillées sur les trois technologies ci-dessus peuvent être lues ici.

Nous allons apprendre à créer une application en temps réel en couvrant le scénario suivant.

Imaginez que vous êtes assis dans votre restaurant préféré et avez un menu numérique. Vous passez la commande et la cuisine est mise à jour de votre commande en temps réel. Lorsque la cuisine est terminée avec la commande, ils la mettent également à jour en temps réel.

Caractéristiques en détail:

  1. Passer la commande : Interface pour sélectionner la quantité et passer la commande d'un aliment sélectionné à la cuisine.
  2. Cuisine : Interface qui peut être ouverte sur plusieurs cuisines et met à jour en temps réel les chefs et cuisiniers concernant le total des commandes créées et la quantité prévue de produits alimentaires, leur donnant la flexibilité de la mettre à jour. Dispose également d'une fonctionnalité pour télécharger le rapport sous la forme d'une feuille Excel.
  3. Changement prévu : interface pour mettre à jour la quantité prévue de produits alimentaires.

Une démo en direct de ce scénario peut être trouvée ici.

Pour une meilleure compréhension, ouvrez-le dans différents onglets / appareils en même temps pour voir les données changer en temps réel.

Le code source est ici. N'hésitez pas à faire quelque chose d'innovant / utile en plus.

Alors, commençons.

Pile technologique:

Frontend : React.js, Reactstrap, Socket.io

Backend : Node.js (Express), MongoDB, Socket.io

Structure des dossiers:

/* Go to the root directory in the source code and find out the below-mentioned files. This architecture helps in creating a big modular App. */ backend-my-app/ /* Backend code of the app */ server.js /* Socket and backend code resides here*/ build/ /* Optional for deployment of Frontend Build */ package.json /* Backend dependency */ ... public/ src/ /* Frontend Sourcecode */ global/ /* Components getting used everywhere */ header.css header.js main/ Kitchen.js PlaceOrder.js UpdatePredicted.js App.js /* Routing logic and component assembly part */ package.json /* Frontend dependency */ ............

Explication du code source:

L'extrémité avant:

git clone //github.com/honey93/OrderKitchen.git cd OrderKitchen npm install npm start

Forfaits utilisés:

  1. Reactstrap: composants bootstrap4 faciles à utiliser
  2. Socket.io: Socket.io est une bibliothèque qui permet une communication en temps réel, bidirectionnelle et basée sur les événements entre le navigateur et le serveur.
  3. react-html-table-to-excel: fournit une génération côté client d'un fichier Excel (.xls) à partir d'un élément de table HTML.
  4. react-router-dom: liaisons DOM pour le routeur react. Il se compose de nombreux composants importants tels que BrowserRouter utilisés lorsqu'il existe un serveur pour gérer les requêtes dynamiques, les commutateurs, les routes, etc.

Composant d'application

Chemin : src / App.js

Ce composant contient la logique de routage principale du Frontend. Ce fichier est utilisé dans src / index.js dans le module Browser Router. Le code ci-dessous illustre l'une des approches permettant de garder votre application modulaire.

import React, { Component } from "react"; import "./App.css"; import { Header } from "./global/header"; import { Switch, Route } from "react-router-dom"; import PlaceOrder from "./main/PlaceOrder"; import UpdatePredicted from "./main/UpdatePredicted"; import Kitchen from "./main/Kitchen"; /*The  component is the main part of React Router. Anywhere that you want to only render content based on the location’s pathname, you should use a  element. */ /* The Route component expects a path prop, which is a string that describes the pathname that the route matches */ /* The  will iterate over routes and only render the first one that matches the current pathname */ class App extends Component { render() { return ( ); } } export default App;

Composant d'en-tête

Chemin : src / global / header.js

Ce composant sera commun et utilisé dans les sections telles que la commande, le changement prévu, la cuisine. Cette approche permet d'éviter la duplication de code et de maintenir l'application modulaire.

import React, { Component } from "react"; import { NavLink } from "react-router-dom"; import socketIOClient from "socket.io-client"; import "./header.css"; // The Header creates links that can be used to navigate // between routes. var socket; class Header extends Component { /* Creating a Socket client and exporting it at the end to be used across the Place Order, Kitchen, etc components*/ constructor() { super(); this.state = { endpoint: '//localhost:3001/' }; socket = socketIOClient(this.state.endpoint); } render() { return (   
  • Place Order
  • Change Predicted
  • Kitchen
); } } export { Header, socket };

Composant de cuisine

Chemin : src / main / Kitchen.js

La logique de l'interface utilisateur de Kitchen Screen et le code html résident dans ce composant:

import React, { Component } from "react"; import { Button, Table, Container } from "reactstrap"; import { socket } from "../global/header"; import ReactHTMLTableToExcel from "react-html-table-to-excel"; class Kitchen extends Component { constructor() { super(); this.state = { food_data: [] // this is where we are connecting to with sockets, }; } getData = foodItems => { console.log(foodItems); this.setState({ food_data: foodItems }); }; changeData = () => socket.emit("initial_data"); /*As soon as the component gets mounted ie in componentDidMount method, firing the initial_data event to get the data to initialize the Kitchen Dashboard */ /* Adding change_data listener for listening to any changes made by Place Order and Predicted Order components*/ componentDidMount() { var state_current = this; socket.emit("initial_data"); socket.on("get_data", this.getData); socket.on("change_data", this.changeData); } /* Removing the listener before unmounting the component in order to avoid addition of multiple listener at the time revisit*/ componentWillUnmount() { socket.off("get_data"); socket.off("change_data"); } /* When Done gets clicked, this function is called and mark_done event gets emitted which gets listened on the backend explained later on*/ markDone = id => { // console.log(predicted_details); socket.emit("mark_done", id); }; getFoodData() { return this.state.food_data.map(food => { return (  {food.name}  {food.ordQty}  {food.prodQty}  {food.predQty}   this.markDone(food._id)}>Done  ); }); } render() { return (  

Kitchen Area

{this.getFoodData()}
NameQuantityCreated Till NowPredictedStatus
); } } export default Kitchen;

Commander le composant

Path: src/main/PlaceOrder.js

import React, { Component } from "react"; import { Button, Table, Container } from "reactstrap"; import { socket } from "../global/header"; class PlaceOrder extends Component { constructor() { super(); this.state = { food_data: [] // this is where we are connecting to with sockets, }; } getData = foodItems => { console.log(foodItems); foodItems = foodItems.map(food => { food.order = 0; return food; }); this.setState({ food_data: foodItems }); }; componentDidMount() { socket.emit("initial_data"); var state_current = this; socket.on("get_data", state_current.getData); } componentWillUnmount() { socket.off("get_data", this.getData); } //Function to place the order. sendOrder = id => { var order_details; this.state.food_data.map(food => { if (food._id == id) { order_details = food; } return food; }); console.log(order_details); socket.emit("putOrder", order_details); var new_array = this.state.food_data.map(food => { food.order = 0; return food; }); this.setState({ food_data: new_array }); }; // Changing the quantity in the state which is emitted to the backend at the time of placing the order. changeQuantity = (event, foodid) => { if (parseInt(event.target.value)  { if (food._id == foodid) { food.order = parseInt(event.target.value); } return food; }); this.setState({ food_data: new_array }); }; // To get the initial data getFoodData() { return this.state.food_data.map(food => { return (  {food.name}   this.changeQuantity(e, food._id)} value={food.order} type="number" placeholder="Quantity" />  this.sendOrder(food._id)}>Order  ); }); } render() { return (  

Order Menu

{this.getFoodData()}
ProductQuantityOrder
); } } export default PlaceOrder;

One more section called Update Predicted Path: src/main/UpdatePredicted.js similar to above section is there in the code repository.

Backend

Starting the Backend:

cd backend-my-app npm install node server.js

Packages used:

  1. Monk: A tiny layer that provides simple yet substantial usability improvements for MongoDB usage within Node.JS.
  2. Socket.io: Socket.io is a library that enables real-time, bidirectional and event-based communication between the browser and the server.

3. Express: Fast, minimalist web framework for node.

Main Code

Path: backend-my-app/server.js

const express = require("express"); const http = require("http"); const socketIO = require("socket.io"); // Connection string of MongoDb database hosted on Mlab or locally var connection_string = "**********"; // Collection name should be "FoodItems", only one collection as of now. // Document format should be as mentioned below, at least one such document: // { // "_id": { // "$oid": "5c0a1bdfe7179a6ca0844567" // }, // "name": "Veg Roll", // "predQty": 100, // "prodQty": 295, // "ordQty": 1 // } const db = require("monk")(connection_string); const collection_foodItems = db.get("FoodItems"); // our localhost port const port = process.env.PORT || 3000; const app = express(); // our server instance const server = http.createServer(app); // This creates our socket using the instance of the server const io = socketIO(server); io.on("connection", socket => { // console.log("New client connected" + socket.id); //console.log(socket); // Returning the initial data of food menu from FoodItems collection socket.on("initial_data", () => { collection_foodItems.find({}).then(docs => { io.sockets.emit("get_data", docs); }); }); // Placing the order, gets called from /src/main/PlaceOrder.js of Frontend socket.on("putOrder", order => { collection_foodItems .update({ _id: order._id }, { $inc: { ordQty: order.order } }) .then(updatedDoc => { // Emitting event to update the Kitchen opened across the devices with the realtime order values io.sockets.emit("change_data"); }); }); // Order completion, gets called from /src/main/Kitchen.js socket.on("mark_done", id => { collection_foodItems .update({ _id: id }, { $inc: { ordQty: -1, prodQty: 1 } }) .then(updatedDoc => { //Updating the different Kitchen area with the current Status. io.sockets.emit("change_data"); }); }); // Functionality to change the predicted quantity value, called from /src/main/UpdatePredicted.js socket.on("ChangePred", predicted_data => { collection_foodItems .update( { _id: predicted_data._id }, { $set: { predQty: predicted_data.predQty } } ) .then(updatedDoc => { // Socket event to update the Predicted quantity across the Kitchen io.sockets.emit("change_data"); }); }); // disconnect is fired when a client leaves the server socket.on("disconnect", () => { console.log("user disconnected"); }); }); /* Below mentioned steps are performed to return the Frontend build of create-react-app from build folder of backend Comment it out if running locally*/ app.use(express.static("build")); app.use("/kitchen", express.static("build")); app.use("/updatepredicted", express.static("build")); server.listen(port, () => console.log(`Listening on port ${port}`));

Database: MongoDB

Mlab: Database as a service for MongoDB

Collection Name: FoodItems

Document format: At least one document is needed in the FoodItems collection with the below mentioned format.

{ "name": "Veg Roll", // Food Name "predQty": 100, // Predicted Quantity "prodQty": 295, // Produced Quantity "ordQty": 1 // Total Order Quantity }

Hope you got the understanding of how to create a modular real time app using the trending MERN stack. If you found it helpful clap below, give stars to the project repo and share with your friends too.