@mdsiha369/adonis-mercure
v2.1.2
Published
Mercure Hub integration for AdonisJS v6 — publish real-time updates via Server-Sent Events (SSE)
Downloads
83
Maintainers
Readme
Table of Contents
Requirements
- AdonisJS v6
- Node.js >= 20.6.0
- A running Mercure Hub
Installation
npm install @mdsiha369/adonis-mercure
node ace configure @mdsiha369/adonis-mercureConfiguration
After running ace configure, a config/mercure.ts file is created and your .env is updated automatically.
// config/mercure.ts
import env from '#start/env'
import { defineConfig } from '@mdsiha369/adonis-mercure'
export default defineConfig({
endpoint: env.get('MERCURE_ENDPOINT'),
adminToken: env.get('MERCURE_ADMIN_JWT'),
jwt: {
alg: 'HS256',
secret: env.get('MERCURE_JWT_SECRET'),
},
// optional — default: 5000ms, set to 0 to disable
http: {
timeout: 5000,
},
})Set the following env variables in your .env:
MERCURE_ENDPOINT=http://localhost:3000/.well-known/mercure
MERCURE_ADMIN_JWT=<your-admin-jwt>
MERCURE_JWT_SECRET=<your-jwt-secret>Note: The
adminTokenmust be a JWT signed with your hub's secret and a"publish": ["*"]claim in themercurefield. See the Mercure auth docs for details.
Usage
Import the service anywhere in your app:
import mercure from '@mdsiha369/adonis-mercure/services/main'Publish an update
await mercure.send('/orders/42', { status: 'shipped' })Multiple topics
await mercure.send(['/orders/42', '/notifications/user/1'], { status: 'shipped' })Private updates
Private updates are only delivered to authenticated subscribers who hold a valid token for that topic.
// shorthand (backward compatible)
await mercure.send('/orders/42', { status: 'shipped' }, true)
// options object
await mercure.send('/orders/42', { status: 'shipped' }, { private: true })SSE options (id, type, retry)
await mercure.send(
'/orders/42',
{ status: 'shipped' },
{
id: 'msg-001', // event ID — enables reconnection recovery
type: 'order.shipped', // event type
retry: 5000, // client reconnect delay in ms
private: true,
}
)Generate a subscriber token
Use this to create JWT tokens for your frontend clients so they can subscribe to topics, including private ones.
// typed shorthand
const token = await mercure.generateSubscribeToken(['/orders/42'])
// or low-level
const token = await mercure.generate({ subscribe: ['/orders/42'] })Pass the token to your frontend:
const url = new URL('http://localhost:3000/.well-known/mercure')
url.searchParams.append('topic', '/orders/42')
const eventSource = new EventSource(url.toString(), {
headers: { Authorization: `Bearer ${token}` },
})Health check
const isReachable = await mercure.ping() // true | falseTesting
FakeMercure lets you test your application code without a real Mercure Hub.
Swap the container binding in your test setup:
import { FakeMercure } from '@mdsiha369/adonis-mercure'
// before your test
app.container.swap('mercure', () => new FakeMercure())
// after your test
app.container.restore('mercure')Assert on what was sent:
const fake = (await app.container.make('mercure')) as FakeMercure
// assert a topic received a message
fake.assertSent('/orders/42')
// assert a topic received a specific payload
fake.assertSent('/orders/42', { status: 'shipped' })
// assert a topic was never sent to
fake.assertNotSent('/admin/secret')
// assert nothing was sent at all
fake.assertNothingSent()
// inspect all recorded messages
const messages = fake.getSent()
// reset between tests
fake.clear()API Reference
send(topics, data?, options?)
Publishes an update to the Mercure Hub.
| Parameter | Type | Default | Description |
| --------- | ------------------------- | ------- | ------------------------------------------------------ |
| topics | string \| string[] | — | Topic(s) to publish to |
| data | Record<string, unknown> | {} | Payload — serialized as JSON |
| options | boolean \| SendOptions | false | true for private (legacy), or a SendOptions object |
SendOptions
| Property | Type | Description |
| --------- | --------- | ---------------------------------------------- |
| private | boolean | Restrict delivery to authenticated subscribers |
| id | string | SSE event ID (enables reconnection recovery) |
| type | string | SSE event type |
| retry | number | Client reconnect delay in milliseconds |
Throws MercurePublishError if the hub returns a non-2xx response.
Throws MercureTimeoutError if the hub does not respond within the configured timeout.
generateSubscribeToken(topics)
Generates a JWT with { subscribe: topics } for a frontend client.
const token = await mercure.generateSubscribeToken(['/chat/1', '/notifications/me'])generate(payload)
Low-level JWT generation. Wraps payload under the mercure claim.
const token = await mercure.generate({ subscribe: ['/chat/1'], publish: ['/chat/1'] })ping()
Returns true if the hub is reachable, false on network error or timeout.
Roadmap
Features planned for upcoming releases:
generatePublishToken(topics)— typed shorthand for publisher JWT tokens (symmetric togenerateSubscribeToken)sendBatch(messages[])— publish multiple updates in a single call- Retry with backoff — automatic retry on transient hub errors with configurable strategy
- Typed event classes — define reusable event objects (
new OrderShippedEvent(id)) instead of inline payloads
Have a use case or suggestion? Open an issue.
License
MIT — Michael DAŞ
