Search code examples
javascriptangularobservablepublish-subscribemercure

Symfony + Mercure / Server Side Events to Angular 16


I have a Symfony backend (BoltCMS) with a custom CaddyServer with the Mercure plugin and I am trying to send a message to my Angular 16 frontend on the event when I save something in the backend so that I can refresh the cache in the Angular application.

I am able to successfully publish the SSE from Symfony to the Mercure/CaddyServer.

However I am having some trouble subscribing to said event. Because I want to authenticate client side subscriptions I cannot use the built in mechanism in Angular if I understood correctly (using EventSource)?

On my search through the internet I found the ng-sse-client package with which - according to the README - I should be able to pass a authentication token in the header:

...
import { SseClient } from 'ngx-sse-client';
...

constructor(private sseClient: SseClient) {
    const headers = new HttpHeaders().set('Authorization', `Basic YWRtaW46YWRtaW4=`);

    this.sseClient.stream('/subscribe', { keepAlive: true, reconnectionDelay: 1_000, responseType: 'event' }, { headers }, 'POST').subscribe((event) => {
      if (event.type === 'error') {
        const errorEvent = event as ErrorEvent;
        console.error(errorEvent.error, errorEvent.message);
      } else {
        const messageEvent = event as MessageEvent;
        console.info(`SSE request with type "${messageEvent.type}" and data "${messageEvent.data}"`);
      }
    });
}

However, when I try to call the mentioned function, I get an error, indicating that the function call only accepts 2 erguments instead of 4: Expected 1-2 arguments, but got 4.

When I ctrl+click on the stream function to navigate to the function definition, located in node_modules/ngx-sse-client/lib/sse-client.service.d.ts, it indeed shows a function which only accepts 2 arguments:

stream(url: string, options?: {
        keepAlive?: boolean;
        reconnectionDelay?: number;
        responseType?: 'event';
    }):

The weird thing is I am able to find a function definition in another location sse-client.service.ts which does in fact have 4 arguments. So it looks like I am importing the wrong function or something?

I have looked online, but saw nobody else having similar issues, so I'm thinking I must be doing something wrong, but I've been unable to figure it out.

Any help would be appreciated, either in pointing me in the right direction for an alternative solution/package, or an indication regarding to what I am doing wrong in using this package.

For me it feels weird that something like this is not natively supported in Angular, but I am rather new to Angular, and its quite difficult to find consistent information online, since there are so many different versions with their respective changes.

I have also read about WebSockets, but from what I understood this would be quite overkill for my use-case. But since I have no experience with either SSE or WebSockets, I might be mistaken, and this might be a viable approach?

What I've tried:

I tried calling the SseClient.stream() function with 4 arguments, expecting it to work, but I got an error that it only accepts 2 arguments.


Solution

  • While I still haven't found a way to get the ng-sse-client to work, after some more digging I have found several solutions to accomplish the sending of an authorization token to Mercure from Angular:

    1. Put the token in a Cookie (with a recommended name of mercureAuthorization), marked with Secure, HtttpOnly and SameSite (this is the recommended method)
    2. Use an EventSource Polyfill, such as this one. Using this package with Angular does require an additional step, namely installing the corresponding types to your devDependencies, using npm i --save-dev @types/event-source-polyfill