<?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: Rukshan J. Senanayaka</title>
    <description>The latest articles on DEV Community by Rukshan J. Senanayaka (@rukshanjs).</description>
    <link>https://dev.to/rukshanjs</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%2F892162%2F8e44578d-2b22-4cd8-84d8-7d05a60b0779.jpeg</url>
      <title>DEV Community: Rukshan J. Senanayaka</title>
      <link>https://dev.to/rukshanjs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rukshanjs"/>
    <language>en</language>
    <item>
      <title>DX_1: How we created an Optimized Search for our Faculty's Career Fair Website</title>
      <dc:creator>Rukshan J. Senanayaka</dc:creator>
      <pubDate>Mon, 19 Feb 2024 07:50:14 +0000</pubDate>
      <link>https://dev.to/rukshanjs/dx1-how-we-created-an-optimized-search-for-our-facultys-career-fair-website-1o44</link>
      <guid>https://dev.to/rukshanjs/dx1-how-we-created-an-optimized-search-for-our-facultys-career-fair-website-1o44</guid>
      <description>&lt;p&gt;Last Update - 19th Feb, 2024 | 1347&lt;/p&gt;

&lt;h2&gt;
  
  
  DX_ Series
&lt;/h2&gt;

&lt;p&gt;This is an article of my &lt;code&gt;DX_&lt;/code&gt; series where I post my developer experience one article at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yr_X91Wr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/dlzrie4qh/image/upload/v1708327902/RJS_Shared/UoM/t9lxeehdyp8wcpqfzlvi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yr_X91Wr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/dlzrie4qh/image/upload/v1708327902/RJS_Shared/UoM/t9lxeehdyp8wcpqfzlvi.gif" alt="Your Alt Text" width="600" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The annual career fair organized by our &lt;a href="https://uom.lk/itfac"&gt;Faculty of IT, University of Moratuwa&lt;/a&gt; stands as a vital bridge connecting students with top industry professionals. 2024 FIT Future Careers Career fair was held on 8th of February,2024.&lt;/p&gt;

&lt;p&gt;Recognizing the need to enhance this connection, the student body &lt;a href="http://intecs.itfac.mrt.ac.lk/"&gt;INTECS&lt;/a&gt; responsible for the event has launched a dedicated website. However, the site's search feature initially fell short of expectations. &lt;/p&gt;

&lt;p&gt;In this article, I'll share how I tackled these challenges to refine the search function, ensuring the website effectively matches the best talents with the greatest opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was the problem?
&lt;/h2&gt;

&lt;p&gt;Upon joining the career fair website team, my main focus was on deployment. However, I quickly identified a major flaw in the website's search functionality. &lt;/p&gt;

&lt;p&gt;Originally designed to support 300 students, the site inefficiently fetched all student records in one API call for every load of the search page, performing the filtering on the client side. &lt;/p&gt;

&lt;p&gt;This not only strained the website's performance but also risked overloading the API server, given our limited server resources. For instance, if even if the user views only the first page of the results, all 300 student details are already loaded, which is a huge overhead.&lt;/p&gt;

&lt;p&gt;We faced a critical need for a more efficient search solution that balanced functionality with our budget constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  My thought pattern
&lt;/h2&gt;

&lt;p&gt;Facing the search functionality issues on our career fair website, I recalled using &lt;a href="https://www.elastic.co/"&gt;Elasticsearch&lt;/a&gt; in a past project for efficient search and filtering. This experience inspired me to apply a similar solution, but cost was a concern due to our tight budget. &lt;/p&gt;

&lt;p&gt;I explored open-source options, finding &lt;a href="https://github.com/typesense/typesense"&gt;Typesense&lt;/a&gt;, a cost-effective, typo-tolerant search engine offering fast, relevant search capabilities and a managed cloud service with a free tier. &lt;/p&gt;

&lt;p&gt;Despite initially considering self-hosting, their managed cloud service - &lt;a href="https://cloud.typesense.org/"&gt;Typesense Cloud&lt;/a&gt; proved to be the most practical choice, allowing us to enhance search efficiency without financial burden or extensive setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  A little bit of problematic implementation
&lt;/h2&gt;

&lt;p&gt;Earlier implementation looked something like this. &lt;/p&gt;

&lt;p&gt;You can see when the &lt;code&gt;Student.jsx&lt;/code&gt; component of the student search page is loaded, it loads all the users from the database via the API, which is not good.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`user/get-all-users`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setAllUsers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&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;data&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 data returned from the API endpoint are then used to update the state variable &lt;code&gt;allUsers&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;allUsers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAllUsers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The users were filtered using a simple JS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter"&gt;&lt;code&gt;filter()&lt;/code&gt;&lt;/a&gt; method. This frontend filter doesn't do much other than just simply filtering the already-retrieved database data. &lt;/p&gt;

&lt;p&gt;This doesn't provide the performance improvements a dedicated search system would provide, such as typo-tolerance etc. multiple search criteria etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filterUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;selectedAreas&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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;fullName&lt;/span&gt; &lt;span class="o"&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;f_name&lt;/span&gt; &lt;span class="o"&gt;||&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;l_name&lt;/span&gt; &lt;span class="o"&gt;||&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="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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;isNameMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;preferredAreas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;preferredAreas&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preferred_area&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[]&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error parsing preferred_area:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&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;isAreaMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;selectedAreas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;preferredAreas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;selectedAreas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;area&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;isNameMatch&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;isAreaMatch&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;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;To improve the search functionality without changing the UI, I chose Typesense for its high performance and React compatibility. Typesense offers fast, typo-tolerant searches, ideal for instant feedback on user queries. &lt;/p&gt;

&lt;p&gt;I integrated it using &lt;a href="https://www.npmjs.com/package/react-instantsearch"&gt;React InstantSearch&lt;/a&gt; components &amp;amp; hooks, adapting them for Typesense to enhance search without altering the UI. The setup involved initializing a Typesense server for the search index, facilitated by Typesense’s clear documentation. &lt;/p&gt;

&lt;p&gt;This allowed for a smooth transition to Typesense’s optimized search components, ensuring easy customization and adaptability to our UI needs. Also automatic pagination and filter components are provided with this library.&lt;/p&gt;

&lt;p&gt;A Node.js program was developed to create the indexes of the fields using which we needed to filter the students contained the schema definitions as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define schema for the 'students' collection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;students&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;num_documents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fields&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uni_index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;l_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preferred_area&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string[]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skills&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string[]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cv_link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;front_picture_link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;facet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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 following is how the indexes look in the managed cloud server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0461w18u2p55fuhiv92d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0461w18u2p55fuhiv92d.png" alt="Image description" width="800" height="968"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then in the react frontend application, we consumed the search and configured which fields and the weights we need to assign to each field during the searching.&lt;br&gt;
&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;typesenseAdapter&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;TypesenseInstantsearchAdapter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TYPESENSE_SERVER_CONFIG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;additionalSearchParameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;queryBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f_name,l_name,skills,preferred_area,uni_index&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;query_by_weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3,3,2,2,1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;numTypos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;typoTokensThreshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;I tackled the challenge of integrating Typesense’s search components without altering the existing design by customizing React InstantSearch components to match the application's aesthetic. &lt;/p&gt;

&lt;p&gt;By applying custom styles and utilizing modified hooks like a debounced useSearchBox and an adapted useHits, I ensured the search functionality blended seamlessly and efficiently with the UI. &lt;/p&gt;

&lt;p&gt;This resulted in an enhanced user search experience—faster and more accurate—without disrupting the design continuity. The successful integration underscored my ability to improve functionality while maintaining the visual integrity of the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who keeps the search index updated? - Cron!
&lt;/h2&gt;

&lt;p&gt;The above mentioned Node.js application was deployed as a Cronb job to automate updating the Typesense search index, ensuring our database records were consistently synchronized for reliable search functionality. &lt;/p&gt;

&lt;p&gt;This application, designed to run as a cron job, leverages Node.js's strengths in asynchronous operations and database compatibility, making it an efficient solution for data indexing. &lt;/p&gt;

&lt;p&gt;It periodically (every 15 minutes) checks for new or modified database entries, formats them for Typesense, and uploads them to the search server on managed Typesense instance. &lt;/p&gt;

&lt;p&gt;Deployed as a &lt;a href="https://docs.digitalocean.com/products/functions/"&gt;serverless function on Digital Ocean&lt;/a&gt; using &lt;a href="https://github.com/mrbrianevans/do-functions"&gt;&lt;code&gt;do-functions&lt;/code&gt;&lt;/a&gt; and &lt;code&gt;do-functions-server&lt;/code&gt; packages, this setup streamlines the indexing process, maintaining the search feature's efficiency and reliability. Otherwise even if the students update their details and save to database, those updated details would not be returned in search results without being indexed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this instalment of DX_ series, I shared how we upgraded our faculty career fair website's search function by shifting from basic front-end filtering to an advanced, type-ahead search with Typesense. &lt;/p&gt;

&lt;p&gt;This cost-effective improvement was achieved by integrating Typesense's search engine and automating data indexing through a custom Node.js &lt;a href="https://en.wikipedia.org/wiki/Cron"&gt;cron&lt;/a&gt; job. &lt;/p&gt;

&lt;p&gt;The result was a significant enhancement in connecting students with employers, without altering the user interface, demonstrating the impact of open-source solutions and innovation in addressing project challenges and user requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/typesense/typesense-instantsearch-demo"&gt;https://github.com/typesense/typesense-instantsearch-demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.typesense.org/"&gt;https://cloud.typesense.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lucene.apache.org/core/"&gt;https://lucene.apache.org/core/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digitalocean.com/products/app-platform"&gt;https://www.digitalocean.com/products/app-platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  BONUS: Tech stack for the whole project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Frontend - React.js&lt;/li&gt;
&lt;li&gt;Backend - Node.js&lt;/li&gt;
&lt;li&gt;Database - MySQL&lt;/li&gt;
&lt;li&gt;Search - Typesense&lt;/li&gt;
&lt;li&gt;Image storage - Firebase storage&lt;/li&gt;
&lt;li&gt;User tour - driver.js&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Although I contributed to the search feature, I was just one member of the whole project. The whole project was a successful effort of many undergraduates of the Faculty from different batches.&lt;/li&gt;
&lt;li&gt;Code snippets and screenshots in this article may not be production-ready. They are edited as needed for educational purposes.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algolia</category>
      <category>search</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Part 3/3 - How to create a server-side timer using WebSockets (with Socket.IO), NestJS and Flutter</title>
      <dc:creator>Rukshan J. Senanayaka</dc:creator>
      <pubDate>Mon, 15 Aug 2022 15:30:00 +0000</pubDate>
      <link>https://dev.to/rukshanjs/part-33-how-to-create-a-server-side-timer-using-websockets-with-socketio-nestjs-and-flutter-10am</link>
      <guid>https://dev.to/rukshanjs/part-33-how-to-create-a-server-side-timer-using-websockets-with-socketio-nestjs-and-flutter-10am</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this part 3/3, I will show you how to implement WebSocket functionality in a new Flutter app and connect it to the NestJS backend server application that we implemented in part 2/3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The code segments in this article are written for reading purpose, the full source code is available on my GitHub, linked at the end.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Create a new flutter project
&lt;/h2&gt;

&lt;p&gt;I’m using Flutter 2.5.2 with null safety. A later version should support as well, but didn’t test with a newer version. &lt;/p&gt;

&lt;p&gt;You can create a new Flutter project by executing following command.&lt;/p&gt;

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

flutter create server_timer_frontend


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

&lt;/div&gt;

&lt;p&gt;This can take a couple of minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Install Socket.IO client package for Flutter
&lt;/h2&gt;

&lt;p&gt;After your Flutter project is created, we can add the package of the socket client we are going to use. &lt;/p&gt;

&lt;p&gt;I have tested several packages for this purpose. It was very difficulty to find a proper one because for our WebSocket connection to work successfully, both the backend Socket.IO version and the frontend client Socket.IO version &lt;strong&gt;must&lt;/strong&gt; be compatible. So I had to do some trial and error and some online research to find out the best package.&lt;/p&gt;

&lt;p&gt;The best package I found is named &lt;strong&gt;socket_io_client&lt;/strong&gt; and its latest update (version 2.0.0) was released a few weeks ago (July of 2022). You can see more info at &lt;a href="https://pub.dev/packages/socket_io_client" rel="noopener noreferrer"&gt;https://pub.dev/packages/socket_io_client&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;You can install it by running&lt;/p&gt;

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

flutter pub add socket_io_client


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

&lt;/div&gt;

&lt;p&gt;One side note - the official Flutter documentation shows a way to implement WebSocket connection using a package called &lt;strong&gt;web_socket_channel&lt;/strong&gt;, it was not working for me with my NestJS backend.&lt;/p&gt;

&lt;p&gt;Before writing code, create a new folder called &lt;strong&gt;sockets&lt;/strong&gt; within the &lt;strong&gt;lib&lt;/strong&gt; folder, where we will put all of our code related to sockets. You can refactor them as however you like later.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create socket service (socket_service.dart)
&lt;/h2&gt;

&lt;p&gt;In the frontend, we have to have several files related to this implementation. One important file is &lt;strong&gt;socket_service.dart&lt;/strong&gt;. Here we define methods such as &lt;code&gt;connectAndListenToSocket(){}&lt;/code&gt;, &lt;code&gt;disconnectSocket(){}&lt;/code&gt;, &lt;code&gt;disposeSocket(){}&lt;/code&gt; which are important for our WebSocket communication.&lt;/p&gt;

&lt;p&gt;The following is the abstract structure of the &lt;code&gt;SocketService&lt;/code&gt; class.&lt;/p&gt;

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

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SocketService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Socket&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// connectAndListenToSocket(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// disconnectSocket(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// disposeSocket(){}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;First, write a method named &lt;code&gt;connectAndListenToSocket(String authToken, String deviceId){}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, within the &lt;code&gt;connectAndListenToSocket(){}&lt;/code&gt; method, we have to set up the connection to the backend. Since the backend requires a user auth token, we have to send that as well with this. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please edit the IP address according to your network connection. Otherwise this will not connect to the NestJS backend. You can view your IP from network settings of your computer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The default port of the NestJS app is 3000. That is written in right side of the semicolon (&lt;code&gt;:&lt;/code&gt;) as in the following code. (Replace the 192.168.X.X with your actual IP address)&lt;/p&gt;

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

    &lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;'http://192.168.X.X:3000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your network IP &lt;/span&gt;
        &lt;span class="n"&gt;OptionBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTransports&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s"&gt;'websocket'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAuth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
              &lt;span class="s"&gt;'token'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'Bearer &lt;/span&gt;&lt;span class="si"&gt;$authToken&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
              &lt;span class="s"&gt;'deviceId'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disableAutoConnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It takes two parameters, the user &lt;code&gt;authToken&lt;/code&gt; (you can get this from Firebase Auth or any other auth method), the &lt;code&gt;deviceId&lt;/code&gt; (you can get this by using &lt;strong&gt;device_info_plus&lt;/strong&gt; package (&lt;a href="https://pub.dev/packages/device_info_plus" rel="noopener noreferrer"&gt;https://pub.dev/packages/device_info_plus&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;We can use the &lt;code&gt;setQuery()&lt;/code&gt; method to send query parameters in the body of our message. This body can be caught by using the &lt;code&gt;@MessageBody()&lt;/code&gt; decorator in NestJS.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;connectAndListenToSocket(){}&lt;/code&gt; method will try to connect the socket to backend. &lt;/p&gt;

&lt;p&gt;Next, write the following piece of code which tries to actually start the WebSocket connection if it is not yet started.&lt;/p&gt;

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

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'connecting....'&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;Next, inside this &lt;code&gt;connectAndListenToSocket(){}&lt;/code&gt; method, we can set up some listeners which are for main events such as &lt;code&gt;onConnect(){}&lt;/code&gt;, &lt;code&gt;onDisconnect(){}&lt;/code&gt;, &lt;code&gt;onError(){}&lt;/code&gt; and &lt;code&gt;onConnectError(){}&lt;/code&gt; etc. as below.&lt;/p&gt;

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

    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;onConnect&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'connected and listening to socket!.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;onDisconnect&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'disconnected from socket!.'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;onConnectError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)});&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We can listen in the frontend, to the custom events set by us in the backend. Therefore, whenever the backend server sends a message under that event, this listener would be fired.&lt;/p&gt;

&lt;p&gt;Next, write the following listener which listens to the &lt;code&gt;tick&lt;/code&gt; event of our server-side timer app. We defined this event in the backend NestJS application.&lt;/p&gt;

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

    &lt;span class="c1"&gt;// When the message event 'tick' received from server, that data is added to a stream 'streamSocket'.&lt;/span&gt;
    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimerEvents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tick&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;streamSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'timer'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&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 that listener, we add the received data to a Flutter &lt;code&gt;StreamController&lt;/code&gt; using the &lt;code&gt;streamSocket.addResponse(data["timer"].toString());&lt;/code&gt; method call. The variable &lt;code&gt;data&lt;/code&gt; is the payload that we send from the backend. The value of the key &lt;code&gt;timer&lt;/code&gt; contains the actual time in seconds.&lt;/p&gt;




&lt;p&gt;Finally write the two functions for disconnecting the socket and disposing the socket.&lt;/p&gt;

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

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;disconnectSocket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;disposeSocket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;dispose&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 difference between the two is, &lt;code&gt;disconnectSocket(){}&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; remove the listeners created in our Flutter app for the WebSocket connection, but the &lt;code&gt;disposeSocket(){}&lt;/code&gt; does.&lt;/p&gt;

&lt;p&gt;If we connect to the same socket again, the multiple listeners would be present if we had only disconnected, and not disposed the WebSocket connection. Therefore, to start everything fresh next time, it's a good idea to &lt;code&gt;disposeSocket(){}&lt;/code&gt; the socket when need to finish the WebSocket connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Create timer service (timer_service.dart)
&lt;/h2&gt;

&lt;p&gt;In this service file, I will define the methods &lt;code&gt;startServerTimer&lt;/code&gt; and &lt;code&gt;stopServerTimer&lt;/code&gt;.&lt;/p&gt;

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

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimerService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// startServerTimer(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// stopServerTimer(){}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Whenever we want to send something to the backend, we use the &lt;code&gt;socket.emit()&lt;/code&gt; method call. This method call would accept a first string parameter - the name of the event, and then the second parameter is the data to be sent as a JSON object. &lt;/p&gt;

&lt;p&gt;We can accept this JSON object in our backend easily - using the &lt;code&gt;@MessageBody()&lt;/code&gt; decorator in NestJS.&lt;/p&gt;

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

  &lt;span class="c1"&gt;// Timer methods.&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;startServerTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimerEvents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timerStart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"dur"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;duration&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No socket connection found."&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="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;stopServerTimer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TimerEvents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timerStop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No socket connection found."&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;h2&gt;
  
  
  5. Stream socket (stream_socket.dart)
&lt;/h2&gt;

&lt;p&gt;This file is not essential for the socket implementation but I’m using it to store data received from the WebSocket connection and use it to update the UI easily using a &lt;code&gt;StreamBuilder&lt;/code&gt; Flutter widget in my countdown timer example.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StreamSocket&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt; &lt;span class="n"&gt;_socketResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamController&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kt"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;addResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_socketResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sink&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;getResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_socketResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asBroadcastStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Stream&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;StreamSocket&lt;/span&gt; &lt;span class="n"&gt;streamSocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StreamSocket&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Of course there can be many other alternative ways to do this part. I would love to hear them in the comments below.&lt;/p&gt;

&lt;p&gt;Don’t forget to import this file inside the &lt;strong&gt;socket_service.dart&lt;/strong&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Events file
&lt;/h2&gt;

&lt;p&gt;In the flutter side also, I’m maintaining a &lt;strong&gt;socket_events.dart&lt;/strong&gt; file to keep the needed socket events as enums, just like we did in NestJS in the part 2/3 of this tutorial series.&lt;/p&gt;

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

&lt;span class="kt"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;TimerEvents&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;timerStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;timerStop&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;Don’t forget to import this file inside the &lt;strong&gt;socket_service.dart&lt;/strong&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Add the UI to display socket data (main.dart)
&lt;/h2&gt;

&lt;p&gt;The abstract structure of the &lt;strong&gt;main.dart&lt;/strong&gt; is as follows.&lt;/p&gt;

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

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Setup MaterialApp&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyHomePage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Initiate the state.&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_MyHomePageState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// _convertToDisplayTime(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// initState(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// build(){}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;First, clear out the comments from the default Flutter application.&lt;/p&gt;

&lt;p&gt;Next, the important thing is, we have to write code inside the &lt;code&gt;_MyHomePageState(){}&lt;/code&gt; class. Since the code is a bit long, I'll mention only the important things here, because you can get the full source code from the GitHub link at the end of the article.&lt;/p&gt;

&lt;p&gt;We declare following function to convert the data from our backend to proper timer format for display.&lt;/p&gt;

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

  &lt;span class="c1"&gt;// This is to convert the time in seconds to a string with the format '00:00'.&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_convertToDisplayTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;twoDigits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;padLeft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;twoDigitMinutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;twoDigits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inMinutes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;twoDigitSeconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;twoDigits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inSeconds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inHours&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$twoDigitMinutes&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;$twoDigitSeconds&lt;/span&gt;&lt;span class="s"&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;return&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${twoDigits(duration.inHours)}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;$twoDigitMinutes&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;$twoDigitSeconds&lt;/span&gt;&lt;span class="s"&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;When the WebSocket connection is established and connected, we are rendering a &lt;code&gt;StreamBuilder&lt;/code&gt; widget on screen to display the data from that connection using the &lt;strong&gt;stream_socket.dart&lt;/strong&gt; file we created earlier.&lt;/p&gt;

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

            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;StreamBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nl"&gt;stream:&lt;/span&gt; &lt;span class="n"&gt;streamSocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Socket Status : TICKING TIMEOUT"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="s"&gt;'Socket Status : TIMER TICKING - &lt;/span&gt;&lt;span class="si"&gt;${_convertToDisplayTime(
                              Duration(
                                seconds: int.parse(snapshot.data.toString()),
                              ),
                            ).toString()}&lt;/span&gt;&lt;span class="s"&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Socket Status : CONNECTED"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Socket Status : DISCONNECTED"&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="o"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Socket Status : DISCONNECTED'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Then we have to create the buttons which are there for the functions such as connect socket, disconnect, dispose, start timer and stop timer.&lt;/p&gt;




&lt;p&gt;Here is the &lt;strong&gt;Connect&lt;/strong&gt; button code.&lt;/p&gt;

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

            &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Populate these two values with your own values.&lt;/span&gt;
                &lt;span class="c1"&gt;// Get token from Firebase Auth or other authentication service.&lt;/span&gt;
                &lt;span class="c1"&gt;// Get deviceId using `device_info_plus` package.&lt;/span&gt;
                &lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connectAndListenToSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'token'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'deviceId'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Start a socket channel with the server.&lt;/span&gt;

                &lt;span class="c1"&gt;// Update the UI when anything change in the socket.&lt;/span&gt;
                &lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;onAny&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="n"&gt;setState&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="nl"&gt;label:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Connect Socket'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;connect_without_contact_rounded&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;styleFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;primary:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;purple&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;Here are the &lt;strong&gt;Start timer&lt;/strong&gt; and &lt;strong&gt;Stop timer&lt;/strong&gt; buttons code.&lt;/p&gt;

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

            &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;TimerService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startServerTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Start the server-side timer with 10 seconds.&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Start Server Countdown'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;styleFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;primary:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;green&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&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="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;TimerService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stopServerTimer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Stop Server Countdown'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timer_off&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;styleFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;primary:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&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;Here we are just calling the methods defined in &lt;code&gt;TimerService&lt;/code&gt; to start a timer on the NestJS server or to stop it.&lt;/p&gt;




&lt;p&gt;The disconnect socket does not remove the listeners, but dispose does. You can see the code for the relevant &lt;strong&gt;Disconnect socket&lt;/strong&gt; and &lt;strong&gt;Dispose socket&lt;/strong&gt; buttons below.&lt;/p&gt;

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

            &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Disconnect from the socket. (Does not remove the listeners.)&lt;/span&gt;
                &lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disconnectSocket&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Disconnect Socket&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;(Does not remove the listeners)'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove_done&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;styleFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;primary:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;purple&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="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="nl"&gt;onPressed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Disconnect the socket and remove the listeners.&lt;/span&gt;
                &lt;span class="n"&gt;SocketService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disposeSocket&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="nl"&gt;label:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Dispose Socket&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;(Fresh Start - removes all listeners)'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;icon:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Icons&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
              &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;ElevatedButton&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;styleFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nl"&gt;primary:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;purple&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;That is all about the frontend UI part of the code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Common errors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Error 1
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebSocketException: Connection to '&lt;a href="http://192.168.X.X:3000/socket.io/?deviceId=deviceId&amp;amp;EIO=4&amp;amp;transport=websocket#" rel="noopener noreferrer"&gt;http://192.168.X.X:3000/socket.io/?deviceId=deviceId&amp;amp;EIO=4&amp;amp;transport=websocket#&lt;/a&gt;' was not upgraded to websocket&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Make sure you have correctly set the `SocketsGateway` as a **provider** in **app.module.ts** in the NestJS backend.

- Error 2

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

&lt;/div&gt;



&lt;p&gt;SocketException: OS Error: Network is unreachable, errno = 101, address = 192.168.X.X, port = XXXXX&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Make sure you are connected to the correct network connection and that you are using the correct IP.

## Conclusion

That’s it. We have completed the flutter frontend implementation of WebSocket communication.

Hooray! 🥳🎉🎉🎉🥳🥳🥳🎉🥳🎉🎉 You've reached this far! Give yourself a pat on the back - you deserve it. 

I hope now you have some idea on how to implement a server-side timer using WebSockets(with Socket.IO), NestJS and Flutter. There may have been alternative ways to do some of the things that I have done in this tutorial series and I would love to hear about them in the comments section below.

Also, don't hesitate to point out any mistakes that I have made because I'm also learning this stuff! 

Please share your solution as well if you follow this series and make your own - I would love to see it. Have a wonderful day - Peace out ✌️

## Support me!

Do you think I deserve a cup of coffee for this article? 😃

&amp;lt;a href="https://www.buymeacoffee.com/rukshanjs" target="_blank"&amp;gt;&amp;lt;img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" &amp;gt;&amp;lt;/a&amp;gt;

## Video

[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/kx7LQNNmTX4/0.jpg)](https://www.youtube.com/watch?v=kx7LQNNmTX4)

## Source code

You can find the full source code of the project at my GitHub, https://github.com/RukshanJS/websockets-nestjs-flutter

## References

1. socket_io_client connection issue - https://stackoverflow.com/q/71679446
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>websockets</category>
      <category>flutter</category>
      <category>nestjs</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Part 2/3 - How to create a server-side timer using WebSockets (with Socket.IO), NestJS and Flutter</title>
      <dc:creator>Rukshan J. Senanayaka</dc:creator>
      <pubDate>Mon, 15 Aug 2022 15:30:00 +0000</pubDate>
      <link>https://dev.to/rukshanjs/part-23-how-to-create-a-server-side-timer-using-websockets-with-socketio-nestjs-and-flutter-4o3n</link>
      <guid>https://dev.to/rukshanjs/part-23-how-to-create-a-server-side-timer-using-websockets-with-socketio-nestjs-and-flutter-4o3n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this part (part 2/3), I will discuss how to implement the backend server application using NestJS to establish a WebSocket connection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The code segments in this article are written for reading purpose, the full source code is available on my GitHub, linked at the end.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;NestJS is a wonderful backend framework to develop mid to large scale projects. It supports WebSockets officially and a very straightforward setup is available to get things going. &lt;/p&gt;

&lt;p&gt;I am considering a single stateful server of NestJS and I will avoid &lt;strong&gt;Redis&lt;/strong&gt; and &lt;strong&gt;microservices&lt;/strong&gt; for now but they would be advisable to use for a fully-scalable application, to keep track of users connected to each WebSocket connection.&lt;/p&gt;

&lt;p&gt;I mentioned this stateful because to keep track of all the connected users and their timers, a single variable called &lt;code&gt;userTimers&lt;/code&gt; is maintained in our backend app.&lt;/p&gt;

&lt;p&gt;That means if the server shuts down for some reason, that state would be lost and we would loose the timers for the users. If we use Redis and microservices knowledge, we could avoid this problem. But I think that should be for another topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract architecture of our server-side timer app
&lt;/h2&gt;

&lt;p&gt;The following diagram gives an abstract idea of the system we are going to implement. The method calls in the diagram are the ones usually used in a WebSocket connection. If you don't understand it fully, please go through this tutorial series again after reading all 3 parts for the first time. I have tried to simplify things as much as possible.&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%2Fuploads%2Farticles%2Fzmj3gfkhn8p5fbr6gk5n.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%2Fuploads%2Farticles%2Fzmj3gfkhn8p5fbr6gk5n.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The things that we have to do for the backend implementation are the following steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start a new NestJS project
&lt;/h2&gt;

&lt;p&gt;Please install NestJS on your computer if you don't have already, by running the following command.&lt;/p&gt;

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

npm i -g @nestjs/cli


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

&lt;/div&gt;

&lt;p&gt;Then create a new NestJS project.&lt;/p&gt;

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

nest new server-timer-backend


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  2. Install the needed packages
&lt;/h2&gt;

&lt;p&gt;The two packages that we require are provided by NestJS developers. Since I’ve tested this setup with NestJS version 8.0.0 (Although the latest version is 9), I’ll be using an earlier, supported version of the required packages. This setup is working as of early August, 2022.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@nestjs/websockets@8.4.7&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@nestjs/platform-socket.io@8.4.7&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can install both of them by running the following command in the terminal. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

npm i --save @nestjs/websockets@8.4.7 @nestjs/platform-socket.io@8.4.7


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  3. Create a sockets gateway (sockets.gateway.ts)
&lt;/h2&gt;

&lt;p&gt;Firstly, I’ll create a folder named &lt;strong&gt;sockets&lt;/strong&gt; where we will keep all of our files required for WebSocket communication for now. You can refactor these files in anyway you like. &lt;/p&gt;

&lt;p&gt;Create a new file called &lt;strong&gt;sockets.gateway.ts&lt;/strong&gt;. &lt;/p&gt;



&lt;p&gt;Here the gateway file can be thought of as the main file in the backend related to WebSocket communication. It handles the &lt;strong&gt;connection&lt;/strong&gt;, &lt;strong&gt;disconnection&lt;/strong&gt; and any other &lt;strong&gt;message event&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;This &lt;code&gt;SocketsGateway&lt;/code&gt; class implements two interfaces which are provided by the &lt;strong&gt;@nestjs/websockets&lt;/strong&gt; package.&lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;@WebSocketServer()&lt;/code&gt; decorator provided by the above package to tell NestJS to inject the WebSocket server.&lt;/p&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="nd"&gt;WebSocketGateway&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;SocketsGateway&lt;/span&gt;
  &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnGatewayConnection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnGatewayDisconnect&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;WebSocketServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// handleConnection(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// handleDisconnect(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// startMyTimer(){}&lt;/span&gt;

  &lt;span class="c1"&gt;// stopMyTimer(){}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;




&lt;p&gt;I mentioned that an agreed-upon communication channel has to be created between client and server to start WebSocket communication. This agreement is established via a &lt;strong&gt;handshake&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Whenever this handshake is established, the &lt;code&gt;handleConnection(){}&lt;/code&gt; method is invoked. This handshake is a real thing and its details can be accessed via &lt;code&gt;client.handshake&lt;/code&gt; as a JSON with various information related to this WebSocket connection.&lt;/p&gt;

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

  &lt;span class="nf"&gt;handleConnection&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;ConnectedSocket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log(&lt;/span&gt;
    &lt;span class="c1"&gt;//   `user ${client.user.id} with socket ${client.id} connected with device ${client.handshake?.query?.deviceId}`,&lt;/span&gt;
    &lt;span class="c1"&gt;// );&lt;/span&gt;

    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;getUserDeviceRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handshake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;&lt;code&gt;handleDisconnect(){}&lt;/code&gt; is more or less similar and it is invoked when an already connected socket is disconnected. You still have access to the handshake via &lt;code&gt;client.handshake&lt;/code&gt;.&lt;/p&gt;

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

  &lt;span class="nf"&gt;handleDisconnect&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;ConnectedSocket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// console.log(&lt;/span&gt;
    &lt;span class="c1"&gt;//   `user ${client.user.id} with socket ${client.id} with device ${client.handshake?.query?.deviceId} DISCONNECTED`,&lt;/span&gt;
    &lt;span class="c1"&gt;// );&lt;/span&gt;

    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;getUserDeviceRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handshake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;A message event is a user-defined event to which the server &lt;strong&gt;listens&lt;/strong&gt; (or &lt;strong&gt;subscribes&lt;/strong&gt;). When a client (for example our flutter app) sends a message under that event, the server will execute whatever code is inside this listener method for that particular message event.&lt;/p&gt;

&lt;p&gt;We are using two message events in our timer app - &lt;code&gt;timerStart&lt;/code&gt; and &lt;code&gt;timerStop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;startMyTimer(){}&lt;/code&gt; method, I use the &lt;code&gt;@MessageBody()&lt;/code&gt; decorator to catch the payload sent from the client to server via the WebSocket connection. Here, the duration of the timer to be started in seconds, is contained in &lt;code&gt;body.dur&lt;/code&gt; value. (We set this in our Flutter app)&lt;/p&gt;

&lt;p&gt;In this example, I stop any existing timer for a user device before starting a new timer for that device.&lt;/p&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="nd"&gt;SubscribeMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TimerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timerStart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nf"&gt;startMyTimer&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;ConnectedSocket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;MessageBody&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Stop any existing timer for this user device.&lt;/span&gt;
    &lt;span class="nf"&gt;stopTimerForUserDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handshake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Start a new timer for this user device.&lt;/span&gt;
    &lt;span class="nf"&gt;startTimerForUserDevice&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;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handshake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dur&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Timer duration&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;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="nd"&gt;SubscribeMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TimerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timerStop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="nf"&gt;stopMyTimer&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;ConnectedSocket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Stop current timer for this user device.&lt;/span&gt;
    &lt;span class="nf"&gt;stopTimerForUserDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handshake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&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;h2&gt;
  
  
  4. Create a socket adapter (authenticated-socket.adapter.ts)
&lt;/h2&gt;

&lt;p&gt;When we use WebSockets, we want to only allow authenticated users to connect with our backend WebSocket service. For that, we have to have a custom socket adapter (although one is given to us by default). This will make sure each and every request to establish a WebSocket connection will be made by a user authenticated in your backend. &lt;/p&gt;



&lt;p&gt;First, create a class named &lt;code&gt;AuthenticatedSocketAdapter&lt;/code&gt; which extends the &lt;code&gt;IoAdapter&lt;/code&gt; from &lt;code&gt;@nestjs/platform-socket.io&lt;/code&gt; package as below.&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;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthenticatedSocketAdapter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;IoAdapter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// constructor(){}&lt;/span&gt;

   &lt;span class="c1"&gt;// createIOServer(){}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Next, write the constructor for this class. Here, if you have an &lt;strong&gt;AuthService&lt;/strong&gt; implemented using Firebase Auth or any other auth service of your choice, just uncomment the commented lines shown in the code snippet below. &lt;/p&gt;

&lt;p&gt;This authentication can be done using Firebase Auth or any other auth service of your choice and it doesn’t matter, as long as it will throw an exception for an unauthorized user. If the user is authorized, the WebSocket connection is established and if not, it is terminated by throwing &lt;code&gt;Authentication error&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;If you don't have such an implementation yet, don't worry just leave these as is - we can test the implementation without authentication also.&lt;/p&gt;

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

  &lt;span class="c1"&gt;// private readonly authService:AuthService;&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;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;INestApplicationContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// this.authService = this.app.get(AuthService);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We'll cover authentication in another tutorial so that the WebSocket connections will only be available to authenticated users.&lt;/p&gt;




&lt;p&gt;Next, create a method named &lt;code&gt;createIOServer()&lt;/code&gt;. Inside it, we are writing a middleware to grab the auth token from the handshake of the WebSocket connection, and authenticate that token using an &lt;strong&gt;AuthService&lt;/strong&gt; (AuthService file is not implemented in this tutorial - if you don't have one yourself, you can ignore it for now because we don't need authentication for testing).&lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;token&lt;/code&gt; is valid, the AuthService should return the relevant &lt;code&gt;user&lt;/code&gt; from the &lt;code&gt;authenticateToken()&lt;/code&gt; method. This returned &lt;code&gt;user&lt;/code&gt; value is assigned to the &lt;code&gt;socket.user&lt;/code&gt; variable. It is because we do this, we are able to access the relevant user as &lt;code&gt;client.user&lt;/code&gt; by using the &lt;code&gt;@ConnectedSocket()&lt;/code&gt; decorator in the &lt;strong&gt;sockets.gateway.ts&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;If the token is invalid, it will throw an error and the WebSocket connection would not be established.&lt;/p&gt;

&lt;p&gt;Here also, if you have an &lt;strong&gt;AuthService&lt;/strong&gt; implemented using Firebase Auth or any other auth service of your choice, just uncomment the commented lines shown in the code snippet below.&lt;/p&gt;

&lt;p&gt;Please note that I have imported the &lt;code&gt;Server&lt;/code&gt; type from &lt;code&gt;socket.io&lt;/code&gt; library to our application as,&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;Server&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;socket.io&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This is how I have imported &lt;code&gt;Server&lt;/code&gt; type in &lt;strong&gt;sockets.gateway.ts&lt;/strong&gt;, &lt;strong&gt;rooms.ts&lt;/strong&gt;, &lt;strong&gt;events.ts&lt;/strong&gt; files as well.&lt;/p&gt;

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

  &lt;span class="nf"&gt;createIOServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&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;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createIOServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&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="na"&gt;tokenPayload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handshake&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tokenPayload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Token not provided&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokenPayload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer&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;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid authentication method. Only Bearer is supported.&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="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="c1"&gt;// const user = await this.authService.authenticateToken(token);&lt;/span&gt;
        &lt;span class="c1"&gt;// socket.user = user;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authentication error&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="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;server&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;h2&gt;
  
  
  5. Enable WebSockets in main.ts
&lt;/h2&gt;

&lt;p&gt;In order to use the &lt;code&gt;AuthenticatedSocketAdapter&lt;/code&gt;, the custom WebSocket adapter that we created, we should add the following code to &lt;strong&gt;main.ts&lt;/strong&gt; file as below.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;NestFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppModule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useWebSocketAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AuthenticatedSocketAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Add our custom socket adapter.&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Don’t forget to set the &lt;code&gt;SocketsGateway&lt;/code&gt; as a provider in the &lt;strong&gt;app.module.ts&lt;/strong&gt;&lt;/p&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="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;controllers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppController&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SocketsGateway&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;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  6. Create rooms.ts helper file
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;rooms.ts&lt;/strong&gt; is there to keep track of the connected users. This is the place where I will write the application logic related to our server-side timer. The methods in this file would send messages to clients as required. &lt;/p&gt;



&lt;p&gt;First, create a global variable &lt;code&gt;userTimers&lt;/code&gt;, to keep track of the connected users and their timers. Here, the keys of the object are the &lt;code&gt;userId + deviceId&lt;/code&gt; combinations. The values are the &lt;code&gt;timer&lt;/code&gt;s of each user.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;userTimers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt; &lt;span class="c1"&gt;// To keep track of timers for each user.&lt;/span&gt;


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

&lt;/div&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%2Fuploads%2Farticles%2Fa58qo0rv6xywdcn8k8cu.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%2Fuploads%2Farticles%2Fa58qo0rv6xywdcn8k8cu.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how the &lt;code&gt;userTimers&lt;/code&gt; variable would look like.&lt;/p&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user001device001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* data of timer1 */&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user002device002&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* data of timer2 */&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user003device003&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* data of timer3 */&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;But for more scalable applications, an approach which uses &lt;strong&gt;Redis&lt;/strong&gt; and &lt;strong&gt;microservices&lt;/strong&gt; and thereby making our backend application stateless would be advisable - then unexpected shutdowns of the backend server would not cause loss of user timers. This is because we can use Redis as a high speed key-value store.&lt;/p&gt;




&lt;p&gt;Next, create a method &lt;code&gt;getUserDeviceRoom(){}&lt;/code&gt;, to keep track of a single device of a user, based on &lt;code&gt;userId&lt;/code&gt; and the &lt;code&gt;deviceId&lt;/code&gt;. It returns the &lt;strong&gt;roomName&lt;/strong&gt; as a string for that user device.&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUserDeviceRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;deviceId&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`user:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-device:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deviceId&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This way, we can use this returned &lt;strong&gt;roomName&lt;/strong&gt; to create a conceptual room for that user device. &lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;handleConnection(){}&lt;/code&gt; method, the connected client device &lt;strong&gt;join&lt;/strong&gt; to this room. Then we can send a message to this specific device of a user, by sending that message to that room.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;handleDisconnect(){}&lt;/code&gt; method, the connected client device &lt;strong&gt;leave&lt;/strong&gt; from this room. We can no longer send messages to that user device until it establishes a socket connection again.&lt;/p&gt;




&lt;p&gt;Next, create the method &lt;code&gt;sendToUserDevice(){}&lt;/code&gt; to send the actual message using WebSockets.&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendToUserDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;userId&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;deviceId&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;event&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;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getUserDeviceRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;emit&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;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Actually send the message to the user device via WebSocket channel.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;




&lt;p&gt;Next, we have to implement the methods to start the timer and stop the timer. I am using the &lt;code&gt;setInterval()&lt;/code&gt; method of JavaScript to create a counter, and that &lt;code&gt;counter&lt;/code&gt; value is sent to the user device in each &lt;strong&gt;tick&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the same time, when a timer is created, I add it to a variable &lt;code&gt;timer&lt;/code&gt; and it is stored in the variable &lt;code&gt;userTimers&lt;/code&gt; that I mentioned in the beginning.&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startTimerForUserDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;userId&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;deviceId&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;dur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Set initial counter value to timer duration `dur` (in seconds).&lt;/span&gt;

  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&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="s2"&gt;`counting &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;counter&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;sendToUserDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TimerEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// Send tick message to user device.&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Stop timer for this user.&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="s2"&gt;`user &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; has a timeout`&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;userTimers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Store timer for this user device.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We store the &lt;code&gt;timer&lt;/code&gt;s in the &lt;code&gt;userTimers&lt;/code&gt; variable because we need a reference to the timer of each user, to stop it later if the client requests (using the Flutter app) to stop the server-side timer for that device. The relevant code is shown below.&lt;/p&gt;

&lt;p&gt;After clearing the interval for that timer, we also delete it from the &lt;code&gt;userTimers&lt;/code&gt; object, to release the memory allocated for it.&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;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stopTimerForUserDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;deviceId&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userTimers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Stop the timer for this user device.&lt;/span&gt;

  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;userTimers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;deviceId&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// Delete the timer for this user device from the `userTimers` object.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Don’t forget to import this file inside the &lt;strong&gt;sockets.gateway.ts&lt;/strong&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Create events.ts helper file
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;events.ts&lt;/strong&gt; file just contains an &lt;code&gt;enum&lt;/code&gt; to easily keep track of the &lt;strong&gt;message events&lt;/strong&gt; I mentioned earlier.&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;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;TimerEvents&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;tick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tick&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;timerStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;timerStart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;timerStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;timerStop&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The reason to use enums is because this way we can use the variable name of the enum instead of typing the exact event name as a string everywhere that we need it - this reduces errors due to typing mistakes.&lt;/p&gt;

&lt;p&gt;Don’t forget to import this file inside the &lt;strong&gt;sockets.gateway.ts&lt;/strong&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We wrote code in the following files in our NestJS server application to enable WebSocket communication. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sockets.gateway.ts&lt;/li&gt;
&lt;li&gt;authenticated-sockets.adapter.ts&lt;/li&gt;
&lt;li&gt;rooms.ts&lt;/li&gt;
&lt;li&gt;events.ts&lt;/li&gt;
&lt;li&gt;app.module.ts&lt;/li&gt;
&lt;li&gt;main.ts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although it is recommended to restructure these files according to a proper standard in a production application, this would be more than enough for us for now. &lt;/p&gt;

&lt;p&gt;When you are done with this section, please move onto the next section of this 3-part series to see how to setup the flutter front end to connect to this backend WebSocket implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Support me!
&lt;/h2&gt;

&lt;p&gt;Do you think I deserve a cup of coffee for this article? 😃&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.buymeacoffee.com/rukshanjs" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.buymeacoffee.com%2Fbuttons%2Fv2%2Fdefault-yellow.png" alt="Buy Me A Coffee"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Video
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=EFEtg-vL92k" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.youtube.com%2Fvi%2FEFEtg-vL92k%2F0.jpg" alt="IMAGE ALT TEXT HERE"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Source code
&lt;/h2&gt;

&lt;p&gt;You can find the full source code of the project at my GitHub, &lt;a href="https://github.com/RukshanJS/websockets-nestjs-flutter" rel="noopener noreferrer"&gt;https://github.com/RukshanJS/websockets-nestjs-flutter&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Official NestJS docs on sockets -&lt;a href="https://docs.nestjs.com/websockets/gateways" rel="noopener noreferrer"&gt;https://docs.nestjs.com/websockets/gateways&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Why Socket.IO is needed? - &lt;a href="https://stackoverflow.com/a/32811489/8995555" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/32811489/8995555&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Why two packages are needed for WebSocket implementation in NestJS? - &lt;a href="https://stackoverflow.com/a/73339808/8995555" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/73339808/8995555&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>websockets</category>
      <category>flutter</category>
      <category>nestjs</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Part 1/3 - How to create a server-side timer using WebSockets (with Socket.IO), NestJS and Flutter</title>
      <dc:creator>Rukshan J. Senanayaka</dc:creator>
      <pubDate>Mon, 15 Aug 2022 15:30:00 +0000</pubDate>
      <link>https://dev.to/rukshanjs/part-13-how-to-create-a-server-side-timer-using-websockets-with-socketio-nestjs-and-flutter-3821</link>
      <guid>https://dev.to/rukshanjs/part-13-how-to-create-a-server-side-timer-using-websockets-with-socketio-nestjs-and-flutter-3821</guid>
      <description>&lt;p&gt;If you have an interest in WebSockets technology, chances are that you have seen several tutorials online that use it to create applications such as realtime chat apps. But is that all that is possible with this WebSockets technology 🤔? - Of course not! We can do so much more with them. &lt;/p&gt;

&lt;p&gt;In this 3-part tutorial series, we will learn how to create a server-side timer using NestJS and Flutter by application of WebSockets (with Socket.IO). You can use such an implementation to create a proper timer for a quiz app etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A decent knowledge on NestJS, Flutter, JavaScript/TypeScript.&lt;/li&gt;
&lt;li&gt;Flutter 2.5.2 (with null safety), NestJS 8.0.0, Node 16.15.0, Android Emulator API S (This series of articles is successfully tested with these - may or may not work with other versions)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final outcome of this 3-part article series
&lt;/h2&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%2Fuploads%2Farticles%2Flizparehhc4mvl48kbig.gif" 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%2Fuploads%2Farticles%2Flizparehhc4mvl48kbig.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This part (part 1/3) contains the basic theory and some other good-to-know info related to WebSocket communication.&lt;/p&gt;

&lt;p&gt;In the world of connectivity, users need to send and receive data to and from various locations around the world. REST APIs are used widely to achieve this purpose. But in the traditional client-server communication using REST API and HTTP protocol, the server only responds to a request firstly made by the client - not initiate a data transfer by itself. &lt;/p&gt;

&lt;p&gt;Therefore, to get new data, a client has to request new data each time it needs that updated data. But how does a client know whether it's the right time to request new data or to wait for some time until the data is updated in the server?&lt;/p&gt;

&lt;p&gt;A few methods are available to achieve this,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refresh the client to send a new HTTP request every time&lt;/li&gt;
&lt;li&gt;Polling&lt;/li&gt;
&lt;li&gt;WebSockets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of these, refreshing the client repeatedly is not good user experience. It is also not sometimes practical.&lt;/p&gt;

&lt;p&gt;Polling is expensive in terms of computational power and resources required.&lt;/p&gt;

&lt;p&gt;WebSockets is a high speed technology that can be used for realtime client-server communication.&lt;/p&gt;

&lt;h2&gt;
  
  
  The socket confusion
&lt;/h2&gt;

&lt;p&gt;First off, let's clarify a couple of things.&lt;/p&gt;

&lt;p&gt;There are a couple of jargon words used in socket communication (mostly interchangeably) in general, but it would be nice to have a clear idea about them. I'll just give a brief of some such words below and you can refer the references for more information on them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket&lt;/strong&gt; - is a protocol that depends on &lt;strong&gt;TCP&lt;/strong&gt; protocol (like &lt;strong&gt;HTTP&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebSockets&lt;/strong&gt; - is the API specification of WebSocket protocol (supports two way communication)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Socket.IO&lt;/strong&gt; - is a library (built on top of WebSocket protocol)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Socket&lt;/strong&gt; - is an endpoint (in a socket communication)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook&lt;/strong&gt; - is an HTTP-based callback function which allows one way, server to server communication  (&lt;strong&gt;not&lt;/strong&gt; related to WebSockets and &lt;strong&gt;not&lt;/strong&gt; concerned in this tutorial series)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although &lt;strong&gt;Socket&lt;/strong&gt;s can be divided as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TCP/IP sockets&lt;/li&gt;
&lt;li&gt;Unix domain sockets,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we are only interested in TCP/IP sockets for this tutorial series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps in establishing a WebSocket connection
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Client sends an HTTP request to open a WebSocket connection (the &lt;strong&gt;HTTP&lt;/strong&gt; connection will be upgraded to a &lt;strong&gt;WebSocket&lt;/strong&gt; connection)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Server responds to the client’s request approving it with a &lt;strong&gt;101 Switching Protocols&lt;/strong&gt; response, and thereby completing the handshake and establishing a WebSocket connection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This TCP/IP connection is kept open (by a ping-pong mechanism) allowing real time communication between the client and server until either the client or the server chooses to close the connection. This is a full-duplex communication, meaning both the client and server can send messages to each other at the same time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After either side terminates the connection, the TCP resources would be unallocated.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fuploads%2Farticles%2Fzyd394udoi6rd19cy6j2.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%2Fuploads%2Farticles%2Fzyd394udoi6rd19cy6j2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In traditional client-server communication, a client sends a request to a server to retrieve some kind of a resource (a text, an image or an audio etc.) and waits for a response from the server. But in WebSocket communication, the server can send the resource when required - without a direct request from a client. &lt;/p&gt;

&lt;p&gt;With the successful establishing of a handshake, a communication channel is created between client and server and then whenever the server wants to send something, it just can send it to the particular client without needing a REST API request from the client itself.&lt;/p&gt;

&lt;p&gt;Either endpoint of this communication channel is called a &lt;strong&gt;Socket&lt;/strong&gt;. Therefore, a socket has an IP address and a port number.&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%2Fuploads%2Farticles%2F7j93uyvo5hhdfhccb103.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%2Fuploads%2Farticles%2F7j93uyvo5hhdfhccb103.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this 3-part article series, we would be implementing a server-side timer using NestJS and Flutter, and we can start and stop it using our Flutter application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I'm also learning this stuff and there can be better ways to do the things that I have done in this tutorial series. Please feel free to give your feedback in the comments. If I have made a mistake, I'm more than grateful if you can bring them up in the comments section in each article 🤓&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Sockets are a great way to implement realtime functionalities in applications. For example, WebSockets can be used to implement a server-side timer for a Flutter app. &lt;/p&gt;

&lt;p&gt;This way, the clients have no way to tamper with or manipulate the timer on the frontend, and therefore this can be used, for example in quiz apps to maintain a server-side timer for each question with high integrity.&lt;/p&gt;

&lt;p&gt;Now you can go to the part 2 of this tutorial series, to see how to implement the backend server application required for WebSocket communication, in NestJS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Video
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=fV0lRcfb1zI" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimg.youtube.com%2Fvi%2FfV0lRcfb1zI%2F0.jpg" alt="IMAGE ALT TEXT HERE"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;General concept of a socket - &lt;a href="https://medium.com/swlh/understanding-socket-connections-in-computer-networking-bac304812b5c" rel="noopener noreferrer"&gt;https://medium.com/swlh/understanding-socket-connections-in-computer-networking-bac304812b5c&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSocket Protocol - &lt;a href="https://datatracker.ietf.org/doc/id/draft-ietf-hybi-thewebsocketprotocol-09.html" rel="noopener noreferrer"&gt;https://datatracker.ietf.org/doc/id/draft-ietf-hybi-thewebsocketprotocol-09.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSocket API (WebSockets) - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Socket.IO can be more than just a wrapper for WebSockets - &lt;a href="https://en.wikipedia.org/wiki/Socket.IO" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Socket.IO&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSockets v Socket.IO - &lt;a href="https://stackoverflow.com/a/62848079/8995555" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/62848079/8995555&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSocket v Socket - &lt;a href="https://stackoverflow.com/a/67826460/8995555" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/67826460/8995555&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSockets v Webhooks - &lt;a href="https://stackoverflow.com/a/24747947/8995555" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/24747947/8995555&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unix Socket v TCP/IP Socket (Just for additional knowledge) - &lt;a href="https://www.baeldung.com/linux/unix-vs-tcp-ip-sockets" rel="noopener noreferrer"&gt;https://www.baeldung.com/linux/unix-vs-tcp-ip-sockets&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What is a Webhook - &lt;a href="https://www.redhat.com/en/topics/automation/what-is-a-webhook" rel="noopener noreferrer"&gt;https://www.redhat.com/en/topics/automation/what-is-a-webhook&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HTTP to WS upgrade request - &lt;a href="https://stackoverflow.com/a/26401940/8995555" rel="noopener noreferrer"&gt;https://stackoverflow.com/a/26401940/8995555&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>websockets</category>
      <category>flutter</category>
      <category>nestjs</category>
      <category>realtime</category>
    </item>
    <item>
      <title>Introduction to Next.js - a personal opinion</title>
      <dc:creator>Rukshan J. Senanayaka</dc:creator>
      <pubDate>Wed, 27 Jul 2022 18:23:00 +0000</pubDate>
      <link>https://dev.to/rukshanjs/introduction-to-nextjs-a-personal-opinion-emn</link>
      <guid>https://dev.to/rukshanjs/introduction-to-nextjs-a-personal-opinion-emn</guid>
      <description>&lt;p&gt;Hi! First off, thanks for having a look at my very first article on dev.to 🥳! In this short article I’m going to give an introduction to Next.js while giving you my personal opinion on it as well. Your comments and ideas are also welcome 😎!&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s in this article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What is Next.js&lt;/li&gt;
&lt;li&gt;Some personal-favorite features of Next.js&lt;/li&gt;
&lt;li&gt;Choosing Next.js v React&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What’s not in this article
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to start a Next.js project &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to create only a very simple informational website, learning HTML and CSS is just enough. But if you want to add some functionality to that site, you may want to use JavaScript as well. Any website - no matter how complex, almost always can be simplified into HTML, CSS and JS code. &lt;/p&gt;

&lt;p&gt;But what if you want to create a somewhat complex website or a web app instead of a simple one? That’s where web development libraries such as React.js and frameworks such as Next.js, Vue.js and Angular come in handy.&lt;/p&gt;

&lt;p&gt;Next.js can be thought of as a full-stack web development framework, because it allows the developer to write code for frontend and backend in the same application. The frontend code is of course written in React using either JavaScript or TypeScript. (Although my personal favorite is with TypeScript - I’ll tell you why in a future article)&lt;/p&gt;

&lt;p&gt;One major question I get asked is, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Do I need to learn React to start learning Next.js?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My answer to this question is, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Don’t think of Next.js as a completely isolated thing from React, just think of it as an additional layer of help provided by the developers of Next.js to React developers because under the hood, Next.js is React!”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check the following code snippets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hello World in React,&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//src/App.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Hello World in Next.js,&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//pages/_app.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../styles/globals.css&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="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppProps&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;next/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageProps&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AppProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Hello&lt;/span&gt; &lt;span class="nx"&gt;World&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See, very similar. That’s because what Next.js does, is it takes care of some of the mundane yet common tasks needed to be done during the development of a React app. &lt;/p&gt;

&lt;p&gt;As an example, developers who use React know the pain it takes to handle routes. If a route name is changed for example from &lt;code&gt;auth/login&lt;/code&gt; to &lt;code&gt;/login&lt;/code&gt; (among the many requests of real-world clients 😆) then a React developer has to manually change the route in the code. But, a Next.js developer only has to change the folder structure inside the &lt;code&gt;pages&lt;/code&gt; folder. (This folder is a Next.js reserved folder for routing, by the way).&lt;/p&gt;

&lt;h2&gt;
  
  
  Features i like about Next.js
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Page-based routing allows easy management of routing within the web app and it supports dynamic routes which allow easy handling of query params etc. If for example you have a &lt;code&gt;projects/[projectId]&lt;/code&gt; sort of route, getting that &lt;code&gt;projectId&lt;/code&gt; to display contents of a single project is very easy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hot code reloading which allows to see changes updated when saving a code file without server restart.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy deployment on various platforms like Vercel (offered by creators of Next.js) and Netlify.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cool documentation which is very beginner-friendly. (But I was not able to get Server-side-rendering working yet by reading that).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I don’t like about Next.js
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Some third party packages which import global css in them will give an error. For example, &lt;a href="https://github.com/uiwjs/react-md-editor/issues/52#issue-724437226"&gt;https://github.com/uiwjs/react-md-editor/issues/52#issue-724437226&lt;/a&gt;. There are workarounds, but it’s an inconvenience.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sometimes the dev version of the web app is too slow in the browser. No problem once built the production release.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Ok then bye bye React! Hello Next.js? Not so fast 😄! While Next.js is a very good framework for mid to complex projects, for very simple projects, it is wise to think of using plain React. (If it is even simpler than that, just use HTML, CSS and JS).&lt;/p&gt;

&lt;p&gt;One final note - If it is a fairly complex client project, I almost always ask the client if it is okay to use Next.js or if they want pure React. If they have no preference, I always select Next.js because why not!&lt;/p&gt;

&lt;p&gt;But don’t forget the good old HTML, CSS, JS because not all projects require React or Next.js. I mean who is to say you &lt;strong&gt;must&lt;/strong&gt; use React for everything, right? I mean if you want to have a look at the most overengineered, and insanely complicated way to write a hello world web app, take a look at the video by Chris Hawkes - &lt;a href="https://youtu.be/3nHQMAY_J1A"&gt;https://youtu.be/3nHQMAY_J1A&lt;/a&gt;, of course after giving me a like if you think I deserve one for this article 😃. &lt;/p&gt;

&lt;p&gt;I'm also learning this stuff so let me know if I missed something in the comments below. Thanks and have a great day 🥳!&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;I was inspired by this article, please have a look at that also. - &lt;a href="https://dev.to/olenadrugalya/introduction-to-nextjs-3gi4"&gt;https://dev.to/olenadrugalya/introduction-to-nextjs-3gi4&lt;/a&gt; &lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
