DiscoverVulcain | Server Push Web APIs
Mercure.rocks

Contribute!

Getting Started

Subscriptions Schema

Starting the Hub

The easiest way to get started is to install the official Mercure.rocks Hub. When it's done, go directly to the next step. There are also other unofficial libraries implementing Mercure. In the rest of this tutorial, we'll assume that the hub is running on https://localhost and that the JWT_KEY is !ChangeMe!.

Please note that the hub is entirely optional when using the Mercure protocol. Your app can also implement the Mercure protocol directly.

Subscribing

Subscribing to updates from a web browser or any other platform supporting Server-Sent Events is straightforward:

// The subscriber subscribes to updates for the https://example.com/users/dunglas topic
// and to any topic matching https://example.com/books/{id}
const url = new URL('https://localhost/.well-known/mercure');
url.searchParams.append('topic', 'https://example.com/books/{id}');
url.searchParams.append('topic', 'https://example.com/users/dunglas');
// The URL class is a convenient way to generate URLs such as https://localhost/.well-known/mercure?topic=https://example.com/books/{id}&topic=https://example.com/users/dunglas

const eventSource = new EventSource(url);

// The callback will be called every time an update is published
eventSource.onmessage = e => console.log(e); // do something with the payload

The EventSource class is available in all modern web browsers. And for legacy browsers, there are polyfills.

Closing Connection

It is important to close this connection between the client and the hub if it is no longer needed. Opened connections have a continuous buffer that will drain your application resources. This is especially true when using Single Page Applications (based on e.g. ReactJS): the connection is maintained even if the component that created it is unmounted.

To close the connection, call eventSource.close().

Sending Private Updates

Optionally, the authorization mechanism can be used to subscribe to private updates.

Authorization Schema

Discovering the Mercure Hub

Also optionally, the hub URL can be automatically discovered:

Discovery Schema

Here is a snippet to extract the URL of the hub from the Link HTTP header.

fetch('https://example.com/books/1') // Has this header `Link: <https://localhost/.well-known/mercure>; rel="mercure"`
    .then(response => {
        // Extract the hub URL from the Link header
        const hubUrl = response.headers.get('Link').match(/<([^>]+)>;\s+rel=(?:mercure|"[^"]*mercure[^"]*")/)[1];
        // Subscribe to updates using the first snippet, do something with response's body...
    });

Publishing

To dispatch an update, the publisher (an application server, a web browser...) needs to send a POST HTTP request to the hub:

POST example.com HTTP/1.1
Bearer eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdLCJzdWJzY3JpYmUiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbS9teS1wcml2YXRlLXRvcGljIiwie3NjaGVtZX06Ly97K2hvc3R9L2RlbW8vYm9va3Mve2lkfS5qc29ubGQiLCIvLndlbGwta25vd24vbWVyY3VyZS9zdWJzY3JpcHRpb25zey90b3BpY317L3N1YnNjcmliZXJ9Il0sInBheWxvYWQiOnsidXNlciI6Imh0dHBzOi8vZXhhbXBsZS5jb20vdXNlcnMvZHVuZ2xhcyIsInJlbW90ZUFkZHIiOiIxMjcuMC4wLjEifX19.z5YrkHwtkz3O_nOnhC_FP7_bmeISe3eykAkGbAl5K7c

topic=https://example.com/books/1&data={"foo": "updated value"}

Example using curl:

curl -d 'topic=https://example.com/books/1' -d 'data={"foo": "updated value"}' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdLCJzdWJzY3JpYmUiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbS9teS1wcml2YXRlLXRvcGljIiwie3NjaGVtZX06Ly97K2hvc3R9L2RlbW8vYm9va3Mve2lkfS5qc29ubGQiLCIvLndlbGwta25vd24vbWVyY3VyZS9zdWJzY3JpcHRpb25zey90b3BpY317L3N1YnNjcmliZXJ9Il0sInBheWxvYWQiOnsidXNlciI6Imh0dHBzOi8vZXhhbXBsZS5jb20vdXNlcnMvZHVuZ2xhcyIsInJlbW90ZUFkZHIiOiIxMjcuMC4wLjEifX19.z5YrkHwtkz3O_nOnhC_FP7_bmeISe3eykAkGbAl5K7c' -X POST https://localhost/.well-known/mercure

Example using Node.js / Serverless:

// Handle a POST, PUT, PATCH or DELETE request or finish an async job...
// and notify the hub
const http = require('http');
const querystring = require('querystring');

const postData = querystring.stringify({
    'topic': 'https://example.com/books/1',
    'data': JSON.stringify({ foo: 'updated value' }),
});

const req = http.request({
    hostname: 'localhost',
    port: '3000',
    path: '/.well-known/mercure',
    method: 'POST',
    headers: {
        Authorization: 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdLCJzdWJzY3JpYmUiOlsiaHR0cHM6Ly9leGFtcGxlLmNvbS9teS1wcml2YXRlLXRvcGljIiwie3NjaGVtZX06Ly97K2hvc3R9L2RlbW8vYm9va3Mve2lkfS5qc29ubGQiLCIvLndlbGwta25vd24vbWVyY3VyZS9zdWJzY3JpcHRpb25zey90b3BpY317L3N1YnNjcmliZXJ9Il0sInBheWxvYWQiOnsidXNlciI6Imh0dHBzOi8vZXhhbXBsZS5jb20vdXNlcnMvZHVuZ2xhcyIsInJlbW90ZUFkZHIiOiIxMjcuMC4wLjEifX19.z5YrkHwtkz3O_nOnhC_FP7_bmeISe3eykAkGbAl5K7c',
        // the JWT must have a mercure.publish key containing an array of topic selectors (can contain "*" for all topics, and be empty for public updates)
        // the JWT key must be shared between the hub and the server
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData),
    }
}, /* optional response handler */);
req.write(postData);
req.end();

// You'll probably prefer use the request library or the node-fetch polyfill in real projects,
// but any HTTP client, written in any language, will be just fine.

The JWT must contain a publish property containing an array of topic selectors. This array can be empty to allow publishing anonymous updates only. The topic selector * can be used to allow publishing private updates for all topics. To create and read JWTs try jwt.io (demo token, key: !ChangeMe!).

Active Subscriptions

Mercure dispatches events every time a new subscription is created or terminated. It also exposes a web API to retrieve the list of active subscriptions.

Learn more about subscriptions.

Going Further