BSD-3-Clause licensed by Shaun Sharples
Maintained by [email protected]
This version can be pinned in stack with:servant-event-stream-0.4.0.1@sha256:c79ef3516626850347f2a92c15de473244539f2211c05d0350f4073d1659c669,1760

Module documentation for 0.4.0.1

servant-event-stream

Server-Sent Events (SSE) support for Servant. Stream events from your server to connected clients, or consume third-party SSE streams (such as OpenAI) from Haskell.

Quick start

Define a domain type and a ToServerEvent instance. Use jsonEvent to JSON-encode the data payload, or serverEvent for raw bytestrings:

data ChatEvent
  = ContentDelta Text
  | ContentDone Text
  | ChatDone

type MyApi = "chat" :> ServerSentEvents (SourceIO ChatEvent)

instance ToServerEvent ChatEvent where
  toServerEvent (ContentDelta d) = jsonEvent (Just "response.content.delta") Nothing d
  toServerEvent (ContentDone t) = jsonEvent (Just "response.content.done") Nothing t
  toServerEvent ChatDone = jsonEvent (Just "response.done") Nothing ()

server :: Server MyApi
server = pure $ source
  [ContentDelta "Hel", ContentDelta "lo!", ContentDone "Hello!", ChatDone]

For POST endpoints (e.g. OpenAI chat completions), use PostServerSentEvents:

type MyApi = "chat" :> ReqBody '[JSON] ChatRequest
                    :> PostServerSentEvents (SourceIO ChatEvent)

To consume an SSE stream, provide a FromServerEvent instance. Use jsonData to decode JSON from the data field:

instance FromServerEvent ChatEvent where
  fromServerEvent ev = case eventType ev of
    Just "response.content.delta" -> ContentDelta <$> jsonData ev
    Just "response.content.done"  -> ContentDone <$> jsonData ev
    Just "response.done"          -> Right ChatDone
    _                             -> Left "unknown event"

For types that serialise entirely as JSON (no event type dispatch), use DerivingVia:

data Temperature = Temperature { celsius :: Double }
  deriving (Generic, ToJSON, FromJSON)
  deriving (ToServerEvent, FromServerEvent) via JsonData Temperature

See the Haddock documentation for the full API reference, or the upgrading guide if you’re coming from 0.3.

Coming from servant’s built-in SSE

Servant 0.20.3.0 introduced its own ServerSentEvents type in Servant.API.ServerSentEvents. If you’d like to try this library instead, here’s how the concepts map:

servant built-in servant-event-stream
Servant.API.ServerSentEvents Servant.API.EventStream
ServerSentEvents 'JsonEvent MyEvent ServerSentEvents (SourceIO MyEvent)
EventKind (RawEvent / JsonEvent) ToServerEvent / FromServerEvent typeclasses

This library takes a typeclass approach: ToServerEvent and FromServerEvent control how your domain types map to SSE fields, giving you access to event names, ids, comments, and retry directives in a single ServerEvent record. It also provides PostServerSentEvents for POST endpoints, and JSON helpers (jsonEvent, jsonData, JsonData) for APIs that encode payloads as JSON.

The built-in module focuses on client-side consumption (HasClient instances), while this library provides both HasServer and HasClient instances. See the side-by-side examples for a more detailed walkthrough.

Development

Uses Nix flakes for the development environment.

nix develop       # enter dev shell
nix build         # full build
nix flake check   # CI-equivalent check
cabal test        # run tests (inside dev shell)

Changes

Revision history for servant-event-stream

0.4.0.1 – 2026-03-01

  • Fix haddock markup in module header (t'JsonData' rendered literally).

0.4.0.0 – 2026-02-27

  • Breaking: Add eventComment and eventRetry fields to ServerEvent. Existing code using the ServerEvent constructor must add the new fields, e.g. ServerEvent typ eid dat Nothing Nothing.
  • Breaking: Drop charset=utf-8 from the Accept instance. The content type is now text/event-stream (no parameters), matching the WHATWG spec.
  • New dependency: aeson (for JSON helpers).
  • New dependency: servant-client-core (for HasClient instances).
  • Add serverEvent convenience constructor matching the old 3-field API for easy migration.
  • Add dataEvent convenience constructor for simple data-only events.
  • Add commentEvent convenience constructor for heartbeat keepalives.
  • Add retryEvent convenience constructor for setting client reconnection delay.
  • Export encodeServerEvent for direct use outside of Servant.
  • Add FromServerEvent typeclass and decodeServerEvent for parsing SSE events.
  • Add MimeUnrender EventStream and FramingUnrender ServerEventFraming instances for consuming SSE streams.
  • Add HasClient instances for ServerSentEvents and PostServerSentEvents, enabling client-side SSE consumption via servant-client.
  • Add PostServerSentEvents combinator for POST SSE endpoints (e.g. OpenAI’s streaming API), with HasServer, HasClient, and HasForeign instances.
  • Add JsonData newtype for deriving ToServerEvent and FromServerEvent via JSON encoding using DerivingVia.
  • Add jsonEvent helper to construct events with JSON-encoded data payloads.
  • Add jsonData helper to decode JSON from an event’s data field.
  • Strip leading UTF-8 BOM from SSE input, per the WHATWG spec.
  • Export ServerEventFraming (needed for StreamPost SSE endpoints).
  • Always emit at least one data: field, even when eventData is empty. Previously, empty data produced no output and the event was silently dropped by clients.
  • Sanitize eventType, eventId, and eventComment by stripping CR and LF characters to prevent malformed SSE output.
  • Strip NULL characters from eventId (the SSE spec silently ignores ids containing NULL).

0.3.2.1 – 2026-02-27

  • Add guard space after colon in SSE field encoding. Without it, field values starting with a space lost their leading space because the SSE spec strips exactly one leading space from field values. (Issue #16)
  • Add unit tests for encodeServerEvent.

0.3.2.0 – 2026-02-23

  • Support GHC 8.6 through 9.12 (base 4.12 to 4.21).
  • Remove redundant Typeable deriving to fix -Wderiving-typeable warning on GHC 9.12+.
  • Add CI test matrix for GHC 8.6, 8.10, 9.2, 9.8, 9.12.

0.3.1.1 – 2026-02-23

  • Constrain base bounds in test suite to match library.

0.3.1.0 – 2025-08-21

  • Qualify servant imports so that ServerSentEvents added in servant-0.20.3.0 does not conflict with ours. (Issue #12)

0.3.0.0 – 2024-09-05

  • Breaking changes to the API.

    Event streams are implemented using servant’s ‘Stream’ endpoint. You should provide a handler that returns a stream of events that implements ‘ToSourceIO’ where events have a ‘ToServerEvent’ instance.

    Example:

    type MyApi = “books” :> ServerSentEvents (SourceIO Book)

    instance ToServerEvent Book where toServerEvent book = …

    server :: Server MyApi server = streamBooks where streamBooks :: Handler (SourceIO Book) streamBooks = pure $ source [book1, …]

0.2.1.0 – 2021-04-21

  • Import Data.Semigroup for base < 4.11.0

0.2.0.0 – 2021-04-20

  • Servant.EventStream was moved to Servant.API.EventStream to adhere existing upstream layout.

0.1.0.0 – 2018-04-30

  • First version. Released on an unsuspecting world.