Skip to content

Commit f788d0e

Browse files
committed
Add Server-Sent Events documentation
1 parent d14462f commit f788d0e

File tree

3 files changed

+197
-0
lines changed

3 files changed

+197
-0
lines changed

components/http_foundation.rst

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,73 @@ including generators::
817817
return new StreamedJsonResponse(loadArticles());
818818
}
819819

820+
.. _component-http-foundation-sse:
821+
822+
Streaming Server-Sent Events
823+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
824+
825+
The :class:`Symfony\\Component\\HttpFoundation\\EventStreamResponse` class
826+
allows you to implement `Server-Sent Events (SSE)`_ - a standard for pushing
827+
real-time updates from server to client over HTTP.
828+
829+
.. versionadded:: 7.3
830+
831+
The ``EventStreamResponse`` and ``ServerEvent`` classes were introduced in Symfony 7.3.
832+
833+
Basic usage with a generator::
834+
835+
use Symfony\Component\HttpFoundation\EventStreamResponse;
836+
use Symfony\Component\HttpFoundation\ServerEvent;
837+
838+
$response = new EventStreamResponse(function (): iterable {
839+
yield new ServerEvent('First message');
840+
sleep(1);
841+
yield new ServerEvent('Second message');
842+
});
843+
844+
$response->send();
845+
846+
The ``EventStreamResponse`` automatically sets the required headers::
847+
848+
Content-Type: text/event-stream
849+
Cache-Control: no-cache
850+
Connection: keep-alive
851+
852+
The :class:`Symfony\\Component\\HttpFoundation\\ServerEvent` class represents an
853+
individual SSE event following `the WHATWG SSE specification`_. It accepts the
854+
following constructor arguments:
855+
856+
``data``
857+
The event data (string or iterable for multi-line data).
858+
859+
``type``
860+
The event type. Clients can listen for specific types using
861+
``eventSource.addEventListener('type', ...)``.
862+
863+
``id``
864+
The event ID. The browser sends this as ``Last-Event-ID`` header when
865+
reconnecting, allowing you to resume from where the client left off.
866+
867+
``retry``
868+
The reconnection time in milliseconds. Tells the browser how long to wait
869+
before reconnecting if the connection is lost.
870+
871+
``comment``
872+
A comment line (prefixed with ``:`` in the SSE protocol). Useful for
873+
keep-alive messages to prevent connection timeouts.
874+
875+
.. tip::
876+
877+
For usage in Symfony controllers with additional features like automatic
878+
reconnection handling, see :ref:`controller-server-sent-events`.
879+
880+
.. warning::
881+
882+
SSE keeps HTTP connections open, consuming server resources for each client.
883+
For applications with many concurrent connections, consider using
884+
:doc:`Mercure </mercure>` instead, which uses a dedicated hub for efficient
885+
connection management.
886+
820887
.. _component-http-foundation-serving-files:
821888

822889
Serving Files
@@ -1086,3 +1153,5 @@ Learn More
10861153
.. _RFC 8674: https://tools.ietf.org/html/rfc8674
10871154
.. _Doctrine Batch processing: https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/batch-processing.html#iterating-results
10881155
.. _`CHIPS`: https://developer.mozilla.org/en-US/docs/Web/Privacy/Partitioned_cookies
1156+
.. _`Server-Sent Events (SSE)`: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
1157+
.. _`the WHATWG SSE specification`: https://html.spec.whatwg.org/multipage/server-sent-events.html

controller.rst

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,121 @@ This way, browsers can start downloading the assets immediately; like the
951951
``sendEarlyHints()`` method also returns the ``Response`` object, which you
952952
must use to create the full response sent from the controller action.
953953

954+
.. _controller-server-sent-events:
955+
956+
Streaming Server-Sent Events
957+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
958+
959+
`Server-Sent Events (SSE)`_ is a standard that allows a server to push updates
960+
to the client over a single HTTP connection. It's a simple and efficient way
961+
to send real-time updates from the server to the browser, such as live
962+
notifications, progress updates, or data feeds.
963+
964+
.. versionadded:: 7.3
965+
966+
The ``EventStreamResponse`` and ``ServerEvent`` classes were introduced in Symfony 7.3.
967+
968+
The :class:`Symfony\\Component\\HttpFoundation\\EventStreamResponse` class
969+
allows you to stream events to the client using the SSE protocol. It automatically
970+
sets the required headers (``Content-Type: text/event-stream``, ``Cache-Control: no-cache``,
971+
``Connection: keep-alive``) and provides a simple API to send events::
972+
973+
use Symfony\Component\HttpFoundation\EventStreamResponse;
974+
use Symfony\Component\HttpFoundation\ServerEvent;
975+
976+
// ...
977+
978+
public function liveNotifications(): EventStreamResponse
979+
{
980+
return new EventStreamResponse(function (): iterable {
981+
foreach ($this->getNotifications() as $notification) {
982+
yield new ServerEvent($notification->toJson());
983+
984+
sleep(1); // simulate a delay between events
985+
}
986+
});
987+
}
988+
989+
The :class:`Symfony\\Component\\HttpFoundation\\ServerEvent` class is a DTO
990+
that represents an SSE event following `the WHATWG specification`_. You can
991+
customize each event using its constructor arguments::
992+
993+
// basic event with just data
994+
yield new ServerEvent('Some message');
995+
996+
// event with a custom type (client listens via addEventListener('my-event', ...))
997+
yield new ServerEvent(
998+
data: json_encode(['status' => 'completed']),
999+
type: 'my-event'
1000+
);
1001+
1002+
// event with an ID (useful for resuming streams with Last-Event-ID header)
1003+
yield new ServerEvent(
1004+
data: 'Update content',
1005+
id: 'event-123'
1006+
);
1007+
1008+
// event that tells the client to retry after a specific time (in milliseconds)
1009+
yield new ServerEvent(
1010+
data: 'Retry info',
1011+
retry: 5000
1012+
);
1013+
1014+
// event with a comment (can be used for keep-alive)
1015+
yield new ServerEvent(comment: 'keep-alive');
1016+
1017+
For use cases where generators are not feasible, you can use the
1018+
:method:`Symfony\\Component\\HttpFoundation\\EventStreamResponse::sendEvent`
1019+
method for manual control::
1020+
1021+
use Symfony\Component\HttpFoundation\EventStreamResponse;
1022+
use Symfony\Component\HttpFoundation\ServerEvent;
1023+
1024+
// ...
1025+
1026+
public function liveProgress(): EventStreamResponse
1027+
{
1028+
return new EventStreamResponse(function (EventStreamResponse $response) {
1029+
$redis = new \Redis();
1030+
$redis->connect('127.0.0.1');
1031+
$redis->subscribe(['message'], function (/* ... */, string $message) use ($response) {
1032+
$response->sendEvent(new ServerEvent($message));
1033+
});
1034+
});
1035+
}
1036+
1037+
On the client side, you can listen to events using the native ``EventSource`` API:
1038+
1039+
.. code-block:: javascript
1040+
1041+
const eventSource = new EventSource('/live-notifications');
1042+
1043+
// listen to all events (without a specific type)
1044+
eventSource.onmessage = (event) => {
1045+
console.log('Received:', event.data);
1046+
};
1047+
1048+
// listen to events with a specific type
1049+
eventSource.addEventListener('my-event', (event) => {
1050+
console.log('My event:', JSON.parse(event.data));
1051+
});
1052+
1053+
// handle connection errors
1054+
eventSource.onerror = (error) => {
1055+
console.error('SSE error:', error);
1056+
eventSource.close();
1057+
};
1058+
1059+
.. warning::
1060+
1061+
``EventStreamResponse`` is designed for small applications with limited
1062+
concurrent connections. Because SSE keeps HTTP connections open, it consumes
1063+
server resources (memory and connection limits) for each connected client.
1064+
1065+
For high-traffic applications that need to broadcast updates to many clients
1066+
simultaneously, consider using :doc:`Mercure </mercure>`, which is built on
1067+
top of SSE but uses a dedicated hub to manage connections efficiently.
1068+
9541069
Final Thoughts
9551070
--------------
9561071

@@ -991,3 +1106,5 @@ Learn more about Controllers
9911106
.. _`Validate Filters`: https://www.php.net/manual/en/filter.constants.php
9921107
.. _`phpstan/phpdoc-parser`: https://packagist.org/packages/phpstan/phpdoc-parser
9931108
.. _`phpdocumentor/type-resolver`: https://packagist.org/packages/phpdocumentor/type-resolver
1109+
.. _`Server-Sent Events (SSE)`: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events
1110+
.. _`the WHATWG specification`: https://html.spec.whatwg.org/multipage/server-sent-events.html

mercure.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ thanks to a specific HTTP header).
3232

3333
All these features are supported in the Symfony integration.
3434

35+
.. tip::
36+
37+
For simpler use cases with limited concurrent connections (e.g. progress
38+
updates, admin dashboards, or internal tools), you can use native
39+
Server-Sent Events directly via Symfony's ``EventStreamResponse`` class.
40+
See :ref:`controller-server-sent-events` for more information.
41+
42+
Use Mercure when you need its advanced features like authorization,
43+
automatic reconnection with message recovery, broadcasting to many clients,
44+
or high-traffic scalability.
45+
3546
`In this recording`_ you can see how a Symfony web API leverages Mercure
3647
and API Platform to update in live a React app and a mobile app (React Native)
3748
generated using the API Platform client generator.

0 commit comments

Comments
 (0)