Server Sent Events (SSE) jest mechanizmem, który pozwala na jednokierunkową komunikację pomiędzy serwerem a klientem. Możemy przy jego pomocy wysyłać wiadomości i informować klienta o zmianach na serwerze. Brzmi znajomo? Najczęściej taka funkcjonalność kojarzy nam się z Websocket? Jaka jest pomiędzy nimi różnica, kiedy powinniśmy korzystać z SEE i jak z tego skorzystać?

Websocket vs SSE

Idea Server Sent Events jest podobna do Websocketów. To co różni oba rozwiązania to sposób komunikacji pomiędzy serwerem a klientem (czyli aplikacją w przeglądarce). W Websocketach mamy do czynienia z komunikacją dwukierunkową - oprócz tego, że serwer wysyła dane do klienta to klient może również wysłać odpowiedź na serwer. SSE działa trochę inaczej - wiadomości może jedynie wysyłać serwer do klienta. Jest to pewne ograniczenie ale w większości przypadków jest to wystarczająca funkcjonalność.

Kiedy wykorzystywać SSE?

Tak jak wspomniałem w poprzednim akapicie wszystko zależy od tego co tworzymy. Jeśli są to różnego rodzaju chaty gdzie potrzebujemy komunikacji dwukierunkowej to oczywistym wyborem będzie Websocket. Jednak jeśli potrzebujemy żeby informować stronę o pojedynczych zdarzeniach np.:

wtedy wykorzystanie SSE będzie lepszym rozwiązaniem.

Jak zaimplementować SSE?

Z racji tego, że będziemy tutaj otrzymywać wiadomości z serwera implementację musimy podzielić na dwie części: klienta i serwera. Zaczniemy od części serwerowej. Aby móc wysyłać takie zdarzenia musimy ustawić odpowiednie nagłówki w odpowiedzi na zapytanie:

Mając tak ustawione nagłówki jesteśmy w stanie wysłać wiadomości. Wiadomości mają oczywiście swoją strukturę, której trzeba przestrzegać. Mamy tam dostępne 4 pola:

Wiadomości przesyłamy jako zwykłe zmienne tekstowe np.: "data: 'message'". Pojedyncze wiadomości są rozdzielane podwójnym znakiem nowej linii \n\n. Przykład takiego endpointu w Express.js

const express = require('express');

const app = express();

app.get('/events', (req, res) => {

    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Access-Control-Allow-Origin': '*'
    });
    res.flushHeaders();

    let i = 0;
    setInterval(()=>{
        res.write(`id: ${i}\n`);
        res.write(`event: event1\n`);
        res.write(`data: Message -- ${Date.now()}`);
        res.write(`\n\n`);
        i++
    }, 5000)
});

app.listen(3000);

To teraz przyszedł czas na obsługę tego po stronie klienta. Ogranicza się to tutaj tylko do wykorzystania API EventSource w przeglądarce. Podajemy tam podczas tworzenia nowej instancji obiektu adres pod którym oczekujemy otrzymywać zdarzenia

const event = new EventSource('http://localhost:3000/events')

Dalsza obsługa zależy od tego jakie zdarzenia będziemy wysyłać. Tak jak wspomniałem jednym z parametrów zdarzenia może być pole event. Jeśli go podamy to musimy skorzystać z metody addEventListener

event.addEventListener('event_name', (event)=>{
    // obsługa
})

Wszystkie inne zdarzenia są obsługiwane przy pomocy funkcji onmessage

event.onmessage = (event) => {
    //obsługa
}

I właściwie tyle. Jak widzicie jest to proste - nie potrzebujemy żadnych dodatkowych bibliotek by otrzymywać aktualizację na żywo z serwera. Jestem ciekaw czy znaliście wcześniej to rozwiązanie? No i czy z niego korzystaliście?