Real-Time Communication Between Frontend And Backend
Tags: php, node, redis, javascript
Published: 2023-01-09 17:13:07

Communication between frontend and backend is usually handled by the HTTP protocol. Frontend sends a request to change resource or retrieve data, while backend returns appropriate results.

In this form, when any changes occur on the backend, the frontend is not aware of them. As a result, in order to stay up-to-date, it is necessary to make requests at regular intervals and check if something has changed. This is not very convenient for users and unnecessarily burdens servers.

An alternative method is to use two-way communication, such as with sockets. Then, when a resource changes on the backend side, we can send a message to the frontend. This can be a changed value or an info that something has changed and a request has to be made to be up-to-date. This way, the frontend will be notified when something actually changes and will not make unnecessary requests.

In this post I will show you how to implement such communication between a backend application written in PHP and a simple frontend application. Node.js will be used as a mediator between the frontend and backend applications because it is easy and efficient to write an application in this technology.

We have to create three applications:

- Frontend (JavaScript) 
- Node.js (Socket Server) 
- Backend (PHP).

The frontend application will directly connect to the Node.js application and listen for messages using the `socket.io` library. However, how would we send messages from PHP to Node.js? For this purpose, I will use Redis and its Pub/Sub feature. The backend will publish an event to Redis, while Node app will consume the event and forward it to the frontend clients.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jn9pgt2wkhapkoujmf15.png)

# PHP application

As mentioned above, to communicate between PHP and Node.js we are going use Redis. Therefore, we will need a library to handle the connection with this tool, such as Predis.

To install Predis in PHP app, we have to run below command:

```shell
composer require predis/predis
```

To publish an event, we need to call the `publish` method on the Client object. The first argument is the channel, and the second is the payload.

```php
$client = new \Predis\Client([
    'scheme' => 'tcp',
    'host' => 'redis',
    'port' => 6379
]);
 
$client->publish(channel: 'test', message: 'message');
```

# Setting up Node.js application

To begin with, let's create a `package.json` file which includes the following dependencies:

- `express` - a web framework
- `redis` - a library for connecting to Redis and listening for events from the PHP application
- `socket.io` - a library for socket communication

The file should look like this:

```json
{
  "name": "sockets",
  "version": "1.0.0",
  "main": "app.js",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2",
    "redis": "^4.5.1",
    "socket.io": "^4.5.4"
  }
}
```

Next, create the Node.js application:

```javascript
const express = require('express');
const app = express();

const http = require('http');
const socketIo = require("socket.io");
const redis = require('redis');

const server = http.createServer(app);
const io = socketIo(server, {
    cors: {
        origin: '*'
    }
});

server.listen(5000);

io.on('connection', socket => {
    const subscriber = getRedisClient();

    (async () => {
        await subscriber.connect();
    })();

    socket.on('subscribe.test', () => {
        subscriber.subscribe('test', (message, channel) => {
            console.log(`Received ${message} from ${channel}`);

            socket.emit(channel, message);
        });

        console.log('Subscribed to test');
        socket.emit('test', 'Subscribed to test')
    });

    socket.on('disconnect', () => {
        console.log('Disconnected');
        subscriber.quit();
    });

    socket.emit('connected');
});

const getRedisClient = () => {
    const redisClient = redis.createClient({
        socket: {
            host: 'redis',
            port: 6379
        },
    });

    redisClient
        .on('error', function (err) {
            console.error('Redis error: ', err);
        });

    redisClient.on('ready', () => {
        console.log("Connected to Redis!");
    });

    return redisClient;
}
```

This app creates a server that will listen on port 5000. After the client connects to the sockets, several things happen. First, a connection to Redis is established. When a message is received from Redis, it will be sent to the frontend application with the same channel name and payload.

Next, the socket listens for a message with the key `subscribe.test`. If the frontend sends such a message, Redis will start listening for messages on the test channel and forwarding them to the frontend.

When the client disconnects, the Redis connection is closed. 

After establishing the connection and setting up event listeners, the app responds to the client with the message `connected` to inform them that the connection was successful.

To start the application simply run `node app.js` command.

# Frontend application

Handling sockets on the frontend is really simple. All we need is a simple library - socket.io - which has been already used in the Node app. I created a simple html file where I import this library from a CDN server.

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.5.4/socket.io.js"></script>
</head>
<body>
  <script>
    const socket = io('http://localhost:5000');

    socket.on('connected', () => {
        console.log('Connected to sockets');
        socket.emit('subscribe.test');
    });

    socket.on('test', (event) => {
        console.log('Message from socket server');
        console.log(event);
    });

    // socket.disconnect();
  </script>
</body>
</html>
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qb39qyvdfnqf4u20w3il.png)

# Summary

Providing basic communication with sockets is very straightforward. It allows users to be notified of events as they occur, without the need to reload the page which makes the user experience better. An additional benefit is that we can save computing power on the backend servers because data refresh will only happen when it is actually necessary.