agentsclimarketplace

Mercure

Skill api-platform/skillset/skills/mercure

API Platform agent skills

Install
npx -y skills add api-platform/skillset --skill mercure

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 24 stars24 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Pushes real-time resource updates from API Platform to clients over the Mercure protocol (Server-Sent Events). Use whenever the user mentions real-time updates, live data, push notifications, websockets-vs-SSE, Mercure, subscribing to changes, "update the UI when data changes", broadcasting create/update/delete, private/authorized updates, or custom Mercure topics.

SKILL.md

6.0 KB, as published. Nobody here has run it

Mercure: Real-Time Updates

Symfony-only. The Mercure integration is built on Doctrine's lifecycle events and symfony/mercure-bundle; the API Platform Laravel package ships no Mercure support. On Laravel use Laravel's own broadcasting (Reverb/Echo/Pusher).

Mercure pushes the new version of a resource to all subscribed clients whenever it is created, updated or deleted. API Platform hooks Doctrine's lifecycle: after a write it serializes the resource and publishes it to the Mercure hub, which fans it out to connected clients over Server-Sent Events. Clients subscribe with the native EventSource API — no extra client library required.

Use this for dashboards, collaborative editing, notifications. It is push-only and one-directional (server → client); for bidirectional messaging you still need something else.

Enabling on a resource

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(mercure: true)]
class Book {}

Now every create/update/delete on a Book publishes to the hub. On delete, only the (now-gone) IRI is sent so clients can drop it. API Platform also adds a Link header advertising the hub URL on every response for this resource, so smart clients auto-discover where to subscribe.

mercure is intentionally untyped — it accepts true, an options array, or an ExpressionLanguage string. There is no MercureOptions attribute class in core; pass a plain array. (Verified against ApiResource::$mercure, declared mixed.)

Symfony hub configuration

Mercure is preconfigured in the API Platform distribution. With a manual install, add the MercureBundle and point it at a hub:

# config/packages/mercure.yaml
mercure:
    hubs:
        default:
            url: '%env(MERCURE_URL)%'          # internal URL API Platform publishes to
            public_url: '%env(MERCURE_PUBLIC_URL)%'  # URL the browser subscribes to
            jwt:
                secret: '%env(MERCURE_JWT_SECRET)%'
                publish: ['*']                 # API Platform must be allowed to publish

The hub itself runs as a separate process/container (the mercure.rocks binary or the Caddy Mercure module).

Subscribing from a browser

Read the hub URL from the Link header, then open an EventSource on the topic (the resource IRI):

const url = new URL('https://example.com/.well-known/mercure');
url.searchParams.append('topic', 'https://example.com/books/1');

const es = new EventSource(url);
es.onmessage = ({ data }) => {
    const book = JSON.parse(data);   // the serialized resource, or {"@id": "..."} on delete
};

Use a topic template like https://example.com/books/{id} to receive updates for every book in one subscription.

Available options

Pass an array instead of true:

#[ApiResource(mercure: [
    'private' => true,                          // authorized subscribers only (see below)
    'topics' => ['https://example.com/books'],  // override the default (resource IRI)
    'normalization_context' => ['groups' => ['book:push']], // serialize differently for the push
    'type' => 'book-updated',                   // SSE event type
    'retry' => 3000,                            // SSE retry hint (ms)
])]
class Book {}

data (override the published payload) and id (SSE event id) are also accepted. A common reason to set normalization_context is to push a leaner shape than the HTTP response, or to avoid pushing fields a subscriber shouldn't compute on.

Private (authorized) updates

A public update reaches anyone subscribed to the topic. For per-user data, mark the update private so only clients whose JWT carries a matching subscribe target selector receive it:

#[ApiResource(mercure: ['private' => true])]
class Notification {}

The subscriber's Mercure JWT must include a mercure.subscribe claim matching the topic. This is the mechanism that prevents one user's real-time updates leaking to another — pair it with HTTP-level security (see operations) so the resource is both fetch-protected and push-protected.

Dynamic options via expression

When the options depend on the resource instance (e.g. the topic must include the owner), use an ExpressionLanguage string resolved per object. iri() builds an IRI, escape() is rawurlencode:

use ApiPlatform\Metadata\ApiResource;

#[ApiResource(mercure: 'object.getMercureOptions()')]
class Book
{
    public function getMercureOptions(): array
    {
        return [
            'private' => true,
            // '@=' marks each topic value as an expression
            'topics' => [
                '@=iri(object)',
                '@=iri(object.getOwner()) ~ "/?topic=" ~ escape(iri(object))',
            ],
        ];
    }
}

The alternate owner/?topic=... form is the "restrictive update" pattern: a subscriber authorized only for https://example.com/users/foo/{?topic} receives the update via the alternate topic without being able to subscribe to all books. This is how you scope a shared resource type to per-owner visibility.

GraphQL subscriptions

When GraphQL is enabled (see graphql), mercure: true also powers GraphQL subscription operations — clients subscribe to a query and receive pushes through the same hub via a generated subscription IRI.

Checklist

  • Hub configured with separate internal url and public public_url
  • API Platform's publisher JWT allows publish
  • Per-user resources use 'private' => true, not public topics
  • Private pushes paired with HTTP security on the same resource
  • normalization_context set when the push shape differs from the HTTP response
  • Clients discover the hub from the Link header, not a hardcoded URL

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.