<?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: Christopher Ribeiro</title>
    <description>The latest articles on DEV Community by Christopher Ribeiro (@christopy).</description>
    <link>https://dev.to/christopy</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%2F119636%2Fd1c585f4-3f41-44f9-8fbb-2bead8148de5.jpeg</url>
      <title>DEV Community: Christopher Ribeiro</title>
      <link>https://dev.to/christopy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christopy"/>
    <language>en</language>
    <item>
      <title>Why we chose Bun</title>
      <dc:creator>Christopher Ribeiro</dc:creator>
      <pubDate>Thu, 08 Feb 2024 12:39:31 +0000</pubDate>
      <link>https://dev.to/alertpix/why-we-chose-bun-28dk</link>
      <guid>https://dev.to/alertpix/why-we-chose-bun-28dk</guid>
      <description>&lt;p&gt;At &lt;a href="https://alertpix.live?utm_source=dev.to&amp;amp;utm_medium=alertpix&amp;amp;utm_campaign=posts"&gt;Alertpix&lt;/a&gt; we allow streamers to receive donations from their audience via Pix Instant Payment and show an alert on the live stream.&lt;/p&gt;

&lt;p&gt;I was eyecatching Bun for a long time. And while developing the initial version of the platform the excitement to use it when it was more stable was really high.&lt;br&gt;
And when 1.0 reached, I knew it, our real time system should be written in Bun.&lt;/p&gt;
&lt;h2&gt;
  
  
  No common js or esmodules madness
&lt;/h2&gt;

&lt;p&gt;Our API is in node. And God, how I suffered to import &lt;a href="https://github.com/ai/nanoid" rel="noopener noreferrer"&gt;nanoid&lt;/a&gt; in an esmodule project. I had to vendor it, since using a previous version was not ideal.&lt;br&gt;
With bun, we can no longer worry about that. Just import what you need and done.&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;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:path&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;h2&gt;
  
  
  TypeScript ou of the box
&lt;/h2&gt;

&lt;p&gt;We all know that TS is just a linter, so we need it as the project grows right?&lt;br&gt;
That feeling when the auto complete works, hits different.&lt;br&gt;
But the struggle to setup TypeScript in nodejs is a pain.&lt;/p&gt;

&lt;p&gt;Well, with Bun, its no longer an issue anymore. It just works.&lt;/p&gt;
&lt;h2&gt;
  
  
  Fast startup times
&lt;/h2&gt;

&lt;p&gt;Oh boy, while developing &lt;a href="https://alertpix.live?utm_source=dev.to&amp;amp;utm_medium=alertpix&amp;amp;utm_campaign=posts"&gt;Alertpix&lt;/a&gt;, I was relying solely on using cloud environments since my laptop was in repair.&lt;br&gt;
I can count how many hours I wasted waiting node to sync the changes (I'm talking about you Next, you are slow even on high end machines) in these cloud enviroments. When switched to Bun, everything felt fast.&lt;/p&gt;
&lt;h2&gt;
  
  
  Buildless deployment
&lt;/h2&gt;

&lt;p&gt;Our realtime service needs to be deployed as fast as possible.&lt;br&gt;
It means that, the time to identify a bug in production, fix the issue, commit and run de CI deployment should be small.&lt;br&gt;
And we know that, even cached, deployments are slow because of the build step. The bigger the project, the more time it takes.&lt;/p&gt;

&lt;p&gt;Since bun runs TypeScript without a build step.&lt;br&gt;
In summary, our container pulls Bun, install the packages, copies the needed files and runs the code with Bun:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; oven/bun&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json .&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; bun.lockb .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;bun &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; tsconfig.json .&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV production&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["bun", "src/index.ts"]&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pub Sub is easier
&lt;/h2&gt;

&lt;p&gt;Our real time system is a pub sub on top of websockets.&lt;br&gt;
And it is so easy to do, that we already covered it in &lt;a href="https://dev.to/alertpix/going-real-time-with-donation-alerts-92n"&gt;this post&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://alertpix.live?utm_source=dev.to&amp;amp;utm_medium=alertpix&amp;amp;utm_campaign=posts"&gt;Alertpix&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>bunjs</category>
    </item>
    <item>
      <title>Emoji Glitch Boosts Streamer's Earnings: Alertpix Bug Post-Mortem Analysis</title>
      <dc:creator>Christopher Ribeiro</dc:creator>
      <pubDate>Sat, 20 Jan 2024 22:24:16 +0000</pubDate>
      <link>https://dev.to/alertpix/emoji-glitch-boosts-streamers-earnings-alertpix-bug-post-mortem-analysis-4eoi</link>
      <guid>https://dev.to/alertpix/emoji-glitch-boosts-streamers-earnings-alertpix-bug-post-mortem-analysis-4eoi</guid>
      <description>&lt;p&gt;At &lt;a href="https://alertpix.live?utm_source=dev.to&amp;amp;utm_medium=alertpix&amp;amp;utm_campaign=posts"&gt;Alertpix&lt;/a&gt; we allow streamers to receive donations from their audience via Pix Instant Payment and show an alert on the live stream.&lt;/p&gt;

&lt;p&gt;Being an early-stage startup, we can easily stay close to our streamers. As usual, I was present when our first incident occurred. A donation with a single emoji made a streamer richer, and chaos erupted in our backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The live stream
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/rbqk" rel="noopener noreferrer"&gt;RBQK&lt;/a&gt;, one of our initial streamers, had just started their livestream. After a few minutes, the first donation alert appeared.&lt;/p&gt;

&lt;p&gt;While everything seemed fine, and the donation goals updated correctly after the first donation, a Discord message and a Rollbar email gave me chills.&lt;/p&gt;

&lt;p&gt;An error 500 occurred after the second donation. I waited a few seconds to see if the alert would appear on the live stream.&lt;/p&gt;

&lt;p&gt;Realizing it wouldn't show up, I started to feel anxious, and so did the donor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logs and monitoring
&lt;/h2&gt;

&lt;p&gt;Since day one, we were committed to having robust monitoring. However, as we never had any incidents in the payment process, we didn't realize that the logs in this flow were not as comprehensive as intended.&lt;/p&gt;

&lt;p&gt;The error log was not helpful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot read properties of null (reading '0')\n at Filter.clean (/app/node_modules/bad-words/lib/badwords.js:58:41)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously, we had the stack trace, and it pointed to the part of the code that verifies if the message for the streamer has sensitive words and applies a filter to it before creating the transaction and dispatching the donation alert:&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;isSensitive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badWords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isProfane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comment&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;redactedComment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badWords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code uses the &lt;a href="https://github.com/web-mech/badwords" rel="noopener noreferrer"&gt;bad-words&lt;/a&gt; library as a Fastify plugin.&lt;/p&gt;

&lt;p&gt;However, this made no sense to me. I became nervous as I saw the error reporting messages and emails repeating every few minutes or so&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks and no conditional updates
&lt;/h2&gt;

&lt;p&gt;As I received more of these alerts, I realized the payment processor was retrying the route until it received a 200 response.&lt;/p&gt;

&lt;p&gt;I checked the database and found that the charge was marked as paid, but no transaction was registered. This meant that the payment processing part was executing every time the route was called and we can't show alerts if no transaction is registered.&lt;/p&gt;

&lt;p&gt;However, I calmed down because we can't process a transaction if the charge has already been processed, right?&lt;/p&gt;

&lt;p&gt;Actually, the biggest mistake was in this part of the code:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;paymentReceived&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="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fastify&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;charge&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;ChargeModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="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;charge&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="na"&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;Could not find related Charge&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="c1"&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 felt so dumb when I realized that the second check should prevent processing the payment if the charge status was paid, but it wasn't there. So I rushed and added the check:&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;paymentReceived&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="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fastify&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;charge&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;ChargeModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="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;charge&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="na"&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;Could not find related Charge&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;paid&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&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 commited into main branch and waited. Bingo! The errors stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  An 🥰 emoji and no more chaos
&lt;/h2&gt;

&lt;p&gt;With no more chaos happening, I investigated the donation more in depth to understand why the check for bad words have failed.&lt;/p&gt;

&lt;p&gt;It seemed perfect: it was paid, the payment provider showed the transaction on their side, and it had all the required info:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;amount&lt;/li&gt;
&lt;li&gt;user name&lt;/li&gt;
&lt;li&gt;comment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the comment was a: 🥰.&lt;br&gt;
Yes, the string was only an emoji.&lt;/p&gt;

&lt;p&gt;So I rushed to my console and ran:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BadWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bad-words&lt;/span&gt;&lt;span class="dl"&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;filter&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;BadWords&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;🥰&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;And there it was! The console screamed the same error as before:&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%2Fjgsmjifhwmwy49oqj7bh.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%2Fjgsmjifhwmwy49oqj7bh.png" alt="Image description" width="792" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As it was late at night, I sent a message to the streamer in their DM and on the Twitch chat, apologizing, and called it a day.&lt;/p&gt;

&lt;p&gt;At 6 AM the next day, I found the issue. The clean method for bad-words was trying to join a string when it had nothing to join back in. It splits the string and replaces the word if it is profane. But when it joins the word back in, it fails miserably because the regex returned null, and we cannot access index 0 of null.&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="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;string&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splitRegex&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isProfane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;;&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;splitRegex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was clear to me: copy the code, fix it myself, and open a PR. So I did my implementation of the library in 5 minutes and tested against all comments we had in the database and then compared the isProfane status with my implementation.&lt;br&gt;
Looks like the code is simple:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;isProfane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&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="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;badWords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&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="nx"&gt;wordExp&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;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(\W)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;$1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;b`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gi&lt;/span&gt;&lt;span class="dl"&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;wordExp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&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="nf"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&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;text&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isProfane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&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="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="nf"&gt;replaceWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&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="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="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;Now I can use it just like before. The clean method can be written in a single line of code. I even added isProfane and replaceWord, which are simple to code as well.&lt;/p&gt;

&lt;p&gt;After calling it quits, I deployed to production and after a week later, I have not seen any problems with a single emoji again. Of course, we still had to normalize the database.&lt;/p&gt;

&lt;h1&gt;
  
  
  No longer rich
&lt;/h1&gt;

&lt;p&gt;Right away I ran the script to get the streamer wallet balance based on the transaction history. The user had 100 bucks more because the wehbook was called many times in a timespan of an hour.&lt;/p&gt;

&lt;p&gt;So I normalized the wallet balance. And created the transaction based on the processed charge and also added the charge amount to the wallet balance.&lt;/p&gt;

&lt;h1&gt;
  
  
  The future and lessons learned
&lt;/h1&gt;

&lt;p&gt;The primary part of running an MVP is to do the best you can with only a few features in hand. However, I don't think about scaling until we need to. Otherwise, we lose the time to market and get a well-optimized server for a hundred thousand concurrent users when we can't get our first paying customer.&lt;/p&gt;

&lt;p&gt;For sure, we can't scale until we need to, but dealing with other people's money is dangerous. We know that; we had only one active streamer. However, what if we had five? Would we lose 500 bucks? How would we be able to handle such issues? So from now on, we have implemented the following measures to prevent incidents like that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better logging: We log more information to Rollbar, providing us with a small payload that shows what happened and what we need to know.&lt;/li&gt;
&lt;li&gt;Conditional writes: After more testing, we are now certain that we can't modify an entity if it can't be modified based on its status.&lt;/li&gt;
&lt;li&gt;Event-based: We've shifted most of the payment receiving processes to use queues. This way, if something goes wrong, we can fix the issue and resume from where it stopped.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://alertpix.live?utm_source=dev.to&amp;amp;utm_medium=alertpix&amp;amp;utm_campaign=posts"&gt;Alertpix&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>programming</category>
    </item>
    <item>
      <title>Ditch REST, go intent based</title>
      <dc:creator>Christopher Ribeiro</dc:creator>
      <pubDate>Fri, 05 Jan 2024 11:56:01 +0000</pubDate>
      <link>https://dev.to/alertpix/ditch-rest-go-intent-based-34hp</link>
      <guid>https://dev.to/alertpix/ditch-rest-go-intent-based-34hp</guid>
      <description>&lt;p&gt;At &lt;a href="https://alertpix.live?utm_source=dev.to&amp;amp;utm_medium=alertpix&amp;amp;utm_campaign=posts"&gt;Alertpix&lt;/a&gt; we allow streamers to receive donations from their audience via Pix Instant Payment and show an alert on the live stream.&lt;/p&gt;

&lt;p&gt;Our API exclusively utilizes the &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; HTTP methods. We think that we can use native features as much as we can. We bring this inspiration from the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element which does not support form submissions &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, or &lt;code&gt;DELETE&lt;/code&gt; methods.&lt;/p&gt;

&lt;p&gt;In this article, we explore the reasons why your API might not require more than two request methods: &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  REST APIs
&lt;/h2&gt;

&lt;p&gt;APIs are designed for machines to communicate with each other, and there are various standards when using HTTP. The most commonly used standard is REST, which provides a great pattern for constructing APIs. If we adhere to it, we end up with a RESTful API.&lt;/p&gt;

&lt;p&gt;However, adhering to REST often compels us to write object-oriented code and use a variety of HTTP methods. While this isn't inherently problematic, one significant drawback is that we need to structure each route for each entity in the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /book
GET /book/:id
DELETE /book/:id
PUT /book/:id
PATCH /book/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we want to add another domain we certainly will use the same route structure for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /author
GET /author/:id
DELETE /author/:id
PUT /author/:id
PATCH /author/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What if we need to get a book by an author? How should we do it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /books/:id/author/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /author/:id/books/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In all honesty, it's not a matter of whether it's readable or understandable; it's about the need for such complexity in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  APIs are for humans
&lt;/h2&gt;

&lt;p&gt;While APIs are intended for machines to consume, they are designed and utilized by humans. Human developers read the documentation and test the integration, so there's no need for excessive complexity in HTTP endpoints. Instead, we can focus on making the API more intent-based.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Intent-Based
&lt;/h2&gt;

&lt;p&gt;Now that we understand that most of the time the integration will be done by humans. We can make it as swift as possible.&lt;/p&gt;

&lt;p&gt;The previous example to get a book by an author can be done this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /books/by-author/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you literally remove the special characters, you can read it just like an English sentence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET books by author id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shift towards an intent-based approach can make API usage more intuitive and user-friendly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going beyond
&lt;/h2&gt;

&lt;p&gt;If we want to create, edit or delete an entity, we can easily have different endpoints for that just like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /books/create {...}
POST /books/edit/:id {...}
POST /books/delete/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we where in REST, editing and deleting whould be a mess. And we all know that, we often forget the difference between &lt;code&gt;PATCH&lt;/code&gt; and &lt;code&gt;PUT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;By going intent based we make our APIs more human readable.&lt;br&gt;
This pattern is presented in our APIs and micro services so integration is always a breeze.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://alertpix.live?utm_source=dev.to&amp;amp;utm_medium=alertpix&amp;amp;utm_campaign=posts"&gt;Alertpix&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Going real time with donation alerts</title>
      <dc:creator>Christopher Ribeiro</dc:creator>
      <pubDate>Fri, 20 Oct 2023 19:34:44 +0000</pubDate>
      <link>https://dev.to/alertpix/going-real-time-with-donation-alerts-92n</link>
      <guid>https://dev.to/alertpix/going-real-time-with-donation-alerts-92n</guid>
      <description>&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%2Fpy39156sp2mm65ut34jc.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%2Fpy39156sp2mm65ut34jc.png" alt="A streamer doing a live code and a donation alert at the bottom left corner of the screen"&gt;&lt;/a&gt;At &lt;a href="//alertpix.live"&gt;Alertpix&lt;/a&gt; we allow streamers to receive donations from their audience via Pix Instant Payment and show an alert on the live stream.&lt;/p&gt;

&lt;p&gt;This post shows how donation alerts feature was coded in just a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notification widget
&lt;/h2&gt;

&lt;p&gt;Our users can use their Streamlabs account to use their own alert box in the live stream.&lt;br&gt;
However, we also have users that don't use Streamlabs, that's where the Notification widget comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The PubSub
&lt;/h2&gt;

&lt;p&gt;The ideia was simple: each widget listens into a topic and receives a new alert to show when its not showing any alerts.&lt;/p&gt;

&lt;p&gt;This new service was bootstrapped with &lt;a href="https://elysiajs.com/" rel="noopener noreferrer"&gt;Elysia&lt;/a&gt;, the fastest Bun framework.&lt;/p&gt;

&lt;p&gt;Now, to create a new http server is just as simple as:&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;new&lt;/span&gt; &lt;span class="nc"&gt;Elysia&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now we need to allow for widget to connect via WebSockets:&lt;/p&gt;

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

&lt;span class="nx"&gt;app&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="nf"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/ws&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Object&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;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Object&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="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&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 is where the tech stack shines. We can allow WebSocket connections, add schema validation to their messages and request params.&lt;br&gt;
We can also create a pub sub mechanism with a built-in API in bun.&lt;/p&gt;

&lt;p&gt;This can be done in a line of code when the socket connects:&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;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;free&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="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;We also stored the widget in a &lt;code&gt;free&lt;/code&gt; list. Meaning that the widget is not currently showing any alerts.&lt;/p&gt;

&lt;p&gt;When the peer disconnects, we remove it from the list:&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;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;free&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The widget can send a message telling that its free, so we handle that as well.&lt;br&gt;
We check if the topic has items so we send the next alert, if any. Tagging it as free or not:&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;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&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;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lRange&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="mi"&gt;0&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="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;value&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;free&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="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lPop&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;free&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="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&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;value&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="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Posting a donation
&lt;/h2&gt;

&lt;p&gt;We updated our API to store the notification in the queue and publish to the notifying service via an API call:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;donationReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Donation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rPush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;donation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;notifyWidget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;donation&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

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


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

&lt;/div&gt;

&lt;p&gt;API call that we defined with the same logic to notify the connected peers if they are free:&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/publish&lt;/span&gt;&lt;span class="dl"&gt;"&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;body&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;free&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;receiver&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lRange&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;receiver&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&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;lPop&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;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;free&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;receiver&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;server&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&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;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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="p"&gt;})&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  What we learned:
&lt;/h2&gt;

&lt;p&gt;Choosing the right tech stack helps a lot. But knowing about the topic and understanding what is needed to build the feature, we can build things pretty fast. &lt;a href="https://dev.to/alertpix/1-hour-features-1la7"&gt;In this post&lt;/a&gt; we show how at Alertpix we code each feature in one hour.&lt;/p&gt;

&lt;p&gt;Since we launched we improve every day a little bit. Of course, today the implementation is pretty much different than this. But this blog aims to illustrate how you can write a pub sub without fighting against the code.&lt;/p&gt;

&lt;p&gt;Take a look in the code exampls if it where a complete code so you can try it yourself:&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;Elysia&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="s2"&gt;elysia&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cors&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;@elysiajs/cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&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;redis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;free&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REDIS_PASSWORD&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="nf"&gt;on&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&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Redis Client Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&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;app&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;Elysia&lt;/span&gt;&lt;span class="p"&gt;()&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="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/ws&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="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ws&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

      &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;free&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="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ws&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

      &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;free&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ws&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;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lRange&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="mi"&gt;0&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="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;value&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;free&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;free&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;await&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;lPop&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;free&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="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;server&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&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;value&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="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/publish&lt;/span&gt;&lt;span class="dl"&gt;"&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;body&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;free&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;receiver&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lRange&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;receiver&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&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;lPop&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;receiver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;free&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;receiver&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;server&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&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;receiver&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&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="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;8080&lt;/span&gt;&lt;span class="dl"&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;`🦊 Elysia is running at &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="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;app&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;port&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;a href="//alertpix.live"&gt;Alertpix&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>bunjs</category>
    </item>
    <item>
      <title>1 hour features</title>
      <dc:creator>Christopher Ribeiro</dc:creator>
      <pubDate>Mon, 16 Oct 2023 12:28:57 +0000</pubDate>
      <link>https://dev.to/alertpix/1-hour-features-1la7</link>
      <guid>https://dev.to/alertpix/1-hour-features-1la7</guid>
      <description>&lt;p&gt;At &lt;a href="//alertpix.live"&gt;Alertpix&lt;/a&gt; we allow streamers to receive donations from their audience via Pix Instant Payment and show an alert on the live stream.&lt;/p&gt;

&lt;p&gt;The whole product deals with real time, which means we need to ship features as fast as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 hour max
&lt;/h2&gt;

&lt;p&gt;As engineers we are used to long development and deployment processes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create feature branch&lt;/li&gt;
&lt;li&gt;code&lt;/li&gt;
&lt;li&gt;write tests&lt;/li&gt;
&lt;li&gt;push code&lt;/li&gt;
&lt;li&gt;open PR&lt;/li&gt;
&lt;li&gt;wait for automated tests&lt;/li&gt;
&lt;li&gt;wait for review&lt;/li&gt;
&lt;li&gt;wait deploy to beta/stating env&lt;/li&gt;
&lt;li&gt;wait validation&lt;/li&gt;
&lt;li&gt;create PR to prod&lt;/li&gt;
&lt;li&gt;wait for automated tests&lt;/li&gt;
&lt;li&gt;wait for review&lt;/li&gt;
&lt;li&gt;wait deploy to production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are used to something like this. Having a separate development environment and a production one (some companies has more environments than this). Also needing to follow git flow and wait for peers and CI to do their job.&lt;/p&gt;

&lt;p&gt;But I think all these can be not only reduced but take 1 hour max.&lt;br&gt;
Anything after that, means you are stuck into a problem or your feature is too big.&lt;/p&gt;

&lt;h2&gt;
  
  
  Research First: 20 minutes
&lt;/h2&gt;

&lt;p&gt;The first 20 minutes should be dedicated solely for research. But if you already have some knowledge of what you're building, you can do it in less time.&lt;/p&gt;

&lt;p&gt;The ideia here is to give some time to understand what you need to build, how to do it and the most important: how to code it in the next 40 minutes.&lt;/p&gt;

&lt;p&gt;This step can't be skipped, even if you know the concepts. Because we should understand things before doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now Code: 20 minutes
&lt;/h2&gt;

&lt;p&gt;After the research process, its time to code the solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't over engineer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your solution should be simple and each piece of code should do one thing and one thing only.&lt;/p&gt;

&lt;p&gt;Don't write tests or docs, just code the first version. It won't be perfect the first time, so do what works and move on to the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now Test: 20 minutes
&lt;/h2&gt;

&lt;p&gt;Finally, you've reached the test phase. If you are a startup in the MVP or pre PMF phase you can skip this step and earn more 20 mins to code another feature.&lt;/p&gt;

&lt;p&gt;But if you want to write tests, now is the time to do it.&lt;br&gt;
We know that TDD tells us to write the tests before we write the code. But lets be real:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You only know what you will code while you are coding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tests come in last because you can test while you code. This phase is more if you need to have them to pass CI or a code review.&lt;br&gt;
Also if you add tests, you garantee that you coded something that works.&lt;/p&gt;

&lt;p&gt;At Alertpix this framework has been applied since day one, and every day we improve the product without having to wait. &lt;/p&gt;




&lt;p&gt;&lt;a href="//alertpix.live"&gt;Alertpix&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
      <category>startup</category>
    </item>
    <item>
      <title>From zero to production with Fastify</title>
      <dc:creator>Christopher Ribeiro</dc:creator>
      <pubDate>Tue, 12 Sep 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/dymmepay/from-zero-to-production-with-fastify-574m</link>
      <guid>https://dev.to/dymmepay/from-zero-to-production-with-fastify-574m</guid>
      <description>&lt;p&gt;When developing an application we often ask ourselves&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which framework should I use?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We know this question actually comes a lot in the JavaScript ecosystem.&lt;/p&gt;

&lt;p&gt;In this post I show why at &lt;a href="https://dymme.com" rel="noopener noreferrer"&gt;Dymme&lt;/a&gt; we chose Fastify as our back-end framework and how we use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The why
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Fast by default:&lt;/strong&gt;&lt;br&gt;
Fastify kills at every benchmark &lt;a href="https://fastify.dev/benchmarks/" rel="noopener noreferrer"&gt;see&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Express like:&lt;/strong&gt;&lt;br&gt;
We can write code just like in the ol' Express days if we want to.&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="nx"&gt;fastify&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="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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&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="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&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;&lt;strong&gt;3. Configurable:&lt;/strong&gt;&lt;br&gt;
Fastify offers a plugin system just like Express. Plugins can modify Fastify or act in certain moments (when the server closes or the request is sent for example).&lt;br&gt;
The chosen approach makes us write decoupled code.&lt;/p&gt;

&lt;p&gt;Each plugin is independent. Meaning that they can't talk to each other directly.&lt;br&gt;
All plugins talk to the Fastify instance directly, which itself is a plugin. And if a plugin wants to communicate with another it communicates via the Fastify instance.&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="nx"&gt;fastify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decorate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;db&lt;/span&gt;&lt;span class="dl"&gt;'&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;DbConnection&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="nx"&gt;fastify&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="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;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&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="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&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;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;world&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See that, we make the &lt;code&gt;db&lt;/code&gt; plugin available for the whole instance. This makes possible to use this plugin directly from anywhere that has access to the Fastify instance.&lt;br&gt;
In this case, our route handler has the Fastify instance by default and we can access it to call our &lt;code&gt;db&lt;/code&gt; plugin without direct access to it.&lt;/p&gt;

&lt;p&gt;And if you followed along, you may note that a route is also a plugin 🤯 that's how it can talk to the Fastify instance and use the &lt;code&gt;db&lt;/code&gt; plugin added to it.&lt;/p&gt;

&lt;p&gt;Take a look in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fastify.dev/ecosystem/" rel="noopener noreferrer"&gt;Ecosystem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastify.dev/docs/latest/Guides/Getting-Started#your-first-plugin" rel="noopener noreferrer"&gt;Your first plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fastify.dev/docs/latest/Reference/Hooks" rel="noopener noreferrer"&gt;Hooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. TypeScript:
&lt;/h3&gt;

&lt;p&gt;Yes it supports TypeScript, so you can have autocomplete (and make some guy want to remove it from your codebase).&lt;/p&gt;

&lt;h2&gt;
  
  
  The how
&lt;/h2&gt;

&lt;p&gt;By now I may have convinced you that Fastify is the best Express alternative than others. But what about how we do it at &lt;a href="https://dymme.com" rel="noopener noreferrer"&gt;Dymme&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Glad you asked!&lt;/p&gt;

&lt;p&gt;Dymme is a Startup that enables merchants to sell their products in one click and shoppers to pay also in one click, instantly. As we deal with real time payments, we need to have an API that handles heavy traffic and be able to move quickly.&lt;/p&gt;

&lt;p&gt;Fastify allows all that but if you start from scratch you may not perceive the difference from Express.&lt;br&gt;
That's why we recommend you to start your application with the &lt;a href="https://github.com/fastify/fastify-cli" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;.&lt;br&gt;
It will not only bootstrap you application but will configure it to have TypeScript if you want.&lt;/p&gt;

&lt;p&gt;The CLI will also setup your API to use filesystem routing (Yes, you heard that).&lt;/p&gt;

&lt;p&gt;Take a look in &lt;a href="https://github.com/ChristoPy/fastify-template" rel="noopener noreferrer"&gt;this base template&lt;/a&gt; we are using internally.&lt;br&gt;
We will update it in the future with better patterns, so stay tuned and give it a star!&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dymme.com" rel="noopener noreferrer"&gt;Dymme&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>typescript</category>
      <category>bunjs</category>
    </item>
    <item>
      <title>5 things I learned as a junior Front-End developer</title>
      <dc:creator>Christopher Ribeiro</dc:creator>
      <pubDate>Sat, 15 May 2021 17:34:47 +0000</pubDate>
      <link>https://dev.to/christopy/5-things-i-learned-as-a-junior-front-end-developer-5g9o</link>
      <guid>https://dev.to/christopy/5-things-i-learned-as-a-junior-front-end-developer-5g9o</guid>
      <description>&lt;p&gt;I've been a Front-End developer for almost 5 years, always as a Freelancer. Until 2019. When I decided to work for other companies.&lt;/p&gt;

&lt;p&gt;But this change made me realize a few things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Search
&lt;/h2&gt;

&lt;p&gt;After a few days looking for jobs, I was contacted by a startup.&lt;br&gt;
They offered me a challenge. I ended up finishing it in time.&lt;/p&gt;

&lt;p&gt;The hiring process was fast. First a video call, one week later I was travelling to another state to be interviewed in their office to a final interview process. And they hired me. The felling was awesome!&lt;br&gt;
Finally, in exactly two weeks since the first contact with them I was moving to another state to work on my first (non freelance) job.&lt;/p&gt;

&lt;p&gt;I worked for them for one year. And I know that I wasn't a junior dev at the time. But that's how it works. And these are the things I learned:&lt;/p&gt;

&lt;h2&gt;
  
  
  1: Git is much more powerful than you think
&lt;/h2&gt;

&lt;p&gt;Git is a powerful tool for version control, used by most of tech companies around the world. It's a requirement.&lt;/p&gt;

&lt;p&gt;I always worked alone, so I never studied Git in depth.&lt;br&gt;
In my mind, I never needed to learn it, because I thought I knew it.&lt;br&gt;
But I was completely wrong.&lt;/p&gt;

&lt;p&gt;Had days where I was hours trying to put back code that I have already written. Just because of tons of merge conflicts and me not being aware that Git follows a timeline. If my code wasn't synced with the branch that I was merging, my code would be overwritten with newer commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  2: Front-End = Full-Stack
&lt;/h2&gt;

&lt;p&gt;Yes, I thought that I was hired as a Front-End developer. But months later I was dealing with micro services, lambda functions, API updates, database management, CI/CD… And the list wont stop.&lt;/p&gt;

&lt;p&gt;Actually I don't think that's a problem.&lt;br&gt;
You're in a small team and you be all in contact with each other and make decisions together. That's a plus if you want to work in your communication skills.&lt;/p&gt;

&lt;p&gt;The thing that concerns me the most is that startups do this because they want to spend less money. And sometimes this isn't a good idea.&lt;br&gt;
If a developer is doing multiple tasks, probably, the result isn't the best.&lt;/p&gt;

&lt;p&gt;And the money saved is going to cost time in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  3: It's okay to say "no"
&lt;/h2&gt;

&lt;p&gt;Sometimes when we want to be the best, to do our best, we tend to say "yes" to every question, to every task that comes in hand.&lt;/p&gt;

&lt;p&gt;Sometimes, as a junior developer, you're afraid to show that you don't know how to do it. Or you don't know how to calculate how much time a task you take to be done. And you end up saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Yeah, I can do this until this afternoon".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But the problem is, if you agree with everything, you have to do everything.&lt;br&gt;
You end up working on multiple tasks at the same time. And in the end of the day, managers don't want to know if you had a problem.&lt;br&gt;
They just want to see it done.&lt;/p&gt;

&lt;p&gt;If you say that you can't, or give the task one more day to be done, it's okay. You'll have more time to finish it, and do your best.&lt;/p&gt;

&lt;h2&gt;
  
  
  4: You code, they get famous
&lt;/h2&gt;

&lt;p&gt;If you think that you'll get recognized for what you do while working for a company. I'm sorry to say that you wont. Or at least, not most part of the time.&lt;/p&gt;

&lt;p&gt;The thing is, a developer is used as a tool. Instead of being the product. We are used as tools to give the company what they want:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Money.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And developers will never be recognized as the product.&lt;br&gt;
Have you ever seen an Apple launch where a developer speaks about the product they made? Or this brand new XYZ autonomous self-driving car where the presenter isn't the CEO or a marketing person?&lt;/p&gt;

&lt;p&gt;You may think I'm wrong, that developers do receive the credit they deserve.&lt;br&gt;
But try pushing a bug to production and see if the CEO/marketing speakers will be held accountable for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5: Code is your wife, side project is your mistress
&lt;/h2&gt;

&lt;p&gt;Sorry for the bad (and non inclusive) title.&lt;br&gt;
But after working for 8+ hours every day, you end up tired and bored of the code you write. If a task take too long to complete, you end up with less and less energy to complete it.&lt;/p&gt;

&lt;p&gt;It may not happen to every developer, but certainly when it happens, It's because you're tired. Your brain is tired.&lt;br&gt;
There are a few things to reverse this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Walk / run&lt;/li&gt;
&lt;li&gt;Meditate&lt;/li&gt;
&lt;li&gt;Sleep more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However. A side project comes more in hand.&lt;br&gt;
Because you can't take a walk or a run after 7/8 PM. You come home, after commute, take a shower (i guess), eat and then you're so tired to even think straight.&lt;/p&gt;

&lt;p&gt;You could meditate. This works most of the time, but try coding anything different. Seriously, try coding anything.&lt;br&gt;
Any code that's different from what you see everyday, will make that joy, the focus reappear.&lt;/p&gt;

&lt;p&gt;And even more, maybe a side project can become your own business?&lt;/p&gt;

</description>
      <category>code</category>
      <category>freelance</category>
      <category>developer</category>
      <category>life</category>
    </item>
  </channel>
</rss>
