Unlocking real-time capabilities in web applications with Symfony and Mercure

David Garcia
6 min readMay 15

--

TL;DR: There is a Demo Project at the end of this article

In the age of instant communication, real-time functionality is a must-have feature for modern web applications. Users expect seamless interactions and immediate updates, whether chatting with friends, tracking deliveries, or monitoring the stock market.

One popular way to implement real-time functionality in web applications is by using the Symfony PHP Framework and Mercure software.

In this article, I will delve into what Symfony and Mercure are, why they work well together, explore a few use cases where this integration shines, and provide a sample code to get you started.

What is Symfony?

Symfony is a leading PHP framework used for building web applications and APIs. It offers a structured and efficient approach to development by providing reusable components, best practices, and a developer-friendly environment.

Symfony is designed to be flexible and extensible, allowing you to build applications of any size and complexity. Its modular architecture lets developers choose the necessary components, speeding up the development process and promoting maintainability.

What is Mercure?

Mercure is a protocol and open-source software that enables real-time updates between the server and clients using the Publish-Subscribe pattern. It provides a simple and efficient way to push data updates to web browsers, mobile apps, and other services without requiring the clients to request the data continually.

Mercure creates a separate server called the Mercure hub, which manages and broadcasts updates to connected clients over an HTTP or HTTPS connection.

Why Symfony and Mercure work well together

Symfony and Mercure are a powerful combination for creating real-time web applications. The Symfony framework offers a built-in Mercure component that simplifies using Mercure in your application, providing a seamless experience for developers and users. Symfony’s event-driven architecture and reusable components integrate smoothly with Mercure’s publish-subscribe pattern.

Use cases for integrating Symfony and Mercure

  • Live notifications: Notify users of important updates or events, such as new messages, friend requests, or system alerts, in real-time.
  • Chat applications: Implement a chat system that allows users to exchange messages instantly without the need for frequent polling.
  • Real-time dashboards: Display up-to-date analytics, performance metrics, or other relevant data that requires real-time monitoring.
  • Collaborative editing: Enable multiple users to edit a document simultaneously, with live updates reflecting each user’s changes.
  • Online gaming: Create interactive games where players can see each other’s moves and actions in real-time.

Sample code to integrate Symfony and Mercure

Step 1: Install the required packages

To begin, you need to install the Symfony Mercure and the Symfony Messenger components:

composer require symfony/mercure

Step 2: Configure the Mercure hub

Next, configure the Mercure hub by adding the following environment variable to your .env file:

# The URL of the Mercure hub, used by the app to publish updates.
# If you run Mercure through a Docker Container, use the service name.
MERCURE_URL=http://mercure/.well-known/mercure

# The public URL of the Mercure hub, used by the browser to connect.
# This needs to be a public URL for your browser, as it connects via JS.
MERCURE_PUBLIC_URL=http://localhost:8080/.well-known/mercure

# The secret used to sign the JWTs.
# This is the same value set when booting the Docker Container or
# when configuring the Mercure software.
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"

Step 3: Dispatch the message

You can now publish the Mercure event in your controller or service into a unique topic name. This topic should be unique per user so that we can load the events for the logged-in user but not data from other users:

// src/Controller/YourController.php

use App\Message\UpdateMessage;
use Symfony\Component\Messenger\MessageBusInterface;

class PublishController extends AbstractController
{
#[Route(path: '/mercure/publish', name: 'mercure_publish', methods: ['GET'])]
public function __invoke(HubInterface $hub): JsonResponse
{
// Mercure Resource (ideally it should be unique per user)
$topic = 'demo-topic';

// Data pushed to the Mercure Resource
$arrayData = [
'datetime' => (new \DateTime('now', new \DateTimeZone('UTC')))->format(\DateTime::ATOM),
'timezone' => 'UTC',
];
$jsonData = json_encode($arrayData, JSON_THROW_ON_ERROR);

// Mercure Data Wrapper
$update = new Update($topic, $jsonData);

// Mercure call
$hub->publish($update);

// Confirm data has been published
return new JsonResponse(json_encode([
'status' => 'published',
'topic' => $topic,
'data' => $arrayData,
], JSON_THROW_ON_ERROR));
}
}

Step 4: Subscribe to updates on the frontend

Finally, in your front-end web app, use JavaScript to subscribe to the updates:

<script>
// This URL must be the same value set for the MERCURE_PUBLIC_URL
// environment variable. It must also contain the topic name,
// that is unique per user.
const url = new URL('http://localhost:8080/.well-known/mercure');
url.searchParams.append('topic', 'your/demo/topic');

// Now we can load the EventSource targeting the Mercure server
// and the unique topic per user
const eventSource = new EventSource(url);

// And, finally, we can process the data to use it as needed
eventSource.onmessage = event => {
const data = JSON.parse(event.data);
// Process the data as needed
};
</script>

If you are using a Symfony Twig Template, then you can use the Twig function to prepare the URL:

<script>
// The Symfony/Mercure component includes a Twig helper to build the URL
// for you, so there is no need to create and prepare a URL manually.
const eventSource = new EventSource("{{ mercure('your/demo/topic') }}");

// Then, process the data to use it as needed
eventSource.onmessage = event => {
const data = JSON.parse(event.data);
// Process the data as needed
};
</script>

Conclusion

With Symfony and Mercure, you can easily implement real-time functionality in your web applications. The powerful combination of Symfony’s event-driven architecture and Mercure’s publish-subscribe pattern allows for seamless integration and a smooth developer experience. By following the steps outlined in this article, you can begin to unlock the full potential of real-time web applications for your users.

However, building a real-time mechanism that loads the Mercure event consumer will not provide much functionality unless the user does not browse to any other page/URL. So, this approach can be used for direct messaging windows or as part of a SPA frontend.

Resources:

Demo project:

Official websites:

Images:

--

--

David Garcia

Senior Software Engineer, Backend, NodeJS & Symfony developer, workaholic, passionate for new technologies and OSS contributor. https://ko-fi.com/davidgarciacat