struct Athena::Mercure::Bundle
inherits Athena::DependencyInjection::AbstractBundle
#
Integrates the Mercure component with the dependency injection and HTTP kernel components, such as within an Athena Framework application.
Require athena-mercure/bundle to register the bundle, then configure at least one hub:
require "athena"
require "athena-mercure/bundle"
ADI.configure({
mercure: {
hubs: {
default: {
url: ENV["MERCURE_URL"],
jwt: {
secret: ENV["MERCURE_JWT_SECRET"],
publish: ["*"],
subscribe: ["*"],
},
},
},
},
})
See Schema for the full set of possible configuration options.
If multiple hubs are configured, they may be injected using the hub name as a constructor parameter typed to AMC::Hub::Interface.
For example some_hub : AMC::Hub::Interface assuming the key used in the hubs named tuple was some_hub.
The bundle brings AMC::Hub, AMC::Bundle::Authorization, and AMC::Bundle::Discovery into the framework as injectable services, with event listeners that handle response headers and cookies automatically.
Publishing#
Inject AMC::Hub::Interface to publish updates from a controller:
@[ARTA::Route(path: "/broadcast")]
class BroadcastController < ATH::Controller
def initialize(@hub : AMC::Hub::Interface); end
@[ARTA::Post("/")]
def broadcast : Nil
@hub.publish AMC::Update.new(
"https://example.com/books/1",
{status: "OutOfStock"}.to_json
)
end
end
Authorization#
Inject AMC::Bundle::Authorization to set the mercureAuthorization cookie for private updates.
The AMC::Bundle::Listeners::SetCookie listener automatically adds the cookie to the response — there is no need to modify the response directly:
@[ARTA::Route(path: "/auth")]
class AuthController < ATH::Controller
def initialize(@authorization : AMC::Bundle::Authorization); end
@[ARTA::Get("/subscribe")]
def subscribe(request : AHTTP::Request) : Nil
@authorization.set_cookie(
request,
subscribe: ["https://example.com/books/{id}"],
)
end
end
See Authorization in the Mercure component docs for more on private updates and cookie-based auth.
Discovery#
Inject AMC::Bundle::Discovery to add the Mercure hub Link header to a response.
The AMC::Bundle::Listeners::AddLinkHeader listener handles adding the header — there is no need to modify the response directly:
@[ARTA::Route(path: "/books")]
class BookController < ATH::Controller
def initialize(@discovery : AMC::Bundle::Discovery); end
@[ARTA::Get("/{id}")]
def show(request : AHTTP::Request, id : Int32) : {id: Int32, title: String}
@discovery.add_link request
{id: id, title: "Hello World!"}
end
end
The response will include a Link: <https://hub.example.com/.well-known/mercure>; rel="mercure" header.
A client can then extract the hub URL to subscribe to updates for this resource:
fetch('/books/1')
.then(response => {
const hubUrl = response.headers.get('Link').match(/<([^>]+)>;\s+rel=(?:mercure|"[^"]*mercure[^"]*")/)[1];
const hub = new URL(hubUrl, window.origin);
hub.searchParams.append('topic', 'https://example.com/books/{id}');
const eventSource = new EventSource(hub);
eventSource.onmessage = event => console.log(event.data);
});
See Discovery in the Mercure component docs for more on the discovery protocol.