<?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: Muhammetberdi Jepbarov</title>
    <description>The latest articles on DEV Community by Muhammetberdi Jepbarov (@muhammetberdi_jepbarov).</description>
    <link>https://dev.to/muhammetberdi_jepbarov</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%2F2834145%2Feaf02575-9cd8-43ca-9d93-8d7d2048d1f8.jpg</url>
      <title>DEV Community: Muhammetberdi Jepbarov</title>
      <link>https://dev.to/muhammetberdi_jepbarov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muhammetberdi_jepbarov"/>
    <language>en</language>
    <item>
      <title>Merging Redis Serialized HyperLogLog Sets in Golang (Without Redis Commands)</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Tue, 25 Feb 2025 08:29:09 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/merging-redis-serialized-hyperloglog-sets-in-golang-without-redis-commands-1ihn</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/merging-redis-serialized-hyperloglog-sets-in-golang-without-redis-commands-1ihn</guid>
      <description>&lt;p&gt;I was looking through questions on Stack Overflow and noticed that one friend needed help with his problem. He wanted to merge two HyperLogLog sets stored in Redis, but instead of using Redis commands like &lt;code&gt;PFMERGE&lt;/code&gt;, he wanted to do it inside his Golang application. The challenge was retrieving the serialized HyperLogLog data, deserializing it, merging the sets, and then using the merged result.&lt;/p&gt;

&lt;p&gt;At first, this seemed straightforward. Redis provides &lt;code&gt;PFADD&lt;/code&gt; for adding elements and &lt;code&gt;PFCOUNT&lt;/code&gt; for getting an estimated count. But the real issue? &lt;strong&gt;Redis does not store HyperLogLog sets as raw serialized data.&lt;/strong&gt; That means you can't simply use &lt;code&gt;GET&lt;/code&gt; to retrieve a HyperLogLog and merge it manually in Go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Problem
&lt;/h3&gt;

&lt;p&gt;When you use HyperLogLog in Redis, the data is stored in a special format optimized for cardinality estimation. Running &lt;code&gt;GET hll1&lt;/code&gt; does not return a serialized HyperLogLog object but instead results in an error. This happens because Redis does not expose the internal structure of a HyperLogLog directly. &lt;/p&gt;

&lt;p&gt;A naive approach would be to assume that Redis stores the raw HyperLogLog object, retrieve it, and attempt to merge it. But that will not work. Instead, a more effective solution is to manage the HyperLogLog instances &lt;strong&gt;inside the Go application&lt;/strong&gt; and store them in Redis as serialized byte arrays.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Correct Approach: Managing HyperLogLog in Go
&lt;/h3&gt;

&lt;p&gt;Instead of relying on Redis to handle HyperLogLog merging, we should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create HyperLogLog instances in Go.&lt;/li&gt;
&lt;li&gt;Serialize them and store the serialized data in Redis.&lt;/li&gt;
&lt;li&gt;Retrieve them later, deserialize them, and merge multiple instances.&lt;/li&gt;
&lt;li&gt;Estimate the final count in Go.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Step 1: Serializing a HyperLogLog and Storing it in Redis&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;To properly store a HyperLogLog instance, we need to serialize it before saving it in Redis. We use &lt;code&gt;encoding/gob&lt;/code&gt; to convert the object into a byte slice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;serializeHLL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hll&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;hyperloglog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sketch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;
    &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hll&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;buf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;main()&lt;/code&gt; function, we create a HyperLogLog instance, insert some values, serialize it, and store it in Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&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;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;rdb&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Addr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"localhost:6379"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;hll1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;hyperloglog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;hll1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;hll1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;data1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;serializeHLL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hll1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error serializing hll1:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;rdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hll1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&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;This approach ensures that we have full control over our HyperLogLog data structure.&lt;/p&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;Step 2: Retrieving and Merging HyperLogLog Sets&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;To merge two HyperLogLog sets, we first need to &lt;strong&gt;deserialize&lt;/strong&gt; them from the stored byte data in Redis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;deserializeHLL&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="kt"&gt;byte&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="n"&gt;hyperloglog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sketch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hll&lt;/span&gt; &lt;span class="n"&gt;hyperloglog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sketch&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBuffer&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;dec&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;hll&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;amp;&lt;/span&gt;&lt;span class="n"&gt;hll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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 retrieve the stored HyperLogLog sets, deserialize them, and merge them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&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;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;rdb&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Addr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"localhost:6379"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;raw1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hll1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error fetching hll1:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;raw2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rdb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hll2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error fetching hll2:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;hll1Deserialized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;deserializeHLL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error deserializing hll1:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;hll2Deserialized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;deserializeHLL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error deserializing hll2:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hll1Deserialized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hll2Deserialized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error merging HyperLogLog sets:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Estimated count after merge:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hll1Deserialized&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Estimate&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 method ensures we can fully control the HyperLogLog lifecycle, from creation to storage and retrieval.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why This Works and Other Approaches Fail&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Attempting to retrieve HyperLogLog directly from Redis&lt;/strong&gt; does not work because Redis does not store it as a serialized object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using &lt;code&gt;PFMERGE&lt;/code&gt; in Redis&lt;/strong&gt; works but does not allow merging outside Redis, making the logic less flexible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manually managing HyperLogLog instances in Go&lt;/strong&gt; ensures better control, allowing serialization, merging, and estimation without depending on Redis-specific operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Instead of relying on Redis commands, we can &lt;strong&gt;store serialized HyperLogLog sets manually in Redis&lt;/strong&gt;, retrieve them in Go, merge them, and get accurate estimates. This gives us more control and flexibility when working with approximate counting in a distributed system.&lt;/p&gt;

&lt;p&gt;This approach is beneficial when you need HyperLogLog merging logic outside of Redis, such as in microservices, offline processing, or custom caching layers.&lt;/p&gt;

&lt;p&gt;Next time you work with HyperLogLog in Go, try managing your own serialized instances—it might save you a lot of trouble! 🚀&lt;/p&gt;

</description>
      <category>go</category>
      <category>redis</category>
      <category>serialize</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building Mobile Apps Without a Backend: The Power of Database Gateway API</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Thu, 20 Feb 2025 15:21:03 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/building-mobile-apps-without-a-backend-the-power-of-database-gateway-api-19m0</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/building-mobile-apps-without-a-backend-the-power-of-database-gateway-api-19m0</guid>
      <description>&lt;h2&gt;
  
  
  The Pain of Backend Development
&lt;/h2&gt;

&lt;p&gt;If you've ever built a mobile or web app that interacts with a database, you know the struggle—designing API endpoints, handling authentication, writing business logic, and ensuring scalability. Sometimes, all you need is &lt;strong&gt;a simple way to query the database&lt;/strong&gt; and retrieve structured JSON responses without going through the entire process of backend development.&lt;/p&gt;

&lt;p&gt;That’s exactly why many years ago I built the &lt;strong&gt;Database Gateway API&lt;/strong&gt;—a solution that allows developers to interact directly with &lt;strong&gt;PostgreSQL and MSSQL databases via a secure API&lt;/strong&gt;, without writing a full-fledged backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea Behind Database Gateway API
&lt;/h2&gt;

&lt;p&gt;The goal was simple: &lt;strong&gt;Why should you have to build an entire backend when all you need is an API for your database?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I needed a lightweight yet powerful solution that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Eliminate the need for a dedicated backend&lt;/strong&gt; in simple applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enable mobile and web apps to access database data seamlessly.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide an easy way to deploy APIs&lt;/strong&gt; for database interactions with minimal setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate with any existing system&lt;/strong&gt;, whether it’s an enterprise &lt;strong&gt;accounting platform&lt;/strong&gt; or a &lt;strong&gt;retail point-of-sale (POS) system&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This led to the creation of the &lt;strong&gt;Database Gateway API&lt;/strong&gt;—a framework-agnostic API layer that acts as a bridge between your database and applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Database Gateway API&lt;/strong&gt; is a standalone service that connects to your &lt;strong&gt;PostgreSQL&lt;/strong&gt; or &lt;strong&gt;MSSQL&lt;/strong&gt; database and &lt;strong&gt;exposes SQL queries as API endpoints&lt;/strong&gt;. It takes care of request parsing, security, and response formatting, allowing you to focus on building your application instead of writing backend logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features:
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Direct SQL Query Execution:&lt;/strong&gt; Supports SELECT, INSERT, UPDATE, and DELETE operations via API requests.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Automatic JSON Responses:&lt;/strong&gt; Converts database query results into well-structured JSON.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Easy Integration with Mobile &amp;amp; Web Apps:&lt;/strong&gt; No need for a complex backend—just plug it into your frontend.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Secure &amp;amp; Configurable:&lt;/strong&gt; Set up role-based access, API keys, and rate limiting.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Supports Complex Queries &amp;amp; Joins:&lt;/strong&gt; Fetch relational data easily, just like using SQL directly.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Minimal Deployment Overhead:&lt;/strong&gt; Run it as a standalone service or containerized in Docker.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Impact: 29 Deployments &amp;amp; 150+ Devices
&lt;/h2&gt;

&lt;p&gt;This API has been successfully deployed across &lt;strong&gt;29 businesses&lt;/strong&gt;, powering over &lt;strong&gt;150 devices&lt;/strong&gt;, primarily in &lt;strong&gt;shops and malls&lt;/strong&gt;. It integrates seamlessly with &lt;strong&gt;mobile apps and existing accounting systems&lt;/strong&gt;, allowing businesses to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sync sales data in real-time&lt;/strong&gt;, eliminating manual record-keeping.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improve customer experience&lt;/strong&gt; by linking mobile apps to inventory and POS systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable effortless data exchange between different applications&lt;/strong&gt; without writing additional backend logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why You Should Use It
&lt;/h2&gt;

&lt;p&gt;If you’re a developer building a &lt;strong&gt;mobile app, web dashboard, or prototype&lt;/strong&gt;, the &lt;strong&gt;Database Gateway API&lt;/strong&gt; can save you &lt;strong&gt;weeks of development time&lt;/strong&gt; by handling database queries and responses automatically.&lt;/p&gt;

&lt;p&gt;Instead of spinning up a full backend, setting up ORM models, and writing CRUD endpoints, &lt;strong&gt;you simply install the gateway, configure it with your database, and start making API requests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;✅ Instant API – Set up in minutes and start making SQL queries right away.&lt;br&gt;
✅ Effortless Integration – Works with mobile, web, and desktop apps.&lt;br&gt;
✅ JSON-Formatted Responses – Your queries return well-structured JSON, ready to use.&lt;br&gt;
✅ Perfect for Prototyping – Quickly test database interactions without a full backend.&lt;br&gt;
✅ Optimized for Performance – Execute fast queries with minimal setup.&lt;/p&gt;

&lt;p&gt;🚀 How It Works&lt;br&gt;
🔍 Querying Your Database&lt;/p&gt;

&lt;p&gt;Need to fetch data? Just send a POST request with your SQL query:&lt;/p&gt;

&lt;p&gt;POST 127.0.0.1:8000/api/v1/make-db-request&lt;br&gt;
{&lt;br&gt;
  "query_string": "SELECT * FROM tbl_mg_materials",&lt;br&gt;
  "base64_columns": ["group_code", "image_pict", "firm_id_guid"]&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Pro tip: Use base64_columns to encode image BLOBs or sensitive data!&lt;br&gt;
📊 The Response&lt;/p&gt;

&lt;p&gt;The API returns a structured JSON response:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "data": [...], &lt;br&gt;
  "status": 1, &lt;br&gt;
  "total": 2, &lt;br&gt;
  "message": "db query result"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;⚠️ Important Considerations&lt;/p&gt;

&lt;p&gt;🔴 Security First! – This API executes raw SQL, so make sure to restrict access and validate inputs.&lt;br&gt;
🔴 Database Changes? – Schema updates might require adjustments to your API queries.&lt;br&gt;
🔴 Use with Caution – Best for internal tools, rapid prototyping, and trusted environments.&lt;/p&gt;

&lt;p&gt;Want to try it out? The &lt;strong&gt;Database Gateway API&lt;/strong&gt; is open-source and available for anyone to use and contribute to. You can integrate it into your next project and &lt;strong&gt;cut down on backend development time significantly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Check out the repository: &lt;strong&gt;[&lt;a href="https://github.com/mikebionic/db-gateway-go" rel="noopener noreferrer"&gt;Db-Gateway-Go&lt;/a&gt;]&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Let me know if you have questions or ideas for improvements—happy coding! 🚀&lt;/p&gt;

</description>
      <category>go</category>
      <category>restapi</category>
      <category>database</category>
      <category>programming</category>
    </item>
    <item>
      <title>Как я разработал библиотеку Viewscount: Решение проблемы органического подсчета просмотров в Golang</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Thu, 20 Feb 2025 14:17:58 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/kak-ia-razrabotal-bibliotieku-viewscount-rieshieniie-probliemy-orghanichieskogho-podschieta-prosmotrov-v-golang-1jn0</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/kak-ia-razrabotal-bibliotieku-viewscount-rieshieniie-probliemy-orghanichieskogho-podschieta-prosmotrov-v-golang-1jn0</guid>
      <description>&lt;p&gt;Как разработчики, мы часто сталкиваемся с необходимостью отслеживания просмотров контента на различных платформах. Будь то отслеживание просмотров для статей, видео или продуктов, большинство приложений полагаются на простую колонку &lt;code&gt;view_count&lt;/code&gt; в своих таблицах базы данных. Проблема возникает, когда вы не хотите писать отдельное API для каждой таблицы, которая нуждается в подсчете просмотров, и при этом важно обеспечить безопасность от таких атак, как DoS (Denial of Service). Это та проблема, с которой я столкнулся, и причина, по которой я создал библиотеку &lt;strong&gt;Viewscount&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;В большинстве приложений просмотры отслеживаются в той или иной форме — будь то для блога, страницы продукта или профиля пользователя. Однако создание решения, которое было бы масштабируемым и безопасным, может быть сложной задачей. Вы можете выбрать путь написания индивидуальных API для каждой из этих таблиц, но это отнимает много времени и не масштабируется. А что если можно отслеживать просмотры, не пиша API для каждой новой таблицы? Что если существует более эффективный способ интеграции этой функциональности в любое приложение? Именно в этот момент я начал работать над созданием &lt;strong&gt;Viewscount&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Необходимость единого решения
&lt;/h3&gt;

&lt;p&gt;Когда вы строите систему с отслеживанием просмотров, одним из важных факторов является сохранение достоверности данных. В некоторых случаях злоумышленники могут попытаться искусственно увеличить счетчик просмотров, делая повторные запросы — здесь важно предотвратить такие атаки, как DoS (Denial of Service). Без надежного решения ваше приложение становится уязвимым для подобных атак, что подрывает достоверность данных.&lt;/p&gt;

&lt;p&gt;Так появилась идея создания библиотеки &lt;strong&gt;Viewscount&lt;/strong&gt;. Я хотел создать решение, которое позволило бы легко отслеживать органические просмотры для любых таблиц, независимо от фреймворка, и при этом предоставить возможность простого интегрирования этого решения в любое приложение, защищая от ненадежных инкрементов. Эта библиотека разработана для того, чтобы интегрироваться с любым Golang фреймворком, таким как &lt;strong&gt;Gin&lt;/strong&gt;, &lt;strong&gt;Chi&lt;/strong&gt; или даже базовым &lt;strong&gt;net/http&lt;/strong&gt;, и быть независимой от базы данных, поддерживая PostgreSQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Как работает Viewscount
&lt;/h3&gt;

&lt;p&gt;Когда вы добавляете &lt;strong&gt;Viewscount&lt;/strong&gt; в свое приложение, вы можете начать отслеживать просмотры сразу — без необходимости писать отдельные API для каждой таблицы. Допустим, у вас есть таблицы, такие как &lt;code&gt;tbl_driver&lt;/code&gt; или &lt;code&gt;tbl_vehicle&lt;/code&gt;, каждая из которых имеет колонку &lt;code&gt;view_count&lt;/code&gt;. Вместо того чтобы создавать API специально для инкрементации счетчика просмотров каждый раз, когда пользователь просматривает страницу, вы можете использовать &lt;strong&gt;Viewscount&lt;/strong&gt; для автоматического отслеживания этого процесса.&lt;/p&gt;

&lt;p&gt;Вот как это работает на практике:&lt;/p&gt;

&lt;p&gt;В вашей базе данных просто добавьте колонку &lt;code&gt;view_count&lt;/code&gt; в ваши таблицы:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;tbl_driver&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;              &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;      &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;view_count&lt;/span&gt;      &lt;span class="nb"&gt;INT&lt;/span&gt;          &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;deleted&lt;/span&gt;         &lt;span class="nb"&gt;INT&lt;/span&gt;          &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Аналогично можно сделать и для других таблиц. Не нужно писать сложные запросы или API — просто добавьте эту колонку для отслеживания просмотров.&lt;/p&gt;

&lt;h3&gt;
  
  
  Интеграция Viewscount в ваше приложение
&lt;/h3&gt;

&lt;p&gt;Самая лучшая часть библиотеки &lt;strong&gt;Viewscount&lt;/strong&gt; заключается в том, что она является &lt;strong&gt;независимой от фреймворков&lt;/strong&gt;. Независимо от того, используете ли вы &lt;strong&gt;Gin&lt;/strong&gt;, &lt;strong&gt;Chi&lt;/strong&gt; или даже &lt;strong&gt;net/http&lt;/strong&gt;, вы можете легко интегрировать ее в ваше приложение. Вот как вы можете добавить ее как middleware в &lt;strong&gt;Gin&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Сначала нужно инициализировать view tracker в вашем приложении:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;middlewares&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/mikebionic/viewscount"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;viewTracker&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;viewscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewTracker&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;InitializeViewTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;minutesGap&lt;/span&gt; &lt;span class="n"&gt;time&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="n"&gt;viewTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewViewTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutesGap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ViewCounterMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&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;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&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="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&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="n"&gt;idInt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sscanf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;idInt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;viewTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ошибка отслеживания просмотра: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&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;Как только middleware настроено, вы можете добавить его к вашим маршрутам. Вот как это делается:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Инициализация базы данных и middleware&lt;/span&gt;
    &lt;span class="n"&gt;middlewares&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InitializeViewTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

    &lt;span class="c"&gt;// Определение маршрутов&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;middlewares&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewCounterMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tbl_driver"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetDriver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&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;Viewscount&lt;/strong&gt; в ваше приложение. Middleware будет автоматически отслеживать просмотры на указанной таблице и увеличивать значение в колонке &lt;code&gt;view_count&lt;/code&gt; по мере необходимости, при этом обеспечивая защиту от атак.&lt;/p&gt;

&lt;h3&gt;
  
  
  Преимущества Viewscount
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Легкая интеграция&lt;/strong&gt;: Всего несколько строк кода — и вы уже отслеживаете просмотры на любой из ваших таблиц.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Независимость от фреймворков&lt;/strong&gt;: Библиотека разработана для работы с любым Golang фреймворком, что позволяет использовать ее в проекте независимо от стека.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Защита от атак DoS&lt;/strong&gt;: Ограничивая быстрые запросы, которые могут искусственно увеличить счетчик просмотров, &lt;strong&gt;Viewscount&lt;/strong&gt; гарантирует, что данные остаются достоверными и точными.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Эффективность и масштабируемость&lt;/strong&gt;: Не нужно создавать кастомные API или сложные запросы — используйте библиотеку и отслеживайте просмотры в реальном времени.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Заключение
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Viewscount&lt;/strong&gt; решает общую проблему разработки: как отслеживать просмотры на различных таблицах, обеспечивая безопасность и эффективность, при этом не писать отдельные API для каждой новой таблицы. Легкость интеграции, защита от атак и независимость от фреймворков делают ее мощным инструментом для разработчиков, создающих современные Golang-приложения.&lt;/p&gt;

&lt;p&gt;Не стесняйтесь внести вклад в проект и сообщать о проблемах или предложениях через &lt;a href="https://github.com/mikebionic/viewscount" rel="noopener noreferrer"&gt;GitHub Viewscount&lt;/a&gt;. Я буду рад увидеть, как эта библиотека поможет другим в их разработке!&lt;/p&gt;

&lt;p&gt;Счастливого кодинга!&lt;/p&gt;

</description>
      <category>viewsount</category>
      <category>go</category>
      <category>webdev</category>
      <category>library</category>
    </item>
    <item>
      <title>How I Developed the Viewscount Library: Solving the Problem of Organic View Counting in Golang</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Thu, 20 Feb 2025 14:16:56 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/how-i-developed-the-viewscount-library-solving-the-problem-of-organic-view-counting-in-golang-1684</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/how-i-developed-the-viewscount-library-solving-the-problem-of-organic-view-counting-in-golang-1684</guid>
      <description>&lt;p&gt;As developers, we often encounter the need to track views on content across various platforms. Whether it's tracking views for articles, videos, or products, most applications rely on a simple &lt;code&gt;view_count&lt;/code&gt; column in their database tables. The challenge arises when you don’t want to go through the hassle of writing a separate API for each table that needs view tracking, while also ensuring that your system is secure from abuse like DoS (Denial of Service) attacks. This is the problem I encountered and the reason I created the &lt;strong&gt;Viewscount&lt;/strong&gt; library.&lt;/p&gt;

&lt;p&gt;In most applications, views are tracked in some form—whether it’s for a blog post, a product listing, or even user profile pages. However, implementing a solution that is both scalable and secure can become cumbersome. You could go the route of writing custom APIs for each of these tables, but that’s time-consuming and doesn’t scale well. What if there was a way to track these views without rewriting APIs for every new table? What if there was a more efficient way to integrate this functionality into any app? That’s when I started working on &lt;strong&gt;Viewscount&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Need for a Unified Solution
&lt;/h3&gt;

&lt;p&gt;When building any system with views tracking, one of the critical concerns is ensuring that the view count remains authentic. In some cases, malicious users may attempt to artificially inflate the view count through repeated requests—this is where preventing DOS (Denial of Service) type increments comes into play. Without a solid solution, your application becomes vulnerable to these types of attacks, undermining the integrity of your view count.&lt;/p&gt;

&lt;p&gt;Thus, the idea for &lt;strong&gt;Viewscount&lt;/strong&gt; was born. I wanted a way to seamlessly count organic views across any tables, regardless of the framework, while providing an easy-to-integrate middleware solution that prevents abusive increments. This library is designed to integrate with any Golang web framework, like &lt;strong&gt;Gin&lt;/strong&gt;, &lt;strong&gt;Chi&lt;/strong&gt;, or even basic &lt;strong&gt;net/http&lt;/strong&gt;, and is database-agnostic, supporting PostgreSQL seamlessly.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Viewscount Works
&lt;/h3&gt;

&lt;p&gt;When you add &lt;strong&gt;Viewscount&lt;/strong&gt; to your application, you can begin tracking views instantly—without having to write separate APIs for each table. Let’s say you have tables like &lt;code&gt;tbl_driver&lt;/code&gt; or &lt;code&gt;tbl_vehicle&lt;/code&gt;, each with a &lt;code&gt;view_count&lt;/code&gt; column. Instead of building an API specifically to increment the &lt;code&gt;view_count&lt;/code&gt; each time a user views a page, you can use &lt;strong&gt;Viewscount&lt;/strong&gt; to track this automatically.&lt;/p&gt;

&lt;p&gt;Here’s how it works in practice:&lt;/p&gt;

&lt;p&gt;In your database, you simply add the &lt;code&gt;view_count&lt;/code&gt; column to your tables like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;tbl_driver&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;              &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;      &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;view_count&lt;/span&gt;      &lt;span class="nb"&gt;INT&lt;/span&gt;          &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;deleted&lt;/span&gt;         &lt;span class="nb"&gt;INT&lt;/span&gt;          &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same goes for your other tables. No need for complicated queries or APIs—just a simple column to track views. &lt;/p&gt;

&lt;h3&gt;
  
  
  Integrating Viewscount into Your Application
&lt;/h3&gt;

&lt;p&gt;The best part of &lt;strong&gt;Viewscount&lt;/strong&gt; is that it’s designed to be &lt;strong&gt;framework-agnostic&lt;/strong&gt;. Whether you're using &lt;strong&gt;Gin&lt;/strong&gt;, &lt;strong&gt;Chi&lt;/strong&gt;, or even &lt;strong&gt;net/http&lt;/strong&gt;, you can easily integrate it into your application. Here’s how you can add it as middleware in &lt;strong&gt;Gin&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;First, you’ll need to initialize the view tracker in your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;middlewares&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/mikebionic/viewscount"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;viewTracker&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;viewscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewTracker&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;InitializeViewTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;minutesGap&lt;/span&gt; &lt;span class="n"&gt;time&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="n"&gt;viewTracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewscount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewViewTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutesGap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ViewCounterMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&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;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&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="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&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="n"&gt;idInt&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sscanf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;idInt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;viewTracker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idInt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error tracking view: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&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;Once you’ve set up the middleware, you can add it to your routes. Here’s how you do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Initialize the database and middleware&lt;/span&gt;
    &lt;span class="n"&gt;middlewares&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InitializeViewTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

    &lt;span class="c"&gt;// Define routes&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;middlewares&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewCounterMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tbl_driver"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetDriver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&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 simple setup integrates the &lt;strong&gt;Viewscount&lt;/strong&gt; library into your existing app. The middleware will automatically track views on the specified table and increment the &lt;code&gt;view_count&lt;/code&gt; column as needed, while also providing protection against abuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Benefits of Viewscount
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Integration&lt;/strong&gt;: With just a few lines of code, you can integrate view counting into any of your existing tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework-Agnostic&lt;/strong&gt;: The library is designed to work with any Golang web framework, making it easy to use in your project regardless of the stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prevention of DOS Attacks&lt;/strong&gt;: By limiting rapid requests that could artificially inflate view counts, &lt;strong&gt;Viewscount&lt;/strong&gt; ensures that the data remains reliable and accurate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient and Scalable&lt;/strong&gt;: No need for custom APIs or complex queries—just use the library and track views automatically in real time.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Viewscount&lt;/strong&gt; solves a common problem in application development: how to track views across tables securely and efficiently without having to build a separate API for each table. Its easy integration, ability to prevent DOS-style attacks, and framework-agnostic design make it a powerful tool for developers building modern Golang applications.&lt;/p&gt;

&lt;p&gt;Feel free to contribute to the project and raise issues or suggestions via &lt;a href="https://github.com/mikebionic/viewscount" rel="noopener noreferrer"&gt;Viewscount GitHub&lt;/a&gt;. I’d love to see how this library can help others in their development journey!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>viewscount</category>
      <category>go</category>
      <category>library</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Hacking Go-TFE and Fetching All Workspaces in Terraform Enterprise: A Journey Through Pagination</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Sun, 16 Feb 2025 11:59:36 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/hacking-go-tfe-and-fetching-all-workspaces-in-terraform-enterprise-a-journey-through-pagination-4lel</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/hacking-go-tfe-and-fetching-all-workspaces-in-terraform-enterprise-a-journey-through-pagination-4lel</guid>
      <description>&lt;p&gt;Terraform Enterprise (TFE) is a powerful platform for organizations using Terraform at scale. It provides collaboration, governance, and self-service workflows for infrastructure automation. As organizations grow, managing infrastructure becomes increasingly complex, often requiring the ability to programmatically interact with TFE using its robust API.&lt;/p&gt;

&lt;p&gt;Recently, I found myself in a situation where I needed to fetch &lt;strong&gt;all workspaces&lt;/strong&gt; for a given organization in TFE. Workspaces, as you may know, are the fundamental units in Terraform where runs occur. Each workspace holds the Terraform state file, which tracks infrastructure resources. For automation and reporting, I needed to pull in the full list of workspaces, but I quickly ran into a seemingly simple yet frustrating problem: &lt;strong&gt;pagination&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Goal: Get All Workspaces
&lt;/h3&gt;

&lt;p&gt;Our goal was clear — we wanted to retrieve every single workspace associated with a particular organization, without worrying about pagination limits. Whether for generating reports, validating configurations, or orchestrating CI/CD workflows, having a full view of all workspaces was crucial.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;go-tfe&lt;/code&gt; library by HashiCorp provides an elegant way to interact with TFE’s API. The most straightforward way to list workspaces looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;workSpaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tfeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orgName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there was a catch.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pain Point: Pagination Woes
&lt;/h3&gt;

&lt;p&gt;When we ran this code, we noticed something odd — it only returned &lt;strong&gt;20 workspaces&lt;/strong&gt;. After digging into the documentation and the source code of the &lt;code&gt;go-tfe&lt;/code&gt; library, it became clear that the API defaults to a &lt;strong&gt;page size of 20&lt;/strong&gt;. Even when we explicitly set the page size to the maximum limit of 100, it still only gave us one page of results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;workSpaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tfeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orgName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tfe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorkspaceListOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ListOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tfe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;PageNumber&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;PageSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="m"&gt;100&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 issue was evident: &lt;strong&gt;the API uses pagination&lt;/strong&gt;. This means that if your organization has more than 100 workspaces (which is not uncommon for larger teams), you have to make multiple requests to get all of them.&lt;/p&gt;

&lt;p&gt;But we didn’t want to be bound by pagination. We wanted all the workspaces in one go — a clean, consolidated list.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Handling Pagination Manually
&lt;/h3&gt;

&lt;p&gt;To work around the pagination limit, the only solution was to &lt;strong&gt;manually paginate&lt;/strong&gt; through the results until all workspaces were fetched. We crafted the following solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetchAllWorkspaces&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&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;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tfe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orgName&lt;/span&gt; &lt;span class="kt"&gt;string&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="n"&gt;tfe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workspace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;allWorkspaces&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tfe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workspace&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;pageSize&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="c"&gt;// Max allowed page size&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tfe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WorkspaceListOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ListOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tfe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;PageNumber&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;PageSize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;pageSize&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;workspaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orgName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;allWorkspaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allWorkspaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;workspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workspaces&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt; &lt;span class="c"&gt;// No more pages left&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;++&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;allWorkspaces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breaking Down the Solution
&lt;/h3&gt;

&lt;p&gt;Let’s walk through the logic step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initialization&lt;/strong&gt;: We start with an empty slice to hold all workspaces and set the page number to 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pagination Loop&lt;/strong&gt;: Inside the &lt;code&gt;for&lt;/code&gt; loop, we make requests using the &lt;code&gt;ListOptions&lt;/code&gt;, setting both the page number and the maximum page size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Appending Results&lt;/strong&gt;: After each request, we append the returned workspaces to our &lt;code&gt;allWorkspaces&lt;/code&gt; slice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit Condition&lt;/strong&gt;: If the number of workspaces in the current response is less than the page size (meaning we’ve reached the last page), we break out of the loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Result&lt;/strong&gt;: Finally, we return the full list of workspaces.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;Fetching all workspaces might seem like a trivial task at first glance, but it highlights a crucial aspect of working with APIs — understanding and handling pagination correctly.&lt;/p&gt;

&lt;p&gt;When dealing with APIs that support pagination:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always check default page sizes.&lt;/li&gt;
&lt;li&gt;Understand the max page size allowed.&lt;/li&gt;
&lt;li&gt;Implement proper loop termination to avoid infinite loops.&lt;/li&gt;
&lt;li&gt;Consider API rate limits when making multiple requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our case, the solution was not only about fetching data but about ensuring the stability and reliability of our automation pipelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In the end, handling pagination in TFE’s API required us to step back, understand the underlying mechanics, and build a robust way to gather all the data we needed. The final solution is now part of our infrastructure tooling, allowing us to work seamlessly with Terraform Enterprise’s workspaces.&lt;/p&gt;

&lt;p&gt;So next time you hit a wall with an API and pagination, remember — it’s not a bug; it’s a feature. Master it, and your automation game will only grow stronger.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;h1&gt;
  
  
  terraform #golang #go-tfe #workspaces #fetching #coding #solution #server #cicd
&lt;/h1&gt;

</description>
      <category>go</category>
      <category>terraform</category>
      <category>solution</category>
      <category>pagination</category>
    </item>
    <item>
      <title>7 Beneficial News in Go 1.24 That Improves A Programming Experience!</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Thu, 13 Feb 2025 18:55:04 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/7-beneficial-news-in-go-124-that-improves-a-programming-experience-pb0</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/7-beneficial-news-in-go-124-that-improves-a-programming-experience-pb0</guid>
      <description>&lt;p&gt;Go 1.24 brings an exciting set of improvements, making the language more efficient, secure, and developer-friendly. Whether you're optimizing performance, improving maintainability, or simplifying debugging, these updates can make a big difference in your projects.&lt;/p&gt;

&lt;p&gt;Let's dive into the highlights and see how they can benefit you!&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Performance Enhancements in Garbage Collection
&lt;/h2&gt;

&lt;p&gt;Go 1.24 introduces optimizations to the garbage collector (GC), reducing pause times and improving memory efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why It Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you're working with high-throughput applications like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time streaming services&lt;/li&gt;
&lt;li&gt;Large-scale web applications&lt;/li&gt;
&lt;li&gt;High-performance microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll see improved responsiveness and reduced latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example Use Case&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before Go 1.24, GC pauses could cause brief performance hiccups in applications processing millions of requests per second. With these improvements, apps handle memory more smoothly, reducing disruptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Improved Typing and Map Enhancements
&lt;/h2&gt;

&lt;p&gt;Go 1.24 refines type inference and simplifies working with maps.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why It Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;More convenient type handling in generics&lt;/li&gt;
&lt;li&gt;Easier manipulation of maps with better performance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example Use Case&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;map&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="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"apple"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"banana"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// New optimizations make working with maps smoother and faster&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&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;"banana"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Banana count:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qty&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 makes handling maps more predictable and performant in real-world applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Better Error Handling with &lt;code&gt;errors.Join&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Error handling has always been a key part of Go, and Go 1.24 improves it by enhancing &lt;code&gt;errors.Join&lt;/code&gt; for wrapping multiple errors in a more readable and structured way.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why It Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your application depends on multiple subsystems (e.g., databases, external APIs), error handling can become complex. This update makes it easier to aggregate errors and present them more clearly.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example Use Case&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&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;err1&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"database connection failed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cache unavailable"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// Output: database connection failed; cache unavailable&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this change, debugging multi-source failures becomes more intuitive and less time-consuming.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. &lt;code&gt;os.OpenRoot()&lt;/code&gt;: Simplified Directory Access
&lt;/h2&gt;

&lt;p&gt;A new function, &lt;code&gt;os.OpenRoot()&lt;/code&gt;, has been added to make directory-based operations more convenient and secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why It Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Previously, handling root directory paths safely required workarounds. With &lt;code&gt;os.OpenRoot()&lt;/code&gt;, directory manipulation becomes easier and more secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example Use Case&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&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;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenRoot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Root directory opened successfully"&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 small helper simplifies working with directories in cross-platform applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. JSON Serialization with &lt;code&gt;omitzero&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A long-requested feature, &lt;code&gt;omitzero&lt;/code&gt;, has been introduced for JSON serialization. It allows you to automatically omit zero-value fields when marshaling structs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why It Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Previously, developers had to manually filter out zero values or use workarounds. Now, you can use &lt;code&gt;omitzero&lt;/code&gt; for cleaner JSON outputs without unnecessary fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example Use Case&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Name&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"name"`&lt;/span&gt;
    &lt;span class="n"&gt;Age&lt;/span&gt;   &lt;span class="kt"&gt;int&lt;/span&gt;    &lt;span class="s"&gt;`json:"age,omitempty"`&lt;/span&gt;
    &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"email,omitzero"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&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;user&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"John"&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;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&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="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// Output: {"name":"John"}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;omitzero&lt;/code&gt;, you no longer need to pre-process structs to remove empty fields manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Improvements to &lt;code&gt;cmd/go&lt;/code&gt; for Module Proxy Handling
&lt;/h2&gt;

&lt;p&gt;Go 1.24 refines the &lt;code&gt;go&lt;/code&gt; command, making module proxy interactions more efficient. This results in faster builds and better dependency management.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why It Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you're using large Go projects with multiple dependencies, you’ll notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster &lt;code&gt;go get&lt;/code&gt; execution&lt;/li&gt;
&lt;li&gt;Improved caching of modules&lt;/li&gt;
&lt;li&gt;Better resilience in network-constrained environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example Use Case&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before Go 1.24, resolving modules over slow or unreliable networks could be frustrating. Now, with improved proxy handling, you experience fewer build failures and faster package retrieval.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Language Refinements and Internal Improvements
&lt;/h2&gt;

&lt;p&gt;Go 1.24 also brings refinements to language internals, including better compiler optimizations and standard library improvements. These updates lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;More optimized runtime performance&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smaller binary sizes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced debugging tools&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why It Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For developers building cloud-native applications, CLI tools, or serverless functions, every byte and CPU cycle counts. Go 1.24 helps ensure that your applications run efficiently without unnecessary overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example Use Case&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you maintain a CLI tool that users download frequently, a smaller binary reduces bandwidth costs and improves execution speed on resource-constrained environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts: Why You Should Upgrade to Go 1.24
&lt;/h2&gt;

&lt;p&gt;Go 1.24 is more than just an incremental update—it's a step forward in efficiency, reliability, and developer experience. With key improvements in garbage collection, error handling, typing, module proxy interactions, JSON handling, and performance optimizations, upgrading ensures you stay ahead with a more robust Go environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrade today and experience the difference!&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>go</category>
      <category>updates</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NASA Space Apps Challenge</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Sun, 09 Feb 2025 07:47:07 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/nasa-space-apps-challenge-418b</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/nasa-space-apps-challenge-418b</guid>
      <description>&lt;p&gt;NASA Space Apps Challenge - dünýäde iň uly we elýeterli hakatonlardan biri bolup, NASA tarapyndan 2012-nji ýylda döredilen global innowasiýa bäsleşigidir. Maksady - jemgyýetiň dürli künjeginden gelen täzelikçi zehinleri bir ýere jemläp, NASA-nyň we beýleki hyzmatdaş guramalaryň kosmos bilen bagly maglumatlaryny ulanmak arkaly häzirki zaman meselelerine çözgüt tapmakdyr. Bu çärä programmistler, dizaýnerler, inženerler, alymlar, mugallymlar we dünýäni gowulandyrmak isleýän ähli adamlar gatnaşyp biler.&lt;br&gt;
Registrasiýa&lt;/p&gt;

&lt;p&gt;NASA Space Apps Challenge-na gatnaşmak üçin şu ädimleri ýerine ýetirip bilersiňiz:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ofisial saýta girip, şahsy maglumatlaryňyzy doldurmak arkaly hasap açyň. Bu saýta girip bilersiňiz: spaceappschallenge.org
Bäsleşigiň ýerleşýän ýerini saýlaň (ýa-da onlaýn formatda hem gatnaşyp bilersiňiz).
Topara goşulyň ýa-da öz toparyňyzy dörediň.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Bäsleşikdäki Challenge-ler&lt;/p&gt;

&lt;p&gt;Her ýyl çärä birnäçe kategoriýada täze we döwrebap meseleler (challenge-ler) hödürlenýär. Meseleleriň käbir görnüşleri şulardan ybarat:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ýer we howa üýtgemesi: Adamzat üçin ýeri we howa üýtgemegini çözmek.
Käin öwrenmek: Planetalaryň we ýyldyzlaryň hereketini analiz etmek.
Innowasiýa we tehnologiýa: Täze tehnologiýalar we işleşýän çözgütler döretmek.
Data analitikasy: NASA-nyň açyk maglumatlaryny ulanmak bilen, maglumatlaryň üstünde işlemek.
Häsiýet we dizaýn: Dizaýnlaryň we infografikalaryň kömegi bilen kosmos maglumatlaryny köpçülige düşündirmek.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Bu meseleler hem-de olaryň düşündirişi bäsleşik başlamazdan öň maglumatlarda berilýär. Her kim öz gyzyklanýan meselelerini saýlap, çözgüt işläp taýýarlap biler.&lt;br&gt;
Şoňa meňzeş çäreler&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Google Code-in: Okuwa gatnaşýan ýaş programmistler üçin bäsleşik.
Hack The Planet: Ekologiýa we klimata degişli meseleleri çözmek maksady bilen geçirilen global hakaton.
European Space Agency (ESA) Challenge: ESA tarapyndan gurnalan kosmos temasyndaky bäsleşikler.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;NASA Space Apps Challenge, pikir alşyp, täze çözgütler döretmek we tehnologik mümkinçilikleri ösdürmek isleýänler üçin ajaýyp mümkinçilikdir.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.spaceappschallenge.org/nasa-space-apps-2024/2024-local-events/ashgabat/" rel="noopener noreferrer"&gt;https://www.spaceappschallenge.org/nasa-space-apps-2024/2024-local-events/ashgabat/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 2024 NASA Space Apps Challenge took place on October 5-6, 2024. It is a global hackathon that welcomes participants from all over the world, both virtually and in-person, to work on challenges related to space and Earth science using NASA's open data. This year's theme, "The Sun Touches Everything," encourages participants to explore the influence of the Sun in various fields such as data visualization, AI, machine learning, and more.&lt;/p&gt;

&lt;p&gt;You can register for the event and find all necessary details on the official website spaceappschallenge.org. For questions about local events or organizing participation, it's best to contact local leads or check the platform's contact information.&lt;/p&gt;

&lt;p&gt;Winners of the hackathon can receive global recognition. In previous years, there have been categories such as "Most Disruptive," "Best Use of Data," and "People’s Choice." These awards are evaluated by experts from NASA and other partner space agencies&lt;/p&gt;

</description>
      <category>mentorship</category>
      <category>nasa</category>
      <category>ai</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Deploying Secure Go Apps on GCP: A Journey from Local to Cloud</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Sat, 08 Feb 2025 13:19:30 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/deploying-secure-go-apps-on-gcp-a-journey-from-local-to-cloud-2pik</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/deploying-secure-go-apps-on-gcp-a-journey-from-local-to-cloud-2pik</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.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%2Fxx8zfn26f1m0qf3301ix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fxx8zfn26f1m0qf3301ix.png" alt="Image description" width="786" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was 3 AM, and I was staring at my computer screen, trying to figure out why our Go application kept crashing in production. Our small e-commerce startup had just experienced its first major traffic spike, and our hosting setup wasn’t ready for it. That night changed how I thought about deploying Go applications forever.&lt;/p&gt;

&lt;p&gt;The Problem We Faced&lt;/p&gt;

&lt;p&gt;Our team had built a simple but powerful inventory management system using Go. It worked perfectly on our local machines, but when we deployed it to production, we faced several challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Random crashes during peak hours&lt;/li&gt;
&lt;li&gt;Security vulnerabilities we hadn’t considered&lt;/li&gt;
&lt;li&gt;Scaling issues that cost us customers&lt;/li&gt;
&lt;li&gt;Credentials being accidentally exposed in our code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enter Google Cloud Platform (GCP)&lt;/p&gt;

&lt;p&gt;After that difficult night, we decided to rebuild our deployment process using GCP. Here’s the story of how we transformed our application deployment from a source of stress into a smooth, secure operation.&lt;/p&gt;

&lt;p&gt;Setting Up a Secure Foundation&lt;/p&gt;

&lt;p&gt;First, let’s look at how we structured our Go application for secure deployment. Here’s a basic example of our initial setup:&lt;/p&gt;

&lt;p&gt;`package main&lt;br&gt;
import (&lt;br&gt;
 "log"&lt;br&gt;
 "os"&lt;br&gt;
 "net/http"&lt;br&gt;
 "github.com/joho/godotenv"&lt;br&gt;
)&lt;br&gt;
func main() {&lt;br&gt;
 // Load environment variables&lt;br&gt;
 if err := godotenv.Load(); err != nil {&lt;br&gt;
 log.Printf("No .env file found")&lt;br&gt;
 }&lt;/p&gt;

&lt;p&gt;// Get port from environment variable&lt;br&gt;
 port := os.Getenv("PORT")&lt;br&gt;
 if port == "" {&lt;br&gt;
 port = "8080"&lt;br&gt;
 }&lt;/p&gt;

&lt;p&gt;http.HandleFunc("/", handleHome)&lt;br&gt;
 log.Printf("Starting server on port %s", port)&lt;br&gt;
 log.Fatal(http.ListenAndServe(":" + port, nil))&lt;br&gt;
}&lt;br&gt;
func handleHome(w http.ResponseWriter, r *http.Request) {&lt;br&gt;
 // Implement secure headers&lt;br&gt;
 w.Header().Set("X-Frame-Options", "DENY")&lt;br&gt;
 w.Header().Set("X-Content-Type-Options", "nosniff")&lt;br&gt;
 w.Header().Set("Content-Security-Policy", "default-src 'self'")&lt;/p&gt;

&lt;p&gt;w.Write([]byte("Welcome to our secure Go application!"))&lt;br&gt;
}`&lt;/p&gt;

&lt;h2&gt;
  
  
  Securing Secrets with GCP Secret Manager
&lt;/h2&gt;

&lt;p&gt;One of our biggest concerns was managing secrets. Instead of storing sensitive data in environment variables, we moved to GCP Secret Manager:&lt;br&gt;
`&lt;br&gt;
import (&lt;br&gt;
 secretmanager "cloud.google.com/go/secretmanager/apiv1"&lt;br&gt;
 secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"&lt;br&gt;
)&lt;br&gt;
func getSecret(projectID, secretID string) (string, error) {&lt;br&gt;
 ctx := context.Background()&lt;br&gt;
 client, err := secretmanager.NewClient(ctx)&lt;br&gt;
 if err != nil {&lt;br&gt;
 return "", err&lt;br&gt;
 }&lt;br&gt;
 defer client.Close()&lt;/p&gt;

&lt;p&gt;name := fmt.Sprintf("projects/%s/secrets/%s/versions/latest", projectID, secretID)&lt;br&gt;
 req := &amp;amp;secretmanagerpb.AccessSecretVersionRequest{&lt;br&gt;
 Name: name,&lt;br&gt;
 }&lt;/p&gt;

&lt;p&gt;result, err := client.AccessSecretVersion(ctx, req)&lt;br&gt;
 if err != nil {&lt;br&gt;
 return "", err&lt;br&gt;
 }&lt;/p&gt;

&lt;p&gt;return string(result.Payload.Data), nil&lt;br&gt;
}`&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Cloud Run
&lt;/h2&gt;

&lt;p&gt;We chose Cloud Run for deployment because it offered:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Automatic scaling&lt;/li&gt;
&lt;li&gt;Pay-per-use pricing&lt;/li&gt;
&lt;li&gt;Built-in security features&lt;/li&gt;
&lt;li&gt;Simple deployment process&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s our Dockerfile that we use for deployment:&lt;br&gt;
`&lt;br&gt;
FROM golang:1.21-alpine&lt;br&gt;
WORKDIR /app&lt;br&gt;
COPY go.mod go.sum ./&lt;br&gt;
RUN go mod download&lt;br&gt;
COPY . .&lt;br&gt;
RUN go build -o main .&lt;/p&gt;

&lt;h1&gt;
  
  
  Use a minimal alpine image for the final stage
&lt;/h1&gt;

&lt;p&gt;FROM alpine:latest&lt;br&gt;
WORKDIR /app&lt;br&gt;
COPY - from=0 /app/main .&lt;/p&gt;

&lt;h1&gt;
  
  
  Run as non-root user
&lt;/h1&gt;

&lt;p&gt;RUN adduser -D appuser&lt;br&gt;
USER appuser&lt;br&gt;
CMD ["./main"]`&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Security Best Practices
&lt;/h2&gt;

&lt;p&gt;We learned to follow these key security practices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Always use HTTPS&lt;/li&gt;
&lt;li&gt;Implement rate limiting&lt;/li&gt;
&lt;li&gt;Use proper logging&lt;/li&gt;
&lt;li&gt;Regular security scanning&lt;/li&gt;
&lt;li&gt;Implement proper authentication&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s an example of how we implemented rate limiting:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import (&lt;br&gt;
 "golang.org/x/time/rate"&lt;br&gt;
 "sync"&lt;br&gt;
)&lt;br&gt;
type IPRateLimiter struct {&lt;br&gt;
 ips map[string]*rate.Limiter&lt;br&gt;
 mu *sync.RWMutex&lt;br&gt;
 r rate.Limit&lt;br&gt;
 b int&lt;br&gt;
}&lt;br&gt;
func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter {&lt;br&gt;
 return &amp;amp;IPRateLimiter{&lt;br&gt;
 ips: make(map[string]*rate.Limiter),&lt;br&gt;
 mu: &amp;amp;sync.RWMutex{},&lt;br&gt;
 r: r,&lt;br&gt;
 b: b,&lt;br&gt;
 }&lt;br&gt;
}&lt;br&gt;
func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter {&lt;br&gt;
 i.mu.Lock()&lt;br&gt;
 defer i.mu.Unlock()&lt;br&gt;
limiter := rate.NewLimiter(i.r, i.b)&lt;br&gt;
 i.ips[ip] = limiter&lt;br&gt;
return limiter&lt;br&gt;
}&lt;br&gt;
func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {&lt;br&gt;
 i.mu.Lock()&lt;br&gt;
 limiter, exists := i.ips[ip]&lt;br&gt;
if !exists {&lt;br&gt;
 i.mu.Unlock()&lt;br&gt;
 return i.AddIP(ip)&lt;br&gt;
 }&lt;br&gt;
i.mu.Unlock()&lt;br&gt;
 return limiter&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
The Results&lt;/p&gt;

&lt;p&gt;After implementing these changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Our application handled traffic spikes smoothly&lt;/li&gt;
&lt;li&gt;Security vulnerabilities were significantly reduced&lt;/li&gt;
&lt;li&gt;Deployment became a straightforward process&lt;/li&gt;
&lt;li&gt;Our team could sleep better at night!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where Can You Use This?&lt;/p&gt;

&lt;p&gt;This setup is perfect for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;E-commerce platforms&lt;/li&gt;
&lt;li&gt;API services&lt;/li&gt;
&lt;li&gt;Web applications&lt;/li&gt;
&lt;li&gt;Microservices&lt;/li&gt;
&lt;li&gt;Data processing systems&lt;/li&gt;
&lt;li&gt;Business automation tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;That 3 AM incident taught us valuable lessons about deploying Go applications securely. By leveraging GCP’s features and following security best practices, we transformed our deployment process from a source of stress into a reliable system.&lt;/p&gt;

&lt;p&gt;Remember: security isn’t a one-time setup but a continuous journey. Keep learning, keep improving, and most importantly, keep your applications secure.&lt;/p&gt;

&lt;p&gt;Want to learn more? Check out the official Go documentation and GCP’s security best practices guide for more detailed information.&lt;/p&gt;

</description>
      <category>go</category>
      <category>gcp</category>
      <category>googlecloud</category>
      <category>webapp</category>
    </item>
    <item>
      <title>How to build an EASIEST Smart Traffic App using OpenCV with RaspberryPi and Arduino | IoT Application</title>
      <dc:creator>Muhammetberdi Jepbarov</dc:creator>
      <pubDate>Sat, 08 Feb 2025 13:17:20 +0000</pubDate>
      <link>https://dev.to/muhammetberdi_jepbarov/how-to-build-an-easiest-smart-traffic-app-using-opencv-with-raspberrypi-and-arduino-iot-pla</link>
      <guid>https://dev.to/muhammetberdi_jepbarov/how-to-build-an-easiest-smart-traffic-app-using-opencv-with-raspberrypi-and-arduino-iot-pla</guid>
      <description>&lt;p&gt;Automation system for traffic control, vehicle and pedestrian detection.&lt;/p&gt;

&lt;p&gt;Project Source code is here&lt;/p&gt;

&lt;p&gt;Basic goals&lt;/p&gt;

&lt;p&gt;This project is intended to improve road traffic processes and ensure pedestrian safety.&lt;/p&gt;

&lt;p&gt;The program should:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Detect vehicles and read traffic information
Make calculations and set favorable traffic light times
Keep pedestrians and vehicles safe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;How does it work?&lt;/p&gt;

&lt;p&gt;Using the technology of computer image and video processing, the program reads traffic conditions from cameras. This is necessary in order to obtain information about traffic congestion, the number of pedestrians.&lt;/p&gt;

&lt;p&gt;The data is transferred to the micro controller, which in turn controls the state and time of the traffic light.&lt;/p&gt;

&lt;p&gt;Activate the system&lt;/p&gt;

&lt;p&gt;To start the system, you must use a computer or microcomputer with preinstalled software. Also connected to one of the USB connectors of the Arduino microprocessor. To get information about traffic, the program needs to indicate from which sources&lt;/p&gt;

&lt;p&gt;you need to read the video stream. It can be two webcams connected to USB ports, or a link to online cameras.&lt;/p&gt;

&lt;p&gt;After that, two windows with a video stream will appear on the graphical user interface of the computer. It is necessary for calibrating cameras. After calibrating, you must press the “C” key on the keyboard, after which the program will display two video streams with information about the proportions of objects on the road and their absence. If at this stage you start to arrange cars, the number of proportions will begin to increase and the data on occupancy will be sent to the microcontroller that controls the time of the traffic light. As a result, if there is more vehicle on one road than on the other, the microprocessor will set the time of the green light of the traffic light, determined from the proportion value.&lt;/p&gt;

&lt;p&gt;Also, the software part of the image recognition system has another version, which provides machine learning and the creation of car models, according to which the recognition system and proportion calculation will work.&lt;/p&gt;

&lt;p&gt;Further development&lt;/p&gt;

&lt;p&gt;The next steps would be:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;improved recognition
determination of the required recognition zone for a clearer analysis
timely automatic calibrations
identifying traffic information on a web page for road safety officials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Programming languages&lt;/p&gt;

&lt;p&gt;Python version above 3.6&lt;/p&gt;

&lt;p&gt;C++ with Arduino library&lt;/p&gt;

&lt;p&gt;Needed materials&lt;/p&gt;

&lt;p&gt;Material + quantity&lt;/p&gt;

&lt;p&gt;RaspberryPi 4 x1&lt;/p&gt;

&lt;p&gt;Web camera / Network camera stream x2&lt;/p&gt;

&lt;p&gt;Arduino x1&lt;/p&gt;

&lt;p&gt;Led strip x12 diodes&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
