<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: vichanse</title>
    <description>The latest articles on DEV Community by vichanse (@vichanse).</description>
    <link>https://dev.to/vichanse</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F333826%2F6ac03b0d-8bc2-4b4d-963d-0ee721c583bc.jpeg</url>
      <title>DEV Community: vichanse</title>
      <link>https://dev.to/vichanse</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vichanse"/>
    <language>en</language>
    <item>
      <title>Real Time web applications with the Server Sent Events protocol. Symfony, ApiPlatform, Angular, Akita and Mercure</title>
      <dc:creator>vichanse</dc:creator>
      <pubDate>Wed, 19 Feb 2020 09:45:16 +0000</pubDate>
      <link>https://dev.to/vichanse/real-time-web-applications-with-the-server-sent-events-protocol-symfony-apiplatform-angular-akita-and-mercure-cnc</link>
      <guid>https://dev.to/vichanse/real-time-web-applications-with-the-server-sent-events-protocol-symfony-apiplatform-angular-akita-and-mercure-cnc</guid>
      <description>&lt;p&gt;In this post i will show you how to create a modern reactive application using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP 7.4&lt;/li&gt;
&lt;li&gt;Symfony 5&lt;/li&gt;
&lt;li&gt;Api Platform 2.5&lt;/li&gt;
&lt;li&gt;Mercure&lt;/li&gt;
&lt;li&gt;Angular&lt;/li&gt;
&lt;li&gt;Akita&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The work of this post is inspired by &lt;a href="https://stefanoalletti.wordpress.com/2019/06/17/just-another-poc-of-symfony-on-steroids%E2%80%A8-api-platform-vue-js-mercure-and-panther/" rel="noopener noreferrer"&gt;Stefano ALLETTI post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Server-Sent Events (SSE) is a standard that enabled Web servers to push data in real time to clients. &lt;/p&gt;

&lt;p&gt;The idea is simple: a browser can subscribe to a stream of events generated by a server, receiving updates whenever a new event occurs. This led to the birth of the popular EventSource interface, which accepts an HTTP stream connection and keeps the connection open while retrieving available data from it. The connection is kept open until closed by calling EventSource.close().&lt;/p&gt;

&lt;p&gt;Like a good self-respecting Frenchman, I love wine. That's why I decided to make an application allowing me to know in real time the availability of my favorite wine.&lt;br&gt;
In this post, we will learn how to use SSE by building a live winestore product availability demo application with Symfony, Angular and Mercure. &lt;a href="https://github.com/vichanse/symfony-angular-mercure" rel="noopener noreferrer"&gt;You can find the final code of the application in this GitHub repository.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Real-Time App with Server-Sent Events
&lt;/h2&gt;

&lt;p&gt;In order to learn how to use SSE, we are going to develop a live wine stock availability application. The application will consist of a simple web page showing a list of wines and two modals, one showing wine details and another one for editing the wine quantity.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;List&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;th&gt;Update&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsrv95r003nh21852xwf3.png" alt="Alt Text"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjb5qfjygd9wy8gn6jtor.png" alt="Alt Text"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fos4deu44bkdururzbtah.png" alt="Alt Text"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Through this real-time app, we can find wine status availability and after implementing Server-Sent Events, we will see automagically updates when the status changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Real-Time Backends with Server-Sent Events
&lt;/h3&gt;

&lt;p&gt;Create a folder called &lt;strong&gt;winestore&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For simplicity, i cloned the latest stable version of API Platform (2.5.4) into this folder. You can find inspiration from the project Github repository above.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;winestore


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Install Symfony and Api Platform
&lt;/h4&gt;

&lt;p&gt;Firstly, make sure you have installed PHP 7.2.5 or a higher version and the Composer package manager to create a new Symfony application. after that, create a new project by executing the following command in the terminal:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;composer create-project symfony/skeleton api 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now let's install some necessary bundles with composer&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Api Platform&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;composer req api 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Make sure your database configurations are set in the .env file&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;###&amp;gt; doctrine/doctrine-bundle ###&lt;/span&gt;
&lt;span class="c"&gt;# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url&lt;/span&gt;
&lt;span class="c"&gt;# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"&lt;/span&gt;
&lt;span class="c"&gt;# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&amp;amp;charset=utf8"&lt;/span&gt;
&lt;span class="c"&gt;# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml&lt;/span&gt;
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres://api-platform:!ChangeMe!@db/api?server_version&lt;span class="o"&gt;=&lt;/span&gt;12
&lt;span class="c"&gt;###&amp;lt; doctrine/doctrine-bundle ###&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Maker bundle to quickly build entities&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;composer require symfony/maker-bundle &lt;span class="nt"&gt;--dev&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's create three entities : &lt;a href="https://github.com/vichanse/symfony-angular-mercure/blob/master/api/src/Entity/Wine.php" rel="noopener noreferrer"&gt;Wine&lt;/a&gt;, &lt;a href="https://github.com/vichanse/symfony-angular-mercure/blob/master/api/src/Entity/Comment.php" rel="noopener noreferrer"&gt;Comment&lt;/a&gt; and &lt;a href="https://github.com/vichanse/symfony-angular-mercure/blob/master/api/src/Entity/MediaObject.php" rel="noopener noreferrer"&gt;MediaObject&lt;/a&gt; and we define them as &lt;strong&gt;ApiResource&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hautelook/alice bundle for managing fixtures with Alice and Faker&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; hautelook/alice-bundle 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Create a fixture file &lt;a href="https://github.com/vichanse/symfony-angular-mercure/blob/master/api/fixtures/wines.yaml" rel="noopener noreferrer"&gt;wines.yaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this create and update the database&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;bin/console doctrine:schema:drop &lt;span class="nt"&gt;--force&lt;/span&gt; &lt;span class="nt"&gt;--no-interaction&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;bin/console doctrine:schema:create &lt;span class="nt"&gt;--no-interaction&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And load your fixtures with this following command&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;bin/console hautelook:fixtures:load &lt;span class="nt"&gt;--no-interaction&lt;/span&gt; &lt;span class="nt"&gt;--purge-with-truncate&lt;/span&gt; &lt;span class="nt"&gt;-vvv&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Opening the Swagger-Frontend you can now play around adding, deleting and viewing wines.&lt;/p&gt;

&lt;p&gt;If you are using the symfony server, you need to open &lt;a href="http://localhost:8000/api" rel="noopener noreferrer"&gt;http://localhost:8000/api&lt;/a&gt;. If you use docker, the url is &lt;a href="http://localhost:8080/api" rel="noopener noreferrer"&gt;http://localhost:8080/api&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffiquihusgbpgzlj65vfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffiquihusgbpgzlj65vfj.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Mercure : Real-time made easy😛
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://mercure.rocks/" rel="noopener noreferrer"&gt;Mercure&lt;/a&gt; is an open protocol allowing to push data updates to web browsers and other HTTP clients in a convenient, fast, reliable and battery-friendly way. It is especially useful to publish real-time updates of resources served through web APIs, to reactive web and mobile applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you're following along using the API Platform distribution, you don't need to install mercure as it is already installed and configured.&lt;/p&gt;

&lt;p&gt;If you installed from scratch using symfony flex you must:&lt;/p&gt;

&lt;h5&gt;
  
  
  1) Install mercure
&lt;/h5&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt; composer require mercure


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h5&gt;
  
  
  2) Download and run a Mercure hub
&lt;/h5&gt;

&lt;p&gt;To manage persistent connections, Mercure relies on a Hub: a dedicated server that handles persistent SSE connections with the clients. The Symfony app publishes the updates to the hub, that will broadcast them to clients.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flcj43rkywopmx6933euc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flcj43rkywopmx6933euc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An official and open source (AGPL) implementation of a Hub can be downloaded as a static binary from &lt;a href="https://mercure.rocks/pricing" rel="noopener noreferrer"&gt;Mercure.rocks&lt;/a&gt;. Put the downloaded hub at the root of the project.&lt;/p&gt;
&lt;h6&gt;
  
  
  Run the following command to start it:
&lt;/h6&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt; ./mercure &lt;span class="nt"&gt;--jwt-key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'!changeMe!'&lt;/span&gt; &lt;span class="nt"&gt;--addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'localhost:3000'&lt;/span&gt; &lt;span class="nt"&gt;--allow-anonymous&lt;/span&gt; &lt;span class="nt"&gt;--cors-allowed-origins&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h6&gt;
  
  
  Configuration
&lt;/h6&gt;

&lt;p&gt;The preferred way to configure the Mercure is by using environment variables.&lt;/p&gt;

&lt;p&gt;Set the URL of your hub as the value of the MERCURE_PUBLISH_URL env var. The .env file of your project has been updated by the Flex recipe to provide example values. Set it to the URL of the Mercure Hub (&lt;a href="http://localhost:3000/.well-known/mercure" rel="noopener noreferrer"&gt;http://localhost:3000/.well-known/mercure&lt;/a&gt; by default).&lt;/p&gt;

&lt;p&gt;In addition, the Symfony application must bear a JSON Web Token (JWT) to the Mercure Hub to be authorized to publish updates.&lt;/p&gt;

&lt;p&gt;This JWT should be stored in the &lt;strong&gt;MERCURE_JWT_TOKEN&lt;/strong&gt; environment variable.&lt;/p&gt;

&lt;p&gt;To create your token got to &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;jwt.io&lt;/a&gt;. There you need to fill out two things, the payload and the secret.&lt;/p&gt;

&lt;p&gt;The JWT must be signed with the same secret key as the one used by the Hub to verify the JWT (&lt;em&gt;!changeMe!&lt;/em&gt; in our example). Its payload must contain at least the following structure to be allowed to publish:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"mercure"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"publish"&lt;/span&gt;: &lt;span class="o"&gt;[]&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that you're ready to use Mercure, you must add the &lt;strong&gt;Mercure&lt;/strong&gt; option to the Wine ApiResource entity:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;

&lt;span class="cd"&gt;/**
 * @ApiResource(mercure=true,normalizationContext={"groups"={"read"}},denormalizationContext={"groups"={"write"}})
 * @ORM\Entity(repositoryClass="App\Repository\WineRepository")
 */&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Wine&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mf"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this way API Platform will dispatch the updates to the Mercure hub every time a Wine is created, updated or deleted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building Angular with Server-Sent Events
&lt;/h3&gt;

&lt;p&gt;First thing, we need to generate an angular application. This is made very easy using the &lt;a href="https://cli.angular.io/" rel="noopener noreferrer"&gt;angular cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go in the &lt;strong&gt;winestore&lt;/strong&gt; folder and execute following commands:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# if you don't already have the cli installed&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @angular/cli
&lt;span class="c"&gt;# generate new app&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ng new api
&lt;span class="c"&gt;# cd into the app&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;api
&lt;span class="c"&gt;# Start it up&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ng serve


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Open your web-browser of choice and navigate to &lt;a href="http://localhost:4200" rel="noopener noreferrer"&gt;http://localhost:4200&lt;/a&gt; (4200 is the default port for angular apps, but you can specify whatever port your heart desires). If all went well, you should see the angular default landing page.&lt;/p&gt;

&lt;p&gt;In this post i will focus on Angular and SSE. A full example of the client application can be found &lt;a href="https://github.com/vichanse/symfony-angular-mercure/tree/master/client" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  SSE and Angular
&lt;/h4&gt;

&lt;p&gt;On the client side, we use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource" rel="noopener noreferrer"&gt;EventSource&lt;/a&gt; to receive the stream events. This object has 3 eventHandler methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;EventSource.onerror&lt;/code&gt; called when an error occurs,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EventSource.onmessage&lt;/code&gt; called when a message event is received&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EventSource.onopen&lt;/code&gt; called when an open event is received&lt;/li&gt;
&lt;/ol&gt;

&lt;h5&gt;
  
  
  EventSource Observable Wrapper
&lt;/h5&gt;

&lt;p&gt;To make it reactive and fit in an Angular application, it’s better to wrap it in an RxJS Observable.&lt;/p&gt;

&lt;p&gt;Here is the &lt;code&gt;sse.service.ts&lt;/code&gt; file&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NgZone&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SseService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NgZone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;getEventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;EventSource&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;getServerSentEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getEventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nx"&gt;eventSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The service method &lt;code&gt;getServerSentEvent&lt;/code&gt;can then be subscribed like any other observable:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sseService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getServerSentEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url/parameters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  State Management with Akita
&lt;/h4&gt;

&lt;p&gt;In order to keep track of what has been updated, we are going to use akita as a state management.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://netbasal.gitbook.io/akita/" rel="noopener noreferrer"&gt;Akita&lt;/a&gt; is a state management pattern, built on top of RxJS. It is based on objected-oriented design so developers with OOP experience should feel right at home.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frg0m2gtjywr4g2uh9g8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frg0m2gtjywr4g2uh9g8k.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉🏾 &lt;a href="https://engineering.datorama.com/10-reasons-why-you-should-start-using-akita-as-your-state-management-solution-66b63d033fec" rel="noopener noreferrer"&gt;10 Reasons Why You Should Start Using Akita as Your State Management Solution&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Akita has an API &lt;a href="https://netbasal.gitbook.io/akita/stores/event-based-apis-support" rel="noopener noreferrer"&gt;runStoreAction&lt;/a&gt; that simplify and improve the experience of working with event-based APIs such as Mercure in our example.&lt;/p&gt;
&lt;h4&gt;
  
  
  Setting Up Akita
&lt;/h4&gt;

&lt;p&gt;Adding Akita to our project is easy. We can use the NG add schematic by running the following command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

ng add @datorama/akita


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above command adds Akita, Akita's dev-tools, and Akita's schematics into our project. The next step is to create a store. We need to maintain a collection of wines, so we scaffold a new entity feature:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

ng g af wines


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This command generates a wines store, a wines query, a wines service, and a wine model for us. Those files can be found &lt;a href="https://github.com/vichanse/symfony-angular-mercure/tree/master/client/src/app/features/wines/state" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Using &lt;code&gt;runStoreAction&lt;/code&gt;in our service
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;runStoreAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StoreActions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@datorama/akita&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WinesService&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;NgEntityService&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WinesState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nf"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;APP_CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;merculeHubUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;topic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;APP_CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiWineUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/{id}`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sseService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getServerSentEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;tap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="nf"&gt;runStoreAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wines&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;StoreActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UpdateEntities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stock&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="na"&gt;entityIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our service &lt;code&gt;sync&lt;/code&gt; method we subscribe to server-sent events and update our store by running store action &lt;code&gt;UpdateEntities&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this post, we used Server-Sent Events to develop a real-time application that simulates product availability in a winestore. During the development, we had the opportunity to explore Symfony, Api Platform, Mercure, Angular and Akita. &lt;br&gt;
There is a lot more to develop such modern reactive applications. This post provides a good starting point but i highly recommend going through the docs and trying out different tools.&lt;/p&gt;

&lt;p&gt;📢 An example project containing all of the above can be found &lt;a href="https://github.com/vichanse/symfony-angular-mercure" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you enjoyed the post? Please share🤘🏾.&lt;/p&gt;

</description>
      <category>symfony</category>
      <category>apiplatform</category>
      <category>angular</category>
      <category>mercure</category>
    </item>
  </channel>
</rss>
