Mercure.rocks is a brand new protocol allowing to push data updates to web browsers and other HTTP clients in a convenient, fast, reliable and battery-efficient way. It is especially useful to publish real-time updates of resources served through web APIs, to reactive web and mobile apps.
Both Symfony and API Platform now have an official support for this protocol!
From the ground, Mercure has been designed to work with technologies not able to maintain persistent connections. It’s especially relevant in serverless environments, but is also convenient when using PHP or FastCGI scripts.
Mercure is basically a higher-level replacement for WebSocket. Unlike WebSocket, it is compatible with HTTP/2 and HTTP/3.
It has been designed with hypermedia APIs in mind, is auto-discoverable through the Web Linking RFC and is also compatible with GraphQL.
It natively supports authorization, reconnection in case of network issue (with refetching of missed events), subscribing to several topics, topics patterns (using templated URIs)…
Because it is built on top of Server-sent Events and plain old HTTP requests, it is already compatible with all modern browsers, and requires 0 client-side dependencies.
The protocol is open (available as an Internet Draft), and a reference open source implementation of the server written in Go is available.—
To create your initial project, you just have to describe the public structures of the data to expose through the API. API Platform will take care of exposing the web API and bootstrapping the clients to consume it: get started with API Platform.
Version 2.4 introduces a lot of very interesting new features. Here is the curated list:
Read and write support for MongoDB, the reference document database, including a lot of useful filters
Read support for Elasticsearch, the open source search and analytics engine, including filters for advanced search
Automatic “push” of updated resources from the server to the clients using the brand new Mercure protocol
Integration with the Symfony Messenger component to easily implement the CQRS pattern and to handle messages asynchronously (using brokers such as RabbitMQ, Apache Kafka, Amazon SQS or Google PubSub)
With 114 commits and 234 files changed over almost 3 years. This is one of the biggest contributions to the project.
The MongoDB integration relies on Doctrine MongoDB ODM 2.0 (currently in beta). To enable this feature, just install and configure DoctrineMongoDBBundle. API Platform will autodetect it. Then, create a class mapped with MongoDB, and mark it as an API resource:
The support for MongoDB leverages the flexibility of API Platform: it has been implemented as a data provider and a data persister. Relations, pagination as well as boolean, date, numeric, order, range and search filters are also supported!
A big thanks to all contributors of this amazing feature, and to Andreas Braun, the maintainer of Doctrine MongoDB ODM, for the in-depth reviews!
Elasticsearch is another very popular open-source data store. It allows to perform full-text searches and advanced analyzes on very large datasets. Orange has sponsored the development of an Elasticsearch data provider for API Platform, as well as some interesting search filters. The implementation has been realized by Baptiste Meyer (API Platform Core Team). Thanks to Orange, this feature is now available for everybody in API Platform 2.4.
To enable and configure the Elasticsearch support, refer to the official documentation. Then, a simple resource class corresponding to an Elasticsearch index is enough to benefit from the full power of API Platform:
Then, you can use an URL such as /tweets?message=foo to search using Elasticsearch.
Keep in mind that it’s your responsibility to populate your Elastic index. To do so, you can use Logstash, a custom data persister or any other mechanism that fits for your project (such as an ETL).
Baptiste also took this opportunity to improve the code handling the pagination. It is now a generic class used by all native data providers (Doctrine ORM, MongoDB and Elasticsearch), that you can reuse in your own.
Mercure is a brand new protocol built on top of HTTP/2 and Server-sent Events (SSE). It’s a modern and high-level alternative to WebSocket (WebSocket is not compatible with HTTP/2). Mercure is especially useful to publish updates of resources served through web APIs in real time. It is natively supported by modern browsers (no required library nor SDK) and is very useful to update reactive web and mobile apps.
In version 2.4, I added Mercure support to the server component of API Platform and to the React and React Native app generators. The Docker Compose setup provided with API Platform has also been updated to provide a Mercure hub.
Configuring the framework to automatically dispatch updates to the currently connected clients is straightforward:
CQRS and async message handling with Symfony Messenger
Messenger is a new Symfony component created by Samuel Roze (Symfony and API Platform Core Team). It allows to dispatch messages using message queues (RabbitMQ, Kafka, Amazon SQS, Google PubSub…) and to handle them asynchronously. It provides a message bus that is very useful to implement the CQRS design pattern.
In API Platform 2.4, I added a convenient way to leverage the capabilities of Messenger. This new feature is particularly useful to create service-oriented (RPC-like) endpoints:
HTTP/2 allows a server to pre-emptively send (or “push”) responses (along with corresponding “promised” requests) to a client in association with a previous client-initiated request. This can be useful when the server knows the client will need to have those responses available in order to fully process the response to the original request.
The UI is built client-side dynamically by parsing the API spec. Awesome isn’t it?
Jean-François also added some convenient helpers to help customizing the admin, and Laury Sorriaux fixed a long standing limitation: it’s now possible to use the admin even with API not served at the root of the domain (such as /api).
In Symfony 4.2, another component that is super convenient for apps containing JS code has been released: Panther, a PHP library compatible with BrowserKit, that drives real web browsers to create end-to-end (E2E) tests with ease.
The examples will use VueJS, because it’s probably the easiest JS framework to get started with as a PHP developer, but all the tips and tricks will be applicable with other libraries such as React or Angular.
Finally, we’ll add some real time capabilities to our app using Mercure.rocks
You may have noticed the recent fuss about the compromise of event-stream, a popular NPM package:
This attack raised, again, the problem of the JS dependency cascade: when you install a major project, it comes with hundreds of tiny libraries, sometimes not maintained, and sometimes coming from untrusted sources.
Regarding Symfony, as a maintainer I have the feeling that the Symfony Core Team (carefully) adds dependencies only when strictly necessary. However I had no metrics to prove it. So I checked. Then I compared with other PHP frameworks I’m interested in: Laravel and API Platform.
Even when installing the website skeleton, that comes with all features provided by the framework and third party (trusted) packages such as the Doctrine ORM, the Twig templating library or Swift Mailer, only 94 packages (75 when excluding dev dependencies), from 17 different organisations (14 without dev deps) are installed:
Among these vendors, 7 are directly maintained by members (or former members) of the Symfony Core Team (easycorp, monolog, sensio, swiftmailer, symfony, twig, webmozart) and 2 are from the FIG. All these few libraries, except maybe jdorn/sql-formatter, are actively maintained, by prominent and well known members of the PHP community.
A minimal installation of the server part (the one written in PHP) of the API Platform framework contains only 27 packages (26 without dev deps) from 5 vendors.
$composer create-project symfony/skeleton nb-deps
$composer require api-platform/core
When installing the API pack, that provides all features you can expect from an advanced web API (hypermedia support, automatic persistence with the Doctrine ORM support, automatic generation of human-readable documentation, CORS support, authorisation rules…), 57 packages (56 without dev deps) from 10 vendors are shipped.
The default installation of Laravel (that is somewhat similar in term of features to the Symfony website skeleton) comes with 72 packages (39 when excluding dev deps) from 35 vendors (21 when excluding dev deps).
The number of maintainers you have to trust when using Laravel is just a bit larger than when using Symfony, but again it’s still a bunch of people that are well known in the PHP community, and who may fit in a single room.
In comparison, a default installation of React using Create React App – that is more similar to the minimal Symfony skeleton than to fully-featured frameworks such as the Symfony website skeleton or Laravel – comes with 809 packages, most of them being maintained by different teams or individuals.
Another main difference is that most JS libraries are distributed as compiled and minified builds. Therefore it’s very difficult to guarantee that what is shipped and executed behaves exactly the same way than the code in the sources. In PHP, the source of the libraries are used directly, without intermediate obfuscation. PHP builds are easy to reproduce, it helps a lot when auditing.
Of course, it doesn’t mean that major PHP frameworks are immune to this kind of attacks and – as any IT project – they also have their own security issues. However, the amount of third party code installed and the chain of trust you have to rely on is more under control than in the JS world.