How to return Server-Sent Events
View SourceProblem
Your client uses the EventSource API (or any RFC 8895 compatible
consumer) and expects text/event-stream framing.
Solution
events(_Req) ->
livery_resp:sse(200, fun(Emit) ->
Emit(#{event => <<"tick">>, data => <<"1">>}),
Emit(#{event => <<"tick">>, data => <<"2">>}),
ok
end).livery_resp:sse/2 sets content-type: text/event-stream and
cache-control: no-cache. The producer fun runs in the per-request
process and drives Emit with one event at a time.
Event shape
Emit accepts:
- A map
#{event, id, retry, data}where any field is optional exceptdata. Livery formats it into the standard SSE frame. - Plain
iodata(), framed as adata:line only.
Emit(#{event => <<"ping">>, data => <<"pong">>}).
%% event: ping
%% data: pong
%%
%% (blank line terminates the frame)
Emit(#{event => <<"update">>, id => <<"42">>,
retry => 1500, data => <<"v">>}).
%% event: update
%% id: 42
%% retry: 1500
%% data: v
%%
Emit(<<"plain">>).
%% data: plain
%%Multi-line data is fine: pass an iolist whose bytes contain \n
and Livery emits each line with its own data: prefix when you use
the helper. For multi-line, use iolist of lines:
Emit(#{data => [<<"line 1">>, <<"\nline 2">>]}).Heartbeats
To keep idle connections alive through proxies, emit a comment line
periodically (a line beginning with :):
loop(Emit) ->
receive
{event, E} -> Emit(E), loop(Emit)
after 15_000 ->
Emit(<<":heartbeat">>),
loop(Emit)
end.Disconnect detection
Emit returns {error, closed} once the client disconnects (the
H1/H2/H3 adapters surface this; the test adapter always returns
ok).
loop(Emit) ->
receive {event, E} ->
case Emit(E) of
ok -> loop(Emit);
{error, _} -> ok
end
end.See also
- Recipe: Return a streaming response
- Tutorial: Stream a response