<?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: Carlos Armando Marcano Vargas</title>
    <description>The latest articles on DEV Community by Carlos Armando Marcano Vargas (@carlosm27).</description>
    <link>https://dev.to/carlosm27</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%2F657157%2Fe7550fa7-ced4-4836-8cc6-a283e3b3a90f.jpg</url>
      <title>DEV Community: Carlos Armando Marcano Vargas</title>
      <link>https://dev.to/carlosm27</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/carlosm27"/>
    <language>en</language>
    <item>
      <title>Analyzing Network Traffic With Wireshark</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Wed, 03 Apr 2024 02:05:57 +0000</pubDate>
      <link>https://dev.to/carlosm27/analyzing-network-traffic-with-wireshark-emo</link>
      <guid>https://dev.to/carlosm27/analyzing-network-traffic-with-wireshark-emo</guid>
      <description>&lt;p&gt;Previously, I published an article about analyzing network traffic with Tcpdump. In this article, we are going to analyze network traffic using Wireshark. We will learn what Wireshark is, how to install it, and how to analyze network traffic by reading a pcap file and applying filters.&lt;/p&gt;

&lt;p&gt;The content of this article is limited by what I learned about Wireshark in the Google Cybersecurity course. So, this is not comprehensive material about Wireshark.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Wireshark?
&lt;/h3&gt;

&lt;p&gt;According to the Wireshark official page:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wireshark is a network protocol analyzer. It lets you capture and interactively browse the traffic running on a computer network. It has a rich and powerful feature set and is one worlds most popular tool of its kind. It runs on most computing platforms including Windows, macOS, Linux, and UNIX.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Installing Wireshark
&lt;/h3&gt;

&lt;p&gt;To install Wireshark, we visit this &lt;a href="https://www.wireshark.org/download.html" rel="noopener noreferrer"&gt;page&lt;/a&gt; and select one of the options available according to our OS.&lt;/p&gt;

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

&lt;p&gt;I have Windows 11, so I have to select "Windows x64 Installer".&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Exploring a pcap file
&lt;/h3&gt;

&lt;p&gt;After installing Wireshark, we open the application and explore a pcap file.&lt;/p&gt;

&lt;p&gt;We can download a sample pcap file from these &lt;a href="https://wiki.wireshark.org/SampleCaptures#sample-captures" rel="noopener noreferrer"&gt;websites&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I downloaded a sample for this &lt;a href="https://packetlife.net/captures/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to use the same files I used to follow along, these are the ones I'm used for these examples:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packetlife.net/media/captures/arp_pcap.pcapng.cap" rel="noopener noreferrer"&gt;arp_pcap.pcapng.cap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://packetlife.net/media/captures/Open%20Network%20Connection.pcapng.cap" rel="noopener noreferrer"&gt;Open Network Connection.pcapng.cap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we open a sample with Wireshark. I will open first the "arp_pcap.pcapng.cap" file.&lt;/p&gt;

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

&lt;p&gt;As we can see in the image above, Wireshark shows seven columns or attributes.&lt;/p&gt;

&lt;p&gt;"No." &lt;strong&gt;is&lt;/strong&gt; the index number of the packet in this packet capture file. &lt;strong&gt;Time&lt;/strong&gt; is the packet's timestamp. &lt;strong&gt;Source&lt;/strong&gt; is the source IP address. &lt;strong&gt;Destination&lt;/strong&gt; , The destination IP address. &lt;strong&gt;Protocol&lt;/strong&gt; is the protocol contained in the packet. &lt;strong&gt;Length is&lt;/strong&gt; the total length of the packet. &lt;strong&gt;Info,&lt;/strong&gt; some information about the data in the packet (the payload) as interpreted by Wireshark.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using filters
&lt;/h3&gt;

&lt;p&gt;Now, we can use the search bar in Wireshark to apply filters.&lt;/p&gt;

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

&lt;p&gt;I will search all the packets that contain the IP address "192.168.1.110" no matter if it is the source or destination. So, we need to write this text in the search bar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip.addr == 192.168.1.110
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If we double-click on any packet, we can have more detailed information about it.&lt;/p&gt;

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

&lt;p&gt;If we click on &lt;strong&gt;Frame&lt;/strong&gt; , which shows details about the overall network packet, including the packet's length and arrival time.&lt;/p&gt;

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

&lt;p&gt;Then, we click on &lt;strong&gt;Ethernet II&lt;/strong&gt; , this subtree contains details about the packet at the Ethernet level, including the source and destination MAC addresses and the type of internal protocol the Ethernet packet contains.&lt;/p&gt;

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

&lt;p&gt;Next, we click on &lt;strong&gt;Internet Protocol Version 4,&lt;/strong&gt; this section contains information about the source and destination IP addresses and the Internal Protocol (for example, TCP or UDP), which is carried inside the IP packet.&lt;/p&gt;

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

&lt;p&gt;In the case of the file we are analyzing, the packet shows User Datagram Protocol, UDP, and this section contains information about UDP ports. It will show &lt;strong&gt;Transmission Control Protocol&lt;/strong&gt; if the file that is being analyzed contains packets using a TCP connection.&lt;/p&gt;

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

&lt;p&gt;If we want to be more specific with our search, we can use the filter "ip.src" to look for an IP address as source or "ip.dst" to look for an IP address as destination.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip.src == 153.16.31.81
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ip.dst == 147.83.131.33
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We can use a filter to look for a specific MAC address too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eth.addr == 30:b5:c2:eb:4c:b0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Also, we can use a filter to examine DNS traffic.&lt;/p&gt;

&lt;p&gt;DNS is the system used to resolve store information about domain names including IP addresses, mail servers, and other information.&lt;/p&gt;

&lt;p&gt;I will open a different pcap file ("The open connection" file) for this task.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;udp.port == 53
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We can select any entry, scroll down, and double-click on Domain Name System (query).&lt;/p&gt;

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

&lt;p&gt;We click on "Queries" and in the case of the file I am analyzing, we can see the queried website (cl.qustodio.com).&lt;/p&gt;

&lt;p&gt;If we click on the "Answers" section, we can see the address associated with the website (50.17.253.86).&lt;/p&gt;

&lt;p&gt;Finally, in this article, we will examine the traffic of a TCP port.&lt;/p&gt;

&lt;p&gt;We use the filter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tcp.port == 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TCP port 80 is the default port that is associated with web traffic.&lt;/p&gt;

&lt;p&gt;We select any entry. And double-click on "Internet Protocol Version 4". We scroll down and see the packet's &lt;strong&gt;Time To Live&lt;/strong&gt; value, it indicates the maximum amount of time a packet can exist on the network before being discarded.&lt;/p&gt;

&lt;p&gt;In this section, we can see the &lt;strong&gt;Header Length&lt;/strong&gt; , the total size of the header section, in this case, 20 bytes.&lt;/p&gt;

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

&lt;p&gt;Clicking on the "Frame" section, we can see the &lt;strong&gt;Frame Length&lt;/strong&gt; , which is the total size of the captured network packet.&lt;/p&gt;

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

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

&lt;p&gt;So far, I have tried TCPdump and Wireshark. Both are amazing, useful, and widely used tools. However, the GUI that Wireshark offers makes analyzing network data more comfortable, in my opinion. If you want to try a packet sniffer with a GUI, that is widely used in the industry, I recommend Wireshark.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv" rel="noopener noreferrer"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.wireshark.org/faq.html#_what_is_wireshark" rel="noopener noreferrer"&gt;What is Wireshark?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wiki.wireshark.org/SampleCaptures#sample-captures" rel="noopener noreferrer"&gt;Sample Captures&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wireshark</category>
      <category>packet</category>
      <category>sniffer</category>
      <category>cybersecutiry</category>
    </item>
    <item>
      <title>Get Started with TCPdump: Capture and Analyze Network Traffic</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Mon, 11 Mar 2024 01:06:14 +0000</pubDate>
      <link>https://dev.to/carlosm27/get-started-with-tcpdump-capture-and-analyze-network-traffic-35bp</link>
      <guid>https://dev.to/carlosm27/get-started-with-tcpdump-capture-and-analyze-network-traffic-35bp</guid>
      <description>&lt;p&gt;In this article,, we will explore what is Tcpdump and how to use some of its basic features like listing interfaces available, capturing data packets, and reading packet data files.&lt;/p&gt;

&lt;p&gt;I recently started to learn how to use TCPdump as a part of the Google Cybersecurity course. And I thought it was a good idea to write and share what I learned, and also to have material to revisit in the future.&lt;/p&gt;

&lt;p&gt;All the console screenshots are from a virtual machine I used in a lab that was part of the Cybersecurity course, this means that the information that is printed out probably will be different from yours. Also, many of the explanation about some features and flags of TCPdump comes from the course.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A Linux distro installed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WSL2 for Windows users.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is Tcpdump?
&lt;/h3&gt;

&lt;p&gt;Tcpdump is a Command-Line Interface or CLI to capture network traffic. It is used to identify malicious activity in a network and to identify issues in a network. We can capture network traffic and saved it to a &lt;strong&gt;packet capture (p-cap)&lt;/strong&gt;, a file containing data packets intercepted from an interface or network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identifying interfaces
&lt;/h3&gt;

&lt;p&gt;In Tcpdump we use the -D flag to list all the interfaces available for packet capture.&lt;/p&gt;

&lt;p&gt;We open a console or WSL console for Windows users, and execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo tcpdump -D
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we execute the command above, the console should show the following list:&lt;/p&gt;

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

&lt;p&gt;Now, we will capture the packets from the eth0 interface, but not all of them, just five packets of data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo tcpdump -i eth0 -v -c5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; option is used to select an interface, in this case, the &lt;code&gt;eth0&lt;/code&gt; interface.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-v&lt;/code&gt; means "Verbose" and it prints out detailed information about the packet data. And the &lt;code&gt;-c&lt;/code&gt; option allows us to control how many packets we want tcpdump to print out.&lt;/p&gt;

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

&lt;p&gt;In the example data at the start of the packet output, &lt;code&gt;tcpdump&lt;/code&gt; reported that it was listening on the &lt;code&gt;eth0&lt;/code&gt; interface, and it provided information on the link type and the capture size in bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the next line, the first field is the packet's timestamp, followed by the protocol type, IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;22:24:18.910372 IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The verbose option, &lt;code&gt;-v&lt;/code&gt;, has provided more details about the IP packet fields, such as TOS, TTL, offset, flags, internal protocol type (in this case, TCP (6)), and the length of the outer IP packet in bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(tos 0x0, ttl 64, id 5802, offset 0, flags [DF], proto TCP (6), length 134)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's see what it means in the snippet above:&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;tos 0x0&lt;/code&gt; represents the Type of Service (ToS) field in the IP header. In this case, 0x0 indicates the default ToS, with no specific priority or options set.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ttl 64&lt;/code&gt; &lt;strong&gt;,&lt;/strong&gt; time to Live (TTL) field in the IP header. It specifies how many hops (routers) a packet can traverse before being discarded. 64 is a commonly used value for local networks.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;id 5802&lt;/code&gt; this is the identification number of the TCP packet. It helps differentiate different packets belonging to the same TCP connection. &lt;code&gt;offset 0&lt;/code&gt; indicates that the data portion of the packet starts immediately after the header, with no header options present.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;flags [DF]&lt;/code&gt; shows the flags set in the TCP header. The &lt;code&gt;[DF]&lt;/code&gt; flag specifically indicates that the Don't Fragment (DF) flag is set, meaning the packet should not be fragmented if it doesn't fit the Maximum Transmission Unit (MTU) of any network device along the path. &lt;code&gt;proto TCP (6)&lt;/code&gt; specifies the transport layer protocol used, which in this case is TCP (Transmission Control Protocol). And &lt;code&gt;length 134&lt;/code&gt; is the total length of the TCP packet, including the header and data.&lt;/p&gt;

&lt;p&gt;In the next section, the data shows the systems that are communicating with each other:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7acb26dc1f44.5000 &amp;gt; nginx-us-east1-c.c.qwiklabs-terminal-vms-prod-00.internal.59788:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, &lt;code&gt;tcpdump&lt;/code&gt; will convert IP addresses into names, as in the screenshot. The name of my Linux virtual machine also included in the command prompt, appears here as the source for one packet and the destination for the second packet. In your live data, the name will be a different set of letters and numbers.&lt;/p&gt;

&lt;p&gt;The direction of the arrow (&amp;gt;) indicates the direction of the traffic flow in this packet. Each system name includes a suffix with the port number (.5000 in the code snippet) which is used by the source and the destination systems for this packet.&lt;/p&gt;

&lt;p&gt;The remaining data filters the header data for the inner TCP packet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flags [P.], cksum 0x5851 (incorrect &amp;gt; 0x30d3), seq 1080713945:1080714027, ack 62760789, win 501, options [nop,nop,TS val 1017464119 ecr 3001513453], length 82
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flags field identifies TCP flags. In this case, the P represents the push flag and the period indicates it's an ACK flag. This means the packet is pushing out data.&lt;/p&gt;

&lt;p&gt;The next field is the TCP checksum value, which is used for detecting errors in the data.&lt;/p&gt;

&lt;p&gt;This section also includes the sequence and acknowledgment numbers, the window size, and the length of the inner TCP packet in bytes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Capturing network traffic for further analysis
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo tcpdump -i eth0 -nn -c9 port 80 -w capture.pcap &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will run &lt;code&gt;tcpdump&lt;/code&gt; in the background with the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-i eth0&lt;/code&gt;: Capture data from the &lt;code&gt;eth0&lt;/code&gt; interface.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-nn&lt;/code&gt;: Do not attempt to resolve IP addresses or ports to names. This is best practice from a security perspective, as the lookup data may not be valid. It also prevents malicious actors from being alerted to an investigation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-c9&lt;/code&gt;: Capture 9 packets of data and then exit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;port 80&lt;/code&gt;: Filter only port 80 traffic. This is the default HTTP port.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-w capture.pcap&lt;/code&gt;: Save the captured data to the named file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;: This is an instruction to the Bash shell to run the command in the background.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we will use curl, to make a request and see what Tcpdump will print it out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl opensource.google.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Next, we make sure that Tcpdump captures the data packets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -l capture.pcap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;As we can see in the screenshot above, the packet capture was successful and it was written in the &lt;code&gt;capture.pcap&lt;/code&gt; file for further analysis.&lt;/p&gt;

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

&lt;p&gt;In this article, we just explore a little bit about Tcpdump, how to identify interfaces, print out packets data, capturing and reading packet data file. Tcpdump is an amazing tool and very useful for anyone interested in learning about networks or cybersecutiry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.coursera.org/learn/detection-and-response/supplement/sfQGY/overview-of-tcpdump" rel="noopener noreferrer"&gt;Overview of tcpdump&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.tcpdump.org/" rel="noopener noreferrer"&gt;TCPDUMP Documentation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tcpdump</category>
      <category>packet</category>
      <category>sniffer</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Adding A Logging Middleware To A Robyn App | Python</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Wed, 24 Jan 2024 00:16:08 +0000</pubDate>
      <link>https://dev.to/carlosm27/adding-a-logging-middleware-to-a-robyn-app-python-2h96</link>
      <guid>https://dev.to/carlosm27/adding-a-logging-middleware-to-a-robyn-app-python-2h96</guid>
      <description>&lt;p&gt;In this article we are going to build a logging middleware with Robyn. So, every time a request is made to the server, it will show the log in our console.&lt;/p&gt;

&lt;p&gt;The scoop of this article is not to create a robust and complete logging system, but a simple logging middleware for learning purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python 3 installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pip installed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating the Middleware
&lt;/h3&gt;

&lt;p&gt;We create a new python project as usual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
mkdir robbyn-logging
py -m venv venv

#Windows
cd venv/Scripts
./activate
pip install robyn

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

&lt;/div&gt;



&lt;p&gt;First, we are going to create a middleware class with the attributes of the request information and the response information.&lt;/p&gt;

&lt;p&gt;The request information will show the IP address, path, URL, HTTP method and request time. The response information, will show the status code, and the response type.&lt;/p&gt;

&lt;p&gt;Let's start with the middleware that shows the request information.&lt;/p&gt;

&lt;p&gt;app.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;robyn&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Robyn&lt;/span&gt;&lt;span class="p"&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;Response&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;robyn.logger&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Robyn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;request_info&lt;/span&gt;&lt;span class="p"&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;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ip_address&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;ip_addr&lt;/span&gt;
        &lt;span class="n"&gt;request_url&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;
        &lt;span class="n"&gt;request_path&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;
        &lt;span class="n"&gt;request_method&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;method&lt;/span&gt;
        &lt;span class="n"&gt;request_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ip_address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_method&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

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


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

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;app.py&lt;/code&gt; file, we import and create an instance of the Robyn class. Then, we create the &lt;code&gt;LoggingMiddleware&lt;/code&gt; class with the &lt;code&gt;request_info&lt;/code&gt; method, which returns the attributes of the request.&lt;/p&gt;

&lt;p&gt;Now, let's add a route to make a request. And use &lt;code&gt;before_request()&lt;/code&gt; method, which registers a function that runs before each request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="nd"&gt;@app.before_request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_request&lt;/span&gt;&lt;span class="p"&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;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received request: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&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;request&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8080&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 restart the server and make a request to &lt;code&gt;localhost:8080/&lt;/code&gt; to see the response in the console.&lt;/p&gt;

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

&lt;p&gt;Let's go with response data.&lt;/p&gt;

&lt;p&gt;We add another method to the &lt;code&gt;LoggingMiddleware&lt;/code&gt; class, but this one is with the response information.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="bp"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;
        &lt;span class="n"&gt;response_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_type&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response_type&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Next, use the &lt;code&gt;after_request()&lt;/code&gt; method to register the &lt;code&gt;log_response()&lt;/code&gt; function. This function will show the status code of the response, and the response type.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;after_request()&lt;/code&gt; method register a function that runs after a request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="nd"&gt;@app.after_request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sending response: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;response&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8080&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 restart the server and make a request to &lt;code&gt;localhost:8080/&lt;/code&gt; to see the response in the console.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Complete code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;robyn&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Robyn&lt;/span&gt;&lt;span class="p"&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;Response&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;robyn.logger&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Logger&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Robyn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;request_info&lt;/span&gt;&lt;span class="p"&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;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;ip_address&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;ip_addr&lt;/span&gt;
        &lt;span class="n"&gt;request_url&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;
        &lt;span class="n"&gt;request_path&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;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;
        &lt;span class="n"&gt;request_method&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;method&lt;/span&gt;
        &lt;span class="n"&gt;request_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ip_address&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_method&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_time&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

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

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;response_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;
        &lt;span class="n"&gt;response_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;response_type&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status_code&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response_type&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.before_request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_request&lt;/span&gt;&lt;span class="p"&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;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Received request: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&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;request&lt;/span&gt;

&lt;span class="nd"&gt;@app.after_request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sending response: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;LoggingMiddleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;response_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;response&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;h&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;My intention in this article was not to make a robust logging system or feature, but to show how to use the middleware feature of Robyn to create a simple logging middleware.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv" rel="noopener noreferrer"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://robyn.tech/documentation" rel="noopener noreferrer"&gt;Robyn Documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>robyn</category>
      <category>logging</category>
      <category>python</category>
      <category>backend</category>
    </item>
    <item>
      <title>2023 Retrospective: A Year Of Personal Growth</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Sun, 31 Dec 2023 23:19:23 +0000</pubDate>
      <link>https://dev.to/carlosm27/2023-retrospective-a-year-of-personal-growth-1k1m</link>
      <guid>https://dev.to/carlosm27/2023-retrospective-a-year-of-personal-growth-1k1m</guid>
      <description>&lt;p&gt;Started the 2023 with the goal of publishing at one article per week. It would be 52 articles. And I accomplished this goal.&lt;/p&gt;

&lt;p&gt;Every year we set goals and have expectations. I want a goal that would be under my control, because I didn't want to finished this year feeling like a could not accomplished anything. It was up to me to write and read every day to publish 52 by the end of the year. At first was easy because I was motivated, but it takes discipline and consistency to reach this goal. I struggled with the motivation, and lack of ideas. A lot of my articles were reused to write another articles, for example, I wrote many tutorials about Prometheus, I write an article for using it with Python, one for Go, and for Rust too.&lt;/p&gt;

&lt;p&gt;When doing this I felt like a cheater, the only though that comfort me was that probably one of that articles was useful for someone else.&lt;/p&gt;

&lt;p&gt;My consistency was put at a test when I found a job. I started to work at mid of November. And it was difficult to me to find time and energy to write an article, and because mostly what I write are tutorials, I have to find time to write code and test the programs for the articles.&lt;/p&gt;

&lt;p&gt;I learned a lot by writing articles in this year. I learned about how to use Prometheus, Grafana, Docker, RabbitMQ, Apache Pulsar, Ktor. I started to learned about cybersecurity, a topic I want to write about in the 2024.&lt;/p&gt;

&lt;p&gt;Also I continue to be more involved in contributing to Open Source projects. That was another goal that it takes discipline and consistency, but totally worth it. I'm very grateful to all the contributors and maintainers for helping me when I struggled with an issue, thanks to them I feel I did grow a lot this year as a developer. Especial thanks to the Robyn community for been so nice and welcoming.&lt;/p&gt;

&lt;p&gt;I didn't find a job as a software developer, but that is something that is not totally under my control. What is actually under my control is to continue to apply for jobs and continue learning, to grow as a software developer and as a person.&lt;/p&gt;

&lt;p&gt;I want to thanks to those who read my articles. Their likes, comments and feedback encourage me to continue to keep writing and help me to be a better writer each day.&lt;/p&gt;

&lt;p&gt;This was a short article, a brief summary about how my 2023 was. Thanks for reading this article.&lt;/p&gt;

&lt;p&gt;Happy New Year.&lt;/p&gt;

&lt;p&gt;Carlos Armando Marcano Vargas&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Adding A Rate Limiter Middleware To A Robyn Server | Python</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Sun, 31 Dec 2023 01:47:51 +0000</pubDate>
      <link>https://dev.to/carlosm27/adding-a-rate-limiter-middleware-to-a-robyn-server-python-5bck</link>
      <guid>https://dev.to/carlosm27/adding-a-rate-limiter-middleware-to-a-robyn-server-python-5bck</guid>
      <description>&lt;p&gt;In this article, we are going to learn how to add a rate limiter middleware to a Robyn server through &lt;code&gt;robyn-rate-limits&lt;/code&gt;, a plugin developed by &lt;a href="https://github.com/IdoKendo"&gt;IdoKendo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python 3 installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pip installed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rate Limiter
&lt;/h3&gt;

&lt;p&gt;According to &lt;a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/"&gt;this article&lt;/a&gt;, published by CloudFlare, a rate limiter is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain timeframe for instance, trying to log in to an account. Rate limiting can help stop certain kinds of malicious bot activity. It can also reduce strain on web servers.&lt;/p&gt;

&lt;p&gt;Rate limiting is often employed to stop bad bots from negatively impacting a website or application. Bot attacks that rate limiting can help mitigate include Brute force attacks, DoS and DDoS attacks and Web scraping.&lt;/p&gt;

&lt;p&gt;Rate limiting also protects against API overuse, which is not necessarily malicious or due to bot activity, but is important to prevent nonetheless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Robyn Rate Limiter Plugin
&lt;/h3&gt;

&lt;p&gt;According to &lt;a href="https://robyn.tech/documentation/plugins"&gt;the documentation&lt;/a&gt;, the rate limiter plugin enables you to implement rate limiting for your Robyn application's routes. It helps prevent abuse, and brute-force attacks and ensures fair usage of your resources.&lt;/p&gt;

&lt;p&gt;To use this plugin, we have to install it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; pip install robyn robyn-rate-limits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create a python file, &lt;code&gt;app.py&lt;/code&gt; to create a Robyn server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from robyn import Robyn, Robynfrom robyn_rate_limits import InMemoryStorefrom robyn_rate_limits import RateLimiterapp = Robyn( __file__ )limiter = RateLimiter(store=InMemoryStore, calls_limit=3, limit_ttl=100)@app.before_request()def middleware(request: Request): return limiter.handle_request(app, request)@app.get("/")def h(): return "Hello, World!"app.start(port=8080)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above (taken from the documentation), the &lt;code&gt;robyn-rate-limits&lt;/code&gt; plugin is used to enforce a rate limit of 3 requests per 100-seconds window for specific routes. If a client exceeds this limit, they will receive a "Too many requests" message.&lt;/p&gt;

&lt;p&gt;We start our server as usually do, by running &lt;code&gt;py app.py&lt;/code&gt; command in our console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P3cyzlLV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1703985257546/a050a589-c53a-4165-9f86-859f6aba3f5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P3cyzlLV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1703985257546/a050a589-c53a-4165-9f86-859f6aba3f5f.png" alt="" width="800" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we have to create a python script, to test the rate limiter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;test_limiter.py&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For this script we have to import the &lt;code&gt;requests&lt;/code&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requestsimport timedef test_rate_limiter(url, rate_limit, time_window): """Tests a rate limiter with the given parameters.""" start_time = time.time() allowed_requests = 0 blocked_requests = 0 while time.time() - start_time &amp;lt; time_window: try: response = requests.get(url) response.raise_for_status() # Raise an exception for non-200 status codes allowed_requests += 1 except requests.exceptions.RequestException as e: blocked_requests += 1 print(f"Request blocked: {e}") time.sleep(time_window / rate_limit) # Delay to respect the rate limit print(f"Allowed requests: {allowed_requests}") print(f"Blocked requests: {blocked_requests}")# Example usage:url = "http://localhost:8080/" # Replace with the actual URLrate_limit = 3 # Requests per secondtime_window = 5 # Secondstest_rate_limiter(url, rate_limit, time_window)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we define the test_rate_limiter() function with three parameters: &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;rate_limit&lt;/code&gt; and &lt;code&gt;time_window&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This program makes requests to an URL and tracks allowed and blocked requests. &lt;code&gt;response.raise_for_status()&lt;/code&gt; is if the status code in the response is different from a successful status code(200), it will raise an exception. And it will print the exception, in our case, it should be a "Too many request" message. &lt;code&gt;time.sleep&lt;/code&gt; pauses execution to respect the rate limit.&lt;/p&gt;

&lt;p&gt;We set the &lt;code&gt;url&lt;/code&gt; to the URL address where our Robyn server is receiving requests, &lt;code&gt;rate_limit&lt;/code&gt; to 3, and the &lt;code&gt;time_window&lt;/code&gt; to 5.&lt;/p&gt;

&lt;p&gt;We run this program, executing this command in another terminal: &lt;code&gt;py test_limiter.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, we should receive the following response in our console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9KjZy15N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1703985171527/e9270edd-ffdd-4b3e-aabf-8e3b011afff3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9KjZy15N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1703985171527/e9270edd-ffdd-4b3e-aabf-8e3b011afff3.png" alt="" width="800" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This is an easy to use and an useful plugin that allow us to build more resilient services with Robyn. Also, this plugin has a feature that uses Redis to store the calls, you can take a look to the &lt;a href="https://github.com/IdoKendo/robyn_rate_limits"&gt;plugin's repository&lt;/a&gt; for an example of how to use it.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/IdoKendo/robyn_rate_limits"&gt;robyn-rate-limits repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://robyn.tech/documentation/plugins"&gt;robyn-rate-limits documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/"&gt;What is rate limiting&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://builtin.com/software-engineering-perspectives/rate-limiter"&gt;What Is a Rate Limiter?&lt;/a&gt;&lt;/p&gt;

</description>
      <category>robyn</category>
      <category>python</category>
      <category>backend</category>
    </item>
    <item>
      <title>Building a Visitor Tracker With Robyn and React| Python</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Mon, 18 Dec 2023 01:23:08 +0000</pubDate>
      <link>https://dev.to/carlosm27/building-a-visitor-tracker-with-robyn-and-react-python-30im</link>
      <guid>https://dev.to/carlosm27/building-a-visitor-tracker-with-robyn-and-react-python-30im</guid>
      <description>&lt;p&gt;In this article, we are going to build a Visitor Tracker with Robyn and React.&lt;/p&gt;

&lt;p&gt;This is not a new project, I have built the same project using FastAPI and Rails. But, I want to build this app with Robyn to show features that I have not used in previous demos, like middleware, the request object, and the CORS. Also, to show the use of the response object.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Python installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic Python knowledge&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pip installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Postgres installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;NodeJs installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Basic React Knowledge&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Building the Visitor Tracker
&lt;/h3&gt;

&lt;p&gt;First, we create a directory for this application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir visitor_trackercd#Windows userspy -m venv venvcd venv/Scripts./activate#Linuxpython3 -m venv venvsource venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We install all the dependencies we need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install robyn psycopg2-binary python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the &lt;code&gt;visitor_tracker&lt;/code&gt; directory, we create a new file, &lt;a href="http://main.py"&gt;&lt;code&gt;init_db.py&lt;/code&gt;&lt;/a&gt;&lt;code&gt;.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;init_&lt;/strong&gt; &lt;a href="http://main.py"&gt;&lt;strong&gt;db.py&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import osimport psycopg2from dotenv import load_dotenvload_dotenv()PASSWORD = os.getenv('PASSWORD')def get_db_connection(): conn = psycopg2.connect( dbname = "logs_db", user = "postgres", password = PASSWORD ) return connconn = get_db_connection()cur = conn.cursor()cur.execute('DROP TABLE IF EXISTS logs;')cur.execute('CREATE TABLE logs (id serial PRIMARY KEY,' 'ip_address varchar (150) NOT NULL,' 'request_url varchar (50) NOT NULL,' 'request_path varchar (50) NOT NULL,' 'request_method varchar (50) NOT NULL,' 'request_time timestamp (50) NOT NULL,' 'date_added date DEFAULT CURRENT_TIMESTAMP);' )cur.execute('INSERT INTO logs (ip_address,' 'request_url,' 'request_path,' 'request_method,' 'request_time)' 'VALUES (%s, %s,%s, %s, %s)', ('127.0.0.1', 'http://localhost:8000', '/', "GET", "2023-06-25T16:03:24.722256", ))conn.commit()cur.close()conn.close()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we set up a database connection to store log data. First, we load the &lt;code&gt;.env&lt;/code&gt; file using &lt;code&gt;dotenv&lt;/code&gt; to get the database username and password variables. Then, we define a &lt;code&gt;get_db_connection()&lt;/code&gt; function that establishes a connection to a PostgreSQL database named logs_db. Then, the code calls that function to get a database connection and cursor.&lt;/p&gt;

&lt;p&gt;In this file, also the code drops the logs table if it exists and recreates it with the given schema - with columns to store IP address, request URL, port, path, method, time etc. It inserts sample log data into the table with its values. It commits the changes to the database and closes the cursor and connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://helpers.py"&gt;&lt;strong&gt;helpers.py&lt;/strong&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import collectionsdef to_dict(psycopg_tuple:tuple): tracker = collections.OrderedDict() tracker['id'] = psycopg_tuple[0] tracker["ip_address"] = psycopg_tuple[1] tracker["request_url"] = psycopg_tuple[2] tracker["request_path"] = psycopg_tuple[3] tracker["request_method"] = psycopg_tuple[4] tracker["request_time"] = psycopg_tuple[5].strftime("%d-%m-%Y, %H:%M:%S") return trackerdef list_dict(rows:list): row_list = [] for row in rows: book_dict = to_dict(row) row_list.append(book_dict) return row_list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file has two functions: &lt;code&gt;to_dict()&lt;/code&gt; and &lt;code&gt;list_dict()&lt;/code&gt;. The &lt;code&gt;to_dict()&lt;/code&gt; function converts a PostgreSQL tuple to a dictionary. The &lt;code&gt;list_dict()&lt;/code&gt; function converts a list of PostgreSQL tuples to a list of dictionaries.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;to_dict()&lt;/code&gt; function takes a PostgreSQL tuple as input and returns a dictionary. The dictionary contains the values of the tuple in the same order as the tuple. The &lt;code&gt;list_dict()&lt;/code&gt; function takes a list of PostgreSQL tuples as input and returns a list of dictionaries. The dictionaries are created using the &lt;code&gt;to_dict()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;controllers.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from init_db import get_db_connectionfrom helpers import to_dict,list_dictimport jsondef all_logs(): conn = get_db_connection() cur = conn.cursor() cur.execute('SELECT * FROM logs;') logs = list_dict(cur.fetchall()) cur.close() conn.close() return logsdef new_log(ip_address: str, request_url: str, request_path: str, request_method: str, request_time: str,): conn = get_db_connection() cur = conn.cursor() cur.execute('INSERT INTO logs (ip_address, request_url, request_path, request_method, request_time)' 'VALUES (%s, %s, %s,%s, %s) RETURNING *;',(ip_address, request_url, request_path, request_method, request_time, )) log = cur.fetchone()[:] log_dict = to_dict(log) conn.commit() cur.close() conn.close() return json.dumps(log_dict)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;all_logs()&lt;/code&gt; function gets all logs from the database and returns a list of dictionaries. Each dictionary contains information about a single log.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;new_log()&lt;/code&gt; function inserts a new log into the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;middleware.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from robyn.robyn import Requestfrom datetime import datetimeclass Tracker: def visitor_tracker(request: Request): ip_address = request.ip_addr request_url = request.url.host request_path = request.url.path request_method = request.method request_time = str(datetime.now()) return { "ip_address": ip_address, "request_url": request_url, "request_path": request_path, "request_method": request_method, "request_time": request_time, }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we create the Tracker class and define the &lt;code&gt;visitor_tracker&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;visitor_tracker&lt;/code&gt; function returns the attributes: &lt;code&gt;ip_address&lt;/code&gt;, &lt;code&gt;request_url&lt;/code&gt;, &lt;code&gt;request_path&lt;/code&gt;, &lt;code&gt;request_method&lt;/code&gt;, &lt;code&gt;request_time&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from robyn import Robyn, status_codesfrom robyn.robyn import Response, Headers, Requestfrom controllers import all_logs, new_logfrom robyn import loggerfrom middleware import Trackerimport jsonapp = Robyn( __file__ )@app.before_request()async def log_request(request: Request): tracker = Tracker.visitor_tracker(request=request) new_log(tracker["ip_address"], tracker["request_url"], tracker["request_path"], tracker["request_method"], tracker["request_time"]) logger.info(f"Received request: %s", tracker) return request@app.get("/")async def hello(): return Response(status_code=status_codes.HTTP_200_OK,headers=Headers({}), description="Hello, World!")@app.get("/visitors")async def hello(): logs = all_logs() return Response(status_code = status_codes.HTTP_200_OK, headers=Headers({}), description = json.dumps(logs))app.start(port=8000, host="0.0.0.0")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, in the &lt;code&gt;app.py&lt;/code&gt; file we define a middleware that intercepts all the requests using the &lt;code&gt;before_request()&lt;/code&gt; decorator. This middleware will store the request's IP address and other attributes, and it will create an instance of the Tracker class. Then it will store the data in the database.&lt;/p&gt;

&lt;p&gt;Also, here we define two routes: &lt;code&gt;"/"&lt;/code&gt; and &lt;code&gt;"/visitors"&lt;/code&gt;. In the hello() function we retrieve all the logs in the database or the visitors' information and show them using the Response class. The Response class requires &lt;code&gt;status_code&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; as positional arguments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a UI
&lt;/h2&gt;

&lt;p&gt;We will create a React app to visualize the tracking information in a table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding CORS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;app.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from robyn import Robyn, status_codes, ALLOW_CORSfrom robyn.robyn import Response, Headers, Requestfrom controllers import all_logs, new_logfrom robyn import loggerfrom middleware import Trackerimport jsonapp = Robyn( __file__ )ALLOW_CORS(app,["*"])...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Robyn, we import ALLOW_CORS and it needs the instance of Robyn, in this case, the &lt;code&gt;app&lt;/code&gt; variable and the origins. I use &lt;code&gt;["*"]&lt;/code&gt; to allow requests from any origin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Vite and React
&lt;/h3&gt;

&lt;p&gt;In our command line, we install Vite with a React-Typescript template. This command line will create a folder for the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#npmnpm create vite@latest table -- --template react-ts#yarnyarn create vite@latest table --template react-ts#pnpmpnpm create vite@latest table --template react-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all the packages are installed we run Vite with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We navigate to &lt;a href="http://localhost:5173"&gt;&lt;code&gt;localhost:5173&lt;/code&gt;&lt;/a&gt; and we should see the Vite and React homepage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UFKySHMR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1690158293818/39a8a978-93c9-47d5-ab37-49f8964ad67d.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UFKySHMR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1690158293818/39a8a978-93c9-47d5-ab37-49f8964ad67d.png%3Fauto%3Dcompress%2Cformat%26format%3Dwebp" alt="" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect} from "react";const url = "http://localhost:8000/visitors";interface Table { id: number, ip_address: string, request_url: string, request_path: string, request_method: string, request_time: string,}const Table: React.FC = () =&amp;gt; { const [data, setData] = useState&amp;lt;Table[]&amp;gt;([]); useEffect(() =&amp;gt; { fetch(url) .then(res =&amp;gt; res.json()) .then(data =&amp;gt; setData(data)); console.log(data); }, []); return ( &amp;lt;div&amp;gt; &amp;lt;h1&amp;gt;Logs&amp;lt;/h1&amp;gt; &amp;lt;table&amp;gt; &amp;lt;thead&amp;gt; &amp;lt;tr&amp;gt; &amp;lt;th&amp;gt;Id&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;IP Address&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Request URL&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Request Path&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Request Method&amp;lt;/th&amp;gt; &amp;lt;th&amp;gt;Request Time&amp;lt;/th&amp;gt; &amp;lt;/tr&amp;gt; &amp;lt;/thead&amp;gt; &amp;lt;tbody&amp;gt; {data.map((item, index) =&amp;gt; ( &amp;lt;tr key={index}&amp;gt; &amp;lt;td&amp;gt;{item.id}&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;{item.ip_address}&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;{item.request_url}&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;{item.request_path}&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;{item.request_method}&amp;lt;/td&amp;gt; &amp;lt;td&amp;gt;{item.request_time}&amp;lt;/td&amp;gt; &amp;lt;/tr&amp;gt; ))} &amp;lt;/tbody&amp;gt; &amp;lt;/table&amp;gt; &amp;lt;/div&amp;gt; );};export default Table;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start the Robyn server and the Vite server simultaneously. And then, we navigate to &lt;code&gt;localhost:5173&lt;/code&gt;, we should see the following page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rc79qT90--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1702339060210/00ca3693-4389-4ee7-87e0-4efbf13a1d2d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rc79qT90--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1702339060210/00ca3693-4389-4ee7-87e0-4efbf13a1d2d.png" alt="" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's add a &lt;code&gt;.css&lt;/code&gt; file to add style and see the data easily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index.css&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; color-scheme: light dark; color: rgba(255, 255, 255, 0.87); background-color: #242424; font-synthesis: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%;}table { border-collapse: collapse; margin-bottom: 1rem;}th,td { padding: 0.5rem; border: 1px solid #ccc;}th { text-align: left;}td { text-align: left;}.column-gap-10 { column-gap: 10px;}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mgVpjxH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1702339348485/37115e63-f268-480a-8fdb-387df12802de.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mgVpjxH_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1702339348485/37115e63-f268-480a-8fdb-387df12802de.png" alt="" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we learned how to build a visitor tracker with Robyn and React. We use Robyn's middleware to intercept and store the information of the requests. Also, we learned how to allow CORS in Robyn to make it possible to integrate with any frontend framework.&lt;/p&gt;

&lt;p&gt;The source code is &lt;a href="https://github.com/carlosm27/robyn_visitor_tracker"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://robyn.tech/"&gt;Robyn documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>robyn</category>
      <category>python</category>
      <category>typescript</category>
    </item>
    <item>
      <title>4 Open Source Packet Analyzers To Explore</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Sat, 09 Dec 2023 23:23:52 +0000</pubDate>
      <link>https://dev.to/carlosm27/4-open-source-packet-analyzers-to-explore-2j0l</link>
      <guid>https://dev.to/carlosm27/4-open-source-packet-analyzers-to-explore-2j0l</guid>
      <description>&lt;p&gt;Recently I started a course about cybersecurity and honestly, I find the topic very interesting. One of the modules of the course talks about packet analyzers to inspect the traffic in a network.&lt;/p&gt;

&lt;p&gt;So, I started to look for the most used Packet Analyzers in the market and found Tcpdump and Wireshark. But I wanted to find out if there are any others. I didn't find many alternatives, but two others like Sniffnet and Arkime.&lt;/p&gt;

&lt;p&gt;Also, I want to make clear that I'm new to this topic, and I'm not an expert or a cybersecurity professional.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a packet analyzer?
&lt;/h3&gt;

&lt;p&gt;As it says on this &lt;a href="https://en.wikipedia.org/wiki/Packet_analyzer"&gt;Wikipedia page&lt;/a&gt;, a packet analyzer, also known as a packet sniffer, protocol analyzer, or network analyzer, is a computer program or computer hardware such as a packet capture appliance that can analyze and log traffic that passes over a computer network or part of a network. Packet capture is the process of intercepting and logging traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tcpdump
&lt;/h3&gt;

&lt;p&gt;According to this &lt;a href="https://en.wikipedia.org/wiki/Tcpdump"&gt;Wikipedia page&lt;/a&gt;, tcpdump is a free data-network packet analyzer command line interface. That allows the user to display TCP/IP and other packets being transmitted or received over a network to which the computer is attached.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wireshark
&lt;/h3&gt;

&lt;p&gt;Wireshark is one of the world's most used network protocol analyzers. It lets you see what's happening on your network at a microscopic level. Sometimes it is the de facto standard across many industries and educational institutions.&lt;/p&gt;

&lt;p&gt;Wireshark is a project that started in 1998 and is continuously developing thanks to the contributions of networking experts across the globe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sniffnet
&lt;/h3&gt;

&lt;p&gt;Sniffnet is a completely free and open-source desktop application written in Rust using Iced. This a relatively new project that started in 2022.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W2zqsdea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/GyulyVGC/sniffnet/raw/main/resources/repository/pages/overview_page.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W2zqsdea--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/GyulyVGC/sniffnet/raw/main/resources/repository/pages/overview_page.png%3Fraw%3Dtrue" alt="" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Choose a network adapter for your PC to inspect&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select a set of filters to apply to the observed traffic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;View overall statistics about your Internet traffic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;View real-time charts about traffic intensity (bytes and packets per second, incoming and outgoing).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get details about domain names and network providers of the hosts you are exchanging traffic with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identify connections in your local network.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get information about the country of the remote hosts (IP geolocation).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save your favorite network hosts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set custom notifications to inform you when defined network events occur.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the style that fits you the most from 4 different available themes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inspect each of your network connections in real time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save complete textual reports with detailed information for each network connection:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Arkime
&lt;/h3&gt;

&lt;p&gt;Arkime is an open-source, large-scale, full packet capturing, indexing, and database system written in Javascript.&lt;/p&gt;

&lt;p&gt;As its README says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Arkime exposes APIs which allow for PCAP data and JSON formatted session data to be downloaded and consumed directly. Arkime stores and exports all packets in standard PCAP format, allowing you to also use your favorite PCAP ingesting tools, such as wireshark, during your analysis workflow.&lt;/p&gt;

&lt;p&gt;Arkime is built to be deployed across many systems and can scale to handle tens of gigabits/sec of traffic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LzOxPluk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/arkime/arkimeweb/raw/main/assets/sessions.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LzOxPluk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/arkime/arkimeweb/raw/main/assets/sessions.png" alt="" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;This was not an extensive list, and probably there a more open source packet analyzers. Still, the ones in this article are really good options to explore if you are interested in cybersecurity and forensics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/arkime/arkime"&gt;Arkime README&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tcpdump.org/index.html#documentation"&gt;TCPdump Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.wireshark.org/docs/"&gt;WireShark Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/GyulyVGC/sniffnet"&gt;Sniffnet README&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Tcpdump"&gt;tcpdump Wikipedia page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Packet_analyzer"&gt;Packet Analyzer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>packet</category>
      <category>analyzer</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>Exploring Format: The New Feature of Ruff | Python</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Tue, 28 Nov 2023 02:17:10 +0000</pubDate>
      <link>https://dev.to/carlosm27/exploring-format-the-new-feature-of-ruff-python-1jbp</link>
      <guid>https://dev.to/carlosm27/exploring-format-the-new-feature-of-ruff-python-1jbp</guid>
      <description>&lt;p&gt;Ruff introduces the &lt;code&gt;format&lt;/code&gt; feature for the v0.1.2. So right now Ruff, not only is a linter but also a formatter. In this short article, we are going to learn how to use the formatter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Python installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Ruff Formatter
&lt;/h3&gt;

&lt;p&gt;As the &lt;a href="https://docs.astral.sh/ruff/formatter/"&gt;documentation&lt;/a&gt; says, the Ruff formatter is an extremely fast Python code formatter designed as a drop-in replacement for Black, available as part of the &lt;code&gt;ruff&lt;/code&gt; CLI (as of Ruff v0.0.289).&lt;/p&gt;

&lt;p&gt;If you already have a previous version of Ruff installed, run this command in your console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install --upgrade ruff

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

&lt;/div&gt;



&lt;p&gt;For anyone who doesn't have Ruff installed, run this command:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;Now, to use the formatter feature in a Python file, we run the &lt;code&gt;ruff format /path/to/file.py&lt;/code&gt; command, and it will format the file.&lt;/p&gt;

&lt;p&gt;To format all the files in a directory, we run the &lt;code&gt;ruff format .&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;I have this code in a main.py file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;log = new_log(tracker["ip_address"], tracker["request_url"], tracker["request_port"],
                      tracker["request_path"], tracker["request_method"],
                      tracker["browser_type"], tracker["operating_system"],tracker["request_time"])

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ruff format main.py

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--heucXw09--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1700411922555/fb44ae49-e6bb-4fa0-96b2-e6dd782d97e5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--heucXw09--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1700411922555/fb44ae49-e6bb-4fa0-96b2-e6dd782d97e5.png" alt="" width="752" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The formatter is an incredible addition to Ruff, it helps us to write more readable and maintainable code. Now, we have a fast linter and formatter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.astral.sh/ruff/formatter/#preview-style"&gt;Ruff Formatter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ruff</category>
    </item>
    <item>
      <title>Building A Visitor Tracker with Rails</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Mon, 20 Nov 2023 23:20:28 +0000</pubDate>
      <link>https://dev.to/carlosm27/building-a-visitor-tracker-with-rails-4oe1</link>
      <guid>https://dev.to/carlosm27/building-a-visitor-tracker-with-rails-4oe1</guid>
      <description>&lt;p&gt;In this article, we are going to build a visitor tracker app using Rails. The app will intercept the requests and show information like the client's IP, URL, path, and request method.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Ruby installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rails installed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating a new Rails project.
&lt;/h3&gt;

&lt;p&gt;First, we need to create a new rails project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails new visitor-tracker

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating the model, database, controllers, and routes.
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails g resource tracker

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--obMDaMWX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699829832992/74a56e89-ca26-4bab-910a-64a3c5d0bde3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--obMDaMWX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699829832992/74a56e89-ca26-4bab-910a-64a3c5d0bde3.png" alt="" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We go to &lt;code&gt;db/migrate/&amp;lt;timestamp&amp;gt;create_trackers.rb&lt;/code&gt; to define the attributes of the &lt;code&gt;tracker&lt;/code&gt; table. There will be eight attributes: IP address, request URL, port, path, method, browser, OS, time and the service name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class CreateTrackers &amp;lt; ActiveRecord::Migration[7.0]
  def change
    create_table :trackers do |t|

      t.timestamps
      t.string :ip_address
      t.string :request_url
      t.integer :request_port
      t.string :request_path
      t.string :request_method
      t.string :browser_type
      t.datetime :request_time
    end
  end
end

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

&lt;/div&gt;



&lt;p&gt;We run the following command in our terminal, to migrate the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:migrate

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

&lt;/div&gt;



&lt;p&gt;We go to &lt;code&gt;app/controllers/tracker_controller.rb&lt;/code&gt; and create the functions to retrieve all the entries in the database, and to store the request info in the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Index&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  def index
        @trackers = Tracker.all
        render json: @trackers
    end

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Show&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; def show
        @tracker = Tracker.find(params[:id])
        render json: @tracker
    end

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   def create
        @tracker = Tracker.create(
            ip_address: params[:ip_address],
            request_url: params[:request_url],
            request_port: params[:request_port],
            request_path: params[:request_path],
            request_method: params[:request_method],
            browser_type: params[:browser_type],
            request_time: params[:request_time],

        )
        render json: @tracker
    end

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Complete&lt;/strong&gt; &lt;code&gt;trackers_controllers.rb&lt;/code&gt; &lt;strong&gt;file.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class TrackersController &amp;lt; ApplicationController
    def index
        @trackers = Tracker.all
        render json: @trackers
    end

    def show
        @tracker = Tracker.find(params[:id])
        render json: @tracker
    end

    def create
        @tracker = Tracker.create(
            ip_address: params[:ip_address],
            request_url: params[:request_url],
            request_port: params[:request_port],
            request_path: params[:request_path],
            request_method: params[:request_method],
            browser_type: params[:browser_type],
            request_time: params[:request_time],

        )
        render json: @tracker
    end    
end

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

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;db/seed.rb&lt;/code&gt; we created a record to display when we make the GET request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tracker_1 = Tracker.create(ip_address: "127.0.0.1", 
request_url: 'http://localhost:8000', request_port: 8000, request_path: "/",
request_method: "GET")

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

&lt;/div&gt;



&lt;p&gt;Then we run the following command to apply the changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rails db:seed

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

&lt;/div&gt;



&lt;p&gt;Now, let's start our server and see if it can show the data.&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;We navigate to &lt;code&gt;localhost:port/trackers&lt;/code&gt; and we should see the following response in the browser or HTTP client:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rGRhKoTK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699833595165/e8e99a10-7217-4c9f-bb0e-d8db897811f2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rGRhKoTK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699833595165/e8e99a10-7217-4c9f-bb0e-d8db897811f2.png" alt="" width="800" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a middleware
&lt;/h3&gt;

&lt;p&gt;We create a new folder, &lt;code&gt;lib/middleware&lt;/code&gt; and create the &lt;code&gt;lib/middleware/visitor_tracker&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module Middleware
    class VisitorTracker
      def initialize(app)
        @app = app

      end

      def call(env)
        request = ActionDispatch::Request.new(env)

        Tracker.create(ip_address: request.ip, 
        request_url: request.original_url, request_port: request.port, request_path: request.path,
        request_method: request.request_method)

        @app.call(env)
      end
    end  
end

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

&lt;/div&gt;



&lt;p&gt;Here, we create our middleware, create the &lt;code&gt;VisitorTracker&lt;/code&gt; class and create two methods, initialize and call.&lt;/p&gt;

&lt;p&gt;In the call method, we create an instance of the &lt;code&gt;ActionDispatch::Request&lt;/code&gt; class and then, create a new record in the database with the request information every time the middleware is executed.&lt;/p&gt;

&lt;p&gt;Then, we go to &lt;code&gt;config/application.rb&lt;/code&gt; file to declare our middleware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require_relative "boot"
require_relative "../lib/middleware/visitor_tracker"

require "rails/all"

Bundler.require(*Rails.groups)

module Visitor
  class Application &amp;lt; Rails::Application

    config.load_defaults 7.0
    config.middleware.use Middleware::VisitorTracker

  end
end

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

&lt;/div&gt;



&lt;p&gt;Here we use the &lt;code&gt;config.middleware.use&lt;/code&gt; method and pass the Middleware module and the &lt;code&gt;VisitorTracker&lt;/code&gt; class. Rails will execute this middleware just before your application is called.&lt;/p&gt;

&lt;p&gt;Now, to create a full-stack app, we need to go to &lt;code&gt;config/routes.rb&lt;/code&gt; and paste the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails.application.routes.draw do
  resources :trackers

  root "trackers#index"
  get "/trackers", to: "trackers#index"
end

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

&lt;/div&gt;



&lt;p&gt;Then, we need to go to the &lt;code&gt;app/controllers/trackers_controllers.rb&lt;/code&gt; file and remove the code &lt;code&gt;render json: @trackers&lt;/code&gt; from the index function. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def index
        @trackers = Tracker.all

end

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

&lt;/div&gt;



&lt;p&gt;Next, we go to the &lt;code&gt;app/views/trackers&lt;/code&gt; folder and create the &lt;code&gt;app/views/trackers/index.html.erb&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;Visitor Tracker&amp;lt;/title&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width,initial-scale=1"&amp;gt;
        &amp;lt;%= csrf_meta_tags %&amp;gt;
        &amp;lt;%= csp_meta_tag %&amp;gt;

        &amp;lt;%= stylesheet_link_tag "index", "data-turbo-track": "reload" %&amp;gt;
        &amp;lt;%= javascript_importmap_tags %&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;h1&amp;gt;Visitor Tracker&amp;lt;/h1&amp;gt;
    &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;IP Address &amp;lt;/th&amp;gt;              
            &amp;lt;th&amp;gt;Request URL &amp;lt;/th&amp;gt;                     
            &amp;lt;th&amp;gt;Request Port &amp;lt;/th&amp;gt;  
            &amp;lt;th&amp;gt;Request Path &amp;lt;/th&amp;gt;  
            &amp;lt;th&amp;gt;Request Method &amp;lt;/th&amp;gt;

        &amp;lt;/tr&amp;gt;
        &amp;lt;% @trackers.each do |tracker| %&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;%= tracker.ip_address %&amp;gt;&amp;lt;/td&amp;gt;         
            &amp;lt;td&amp;gt;&amp;lt;%= tracker.request_url %&amp;gt;&amp;lt;/td&amp;gt;     
            &amp;lt;td&amp;gt;&amp;lt;%= tracker.request_port %&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;%= tracker.request_path %&amp;gt;&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;%= tracker.request_method %&amp;gt;&amp;lt;/td&amp;gt;

        &amp;lt;/tr&amp;gt;
        &amp;lt;% end %&amp;gt;
    &amp;lt;/table&amp;gt;

&amp;lt;/html&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;We navigate to &lt;code&gt;localhost:port/&lt;/code&gt; and we should see the following response in the browser or HTTP client:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nZ93sbje--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699838535574/6ca0e3a1-04cd-4ec0-9ce7-d1c6aa7f3e1d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nZ93sbje--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699838535574/6ca0e3a1-04cd-4ec0-9ce7-d1c6aa7f3e1d.png" alt="" width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we create the &lt;code&gt;app/assets/stylesheets/index.css&lt;/code&gt; file and paste the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
table {
    border-collapse: collapse;
    margin-bottom: 1rem;
  }

  th,
  td {
    padding: 0.5rem;
    border: 1px solid #ccc;
  }

  th {
    text-align: left;
  }

  td {
    text-align: left;
  }

  .column-gap-10 {
    column-gap: 10px;
  }

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IireC3Sf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1700427730809/4dc708ed-3a8e-4cce-a7a7-f77559ba4467.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IireC3Sf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1700427730809/4dc708ed-3a8e-4cce-a7a7-f77559ba4467.png" alt="" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we learned about how to build a visitor tracker with Rails and how to create custom middleware. With this app, we can see the activity of our applications.&lt;/p&gt;

&lt;p&gt;The source code is &lt;a href="https://github.com/carlosm27/rails-visitor-tracker"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/getting_started.html"&gt;Getting Started with Rails&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/swlh/using-rails-and-html-to-view-sort-and-search-tables-fbf8a0543558"&gt;Using Rails and HTML to View, Sort, and Search Tables.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.writesoftwarewell.com/rails-middleware-what-why-and-how/#lets-add-custom-middleware-in-rails"&gt;Rails Middleware: (Almost) Everything You Need to Know&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>Go RabbitMQ: Integrating RabbitMQ into Your Go Applications</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Tue, 14 Nov 2023 00:02:49 +0000</pubDate>
      <link>https://dev.to/carlosm27/go-rabbitmq-integrating-rabbitmq-into-your-go-applications-5gib</link>
      <guid>https://dev.to/carlosm27/go-rabbitmq-integrating-rabbitmq-into-your-go-applications-5gib</guid>
      <description>&lt;p&gt;In this article, we are going to learn how to integrate RabbitMQ with a Go server.&lt;/p&gt;

&lt;p&gt;We are going to the &lt;code&gt;net/http&lt;/code&gt; package in this article. We are going to build two servers, one that will publish messages to the RabbitMQ queue and another to consume from it.&lt;/p&gt;

&lt;p&gt;Also, we are just using the code examples from the &lt;a href="https://www.rabbitmq.com/tutorials/tutorial-one-go.html"&gt;RabbitMQ Go tutorial&lt;/a&gt; and integrating them into a Go server.&lt;/p&gt;

&lt;p&gt;I will use the same disclaimer from the RabbitMQ tutorial:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please keep in mind that this and other tutorials are, well, tutorials. They demonstrate one new concept at a time and may intentionally oversimplify some things and leave out others. For example, topics such as connection management, error handling, connection recovery, concurrency and metric collection are largely omitted for the sake of brevity. Such simplified code should not be considered production ready.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go installed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RabbitMQ installed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Publisher
&lt;/h3&gt;

&lt;p&gt;Here, we are going to develop a server that will publish messages to the queue. First, we have to install the RabbitMQ Go client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get github.com/rabbitmq/amqp091-go

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

&lt;/div&gt;



&lt;p&gt;Then, we create the &lt;code&gt;main.go&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

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

import (
    "context"
    "log"
    "log/slog"
    "net/http"
    "time"

    amqp "github.com/rabbitmq/amqp091-go"
)

func main() {

    mux := http.NewServeMux()
    port := ":8000"

    mux.Handle("/", http.HandlerFunc(homeHandler))

    slog.Info("Listening on ", "port", port)

    err := http.ListenAndServe(port, mux)
    if err != nil {
        slog.Warn("Problem starting the server", "error", err)
    }

}

func homeHandler(w http.ResponseWriter, r *http.Request) {

    w.Write([]byte("Hello this is a home page"))
    rabbitmqConnection()

}

func failOnError(err error, msg string) {
    if err != nil {
        log.Panicf("%s: %s", msg, err)
    }
}

func rabbitmqConnection() {
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()
    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "hello", // name
        false, // durable
        false, // delete when unused
        false, // exclusive
        false, // no-wait
        nil, // arguments
    )
    failOnError(err, "Failed to declare a queue")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    body := "Hello World!"
    err = ch.PublishWithContext(ctx,
        "", // exchange
        q.Name, // routing key
        false, // mandatory
        false, // immediate
        amqp.Publishing{
            ContentType: "text/plain",
            Body: []byte(body),
        })
    failOnError(err, "Failed to publish a message")
    log.Printf(" [x] Sent %s\n", body)

}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Here we&lt;/strong&gt; create a new HTTP multiplexer (&lt;code&gt;mux&lt;/code&gt;) and sets the listening port to &lt;code&gt;8000&lt;/code&gt;. It defines a route handler (&lt;code&gt;homeHandler&lt;/code&gt;) for the root path &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;homeHandler()&lt;/code&gt; function writes a simple message ("Hello this is a home page") to the response writer and then calls the &lt;code&gt;rabbitmqConnection&lt;/code&gt; function to connect to RabbitMQ and publish a message. So, every time the route "/" is hit, it will publish the message defined in the &lt;code&gt;rabbitmqConnection()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;failOnError()&lt;/code&gt; function checks for an error and logs a panic message if the error is not nil.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;rabbitmqConnection()&lt;/code&gt; function establishes a connection to the RabbitMQ server using the &lt;code&gt;amqp.Dial&lt;/code&gt; function. It creates a channel (&lt;code&gt;ch&lt;/code&gt;) and declares a queue named "hello". Then, it publishes a message with the body "Hello World!" to the "hello" queue.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main()&lt;/code&gt; function creates the web server using the &lt;code&gt;http.ListenAndServe&lt;/code&gt; function and starts listening on port &lt;code&gt;8000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZpPMLMx_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699670706697/999fb2ee-c95f-4f34-8eef-ecc406f56d25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZpPMLMx_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699670706697/999fb2ee-c95f-4f34-8eef-ecc406f56d25.png" alt="" width="608" height="136"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;receiver.go&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, we create a program that consumes the messages from the queue and prints them in the console.&lt;/p&gt;

&lt;p&gt;This is the same code from the RabbitMQ Go tutorial.&lt;br&gt;
&lt;/p&gt;

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

import (
    "log"

    amqp "github.com/rabbitmq/amqp091-go"
)

func failOnError(err error, msg string) {
    if err != nil {
        log.Panicf("%s: %s", msg, err)
    }
}

func main() {
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "hello", // name
        false, // durable
        false, // delete when unused
        false, // exclusive
        false, // no-wait
        nil, // arguments
    )
    failOnError(err, "Failed to declare a queue")
    msgs, err := ch.Consume(
        q.Name, // queue
        "", // consumer
        true, // auto-ack
        false, // exclusive
        false, // no-local
        false, // no-wait
        nil, // args
    )
    failOnError(err, "Failed to register a consumer")

    var forever chan struct{}

    go func() {
        for d := range msgs {
            log.Printf("Received a message: %s", d.Body)
        }
    }()

    log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
    &amp;lt;-forever
}

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

&lt;/div&gt;



&lt;p&gt;Here we declared the name of the queue("hello") from this program will consume the messages, using the &lt;code&gt;ch.QueueDeclare&lt;/code&gt; method. The parameters specify that the queue is not durable (won't persist across server restarts), not exclusive (can be consumed by multiple consumers), and not auto-delete (won't be deleted when the last consumer unsubscribes).&lt;/p&gt;

&lt;p&gt;The code registers a consumer using the &lt;code&gt;ch.Consume&lt;/code&gt; method. It specifies the queue name (&lt;a href="http://q.Name"&gt;&lt;code&gt;q.Name&lt;/code&gt;&lt;/a&gt;), an empty consumer tag (allowing RabbitMQ to generate a unique tag), &lt;code&gt;true&lt;/code&gt; for automatic acknowledgment (messages are automatically acknowledged after processing), and &lt;code&gt;false&lt;/code&gt; for other parameters. The method returns a channel (&lt;code&gt;msgs&lt;/code&gt;) that receive incoming messages.&lt;/p&gt;

&lt;p&gt;A goroutine is started to consume messages from the &lt;code&gt;msgs&lt;/code&gt; channel. It iterates over the messages, printing their bodies to the console.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main&lt;/code&gt; function logs a message indicating that it's waiting for messages and starts a &lt;code&gt;forever&lt;/code&gt; channel to block the main thread. It prevents the program from exiting immediately and allows the consumer loop to continue running.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;-forever&lt;/code&gt; statement waits for a signal to be sent to the &lt;code&gt;forever&lt;/code&gt; channel, which will happen when the user presses CTRL+C. This allows the program to exit gracefully when interrupted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TBa0N8zu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699670755532/ab75677d-f840-492b-b52c-50b8b1eff680.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TBa0N8zu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699670755532/ab75677d-f840-492b-b52c-50b8b1eff680.png" alt="" width="800" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Consumer
&lt;/h3&gt;

&lt;p&gt;Here, we create the server that consumes from the queue. We have to install the RabbitMQ client too.&lt;br&gt;
&lt;/p&gt;

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

import (
    "log"
    "log/slog"
    "net/http"

    amqp "github.com/rabbitmq/amqp091-go"
)

func failOnError(err error, msg string) {
    if err != nil {
        log.Panicf("%s: %s", msg, err)
    }
}

func main() {
    mux := http.NewServeMux()
    port := ":8080"

    mux.Handle("/", http.HandlerFunc(homeHandler))

    slog.Info("Listening on ", "port", port)

    err := http.ListenAndServe(port, mux)
    if err != nil {
        slog.Warn("Problem starting the server", "error", err)
    }

}

func homeHandler(w http.ResponseWriter, r *http.Request) {

    body := rabbitmqConsumer()
    w.Write([]byte(body))

}

func rabbitmqConsumer() string {
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "hello", // name
        false, // durable
        false, // delete when unused
        false, // exclusive
        false, // no-wait
        nil, // arguments
    )
    failOnError(err, "Failed to declare a queue")
    msgs, err := ch.Consume(
        q.Name, // queue
        "", // consumer
        true, // auto-ack
        false, // exclusive
        false, // no-local
        false, // no-wait
        nil, // args
    )
    failOnError(err, "Failed to register a consumer")

    forever := make(chan string)
    go func() {
        for d := range msgs {
            log.Printf("Received a message: %s", d.Body)
            forever &amp;lt;- string(d.Body)

        }
    }()

    bodyMessage := &amp;lt;-forever

    return bodyMessage

}

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

&lt;/div&gt;



&lt;p&gt;Here, we just copy the same code from the &lt;code&gt;receiver.go&lt;/code&gt; and wrap it in the &lt;code&gt;rabbitmqConnection()&lt;/code&gt; function. This function will be called everytime the route &lt;code&gt;"/"&lt;/code&gt; is called and will show the body of the message in the browser or HTTP Client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ipqP-Iif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699672189810/51da1ac4-6d14-4fda-9a6a-806a8b378750.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ipqP-Iif--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699672189810/51da1ac4-6d14-4fda-9a6a-806a8b378750.png" alt="" width="648" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1uNuAsep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699672203097/24d13511-d403-47bf-bdc5-42df9000d256.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1uNuAsep--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1699672203097/24d13511-d403-47bf-bdc5-42df9000d256.png" alt="" width="649" height="107"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we learn how to integrate RabbitMQ into a Go server. RabbitMQ is a powerful messaging broker that can be seamlessly integrated with Go applications to handle asynchronous message communication.&lt;/p&gt;

&lt;p&gt;By utilizing RabbitMQ, Go servers can effectively decouple components, enhance scalability, and ensure reliable message delivery.&lt;/p&gt;

&lt;p&gt;The Publisher's source code is &lt;a href="https://github.com/carlosm27/rabbitmq-demo"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Consumer's source code is &lt;a href="https://github.com/carlosm27/goReceiver"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.rabbitmq.com/tutorials/tutorial-one-go.html"&gt;RabbitMQ Go tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rabbitmq/amqp091-go"&gt;AMQP091 README&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>rabbitmq</category>
    </item>
    <item>
      <title>Building a Simple Rate Limiter Middleware in Go using Gin</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Mon, 06 Nov 2023 15:52:44 +0000</pubDate>
      <link>https://dev.to/carlosm27/building-a-simple-rate-limiter-middleware-in-go-using-gin-51np</link>
      <guid>https://dev.to/carlosm27/building-a-simple-rate-limiter-middleware-in-go-using-gin-51np</guid>
      <description>&lt;p&gt;In this article, we are going to build a Rate Limiter middleware for a Gin server.&lt;/p&gt;

&lt;p&gt;We will build a very simple rate limiter, one that stops accepting requests when these exceed the limit in a certain time frame. We Will not create a rate limiter that keeps track of the IP Addresses and stops them from making too many requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate Limiter
&lt;/h3&gt;

&lt;p&gt;According to &lt;a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/"&gt;this article&lt;/a&gt;, published by CloudFlare, a rate limiter is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain timeframe for instance, trying to log in to an account. Rate limiting can help stop certain kinds of malicious bot activity. It can also reduce strain on web servers.&lt;/p&gt;

&lt;p&gt;Rate limiting is often employed to stop bad bots from negatively impacting a website or application. Bot attacks that rate limiting can help mitigate include Brute force attacks, DoS and DDoS attacks and Web scraping.&lt;/p&gt;

&lt;p&gt;Rate limiting also protects against API overuse, which is not necessarily malicious or due to bot activity, but is important to prevent nonetheless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rate Limiter Algorithms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fixed window algorithm:&lt;/strong&gt; This algorithm maintains a counter of the number of requests made within a fixed time window, such as one minute. If the counter exceeds the rate limit, subsequent requests are rejected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sliding window algorithm:&lt;/strong&gt; This algorithm is similar to the fixed window algorithm, but the time window slides forward as new requests are received. This means that requests that were made earlier in the window may still be counted against the rate limit, even if they are no longer within the current window.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leaky bucket algorithm:&lt;/strong&gt; This algorithm maintains a bucket that fills up at a constant rate. When a request is received, it is allowed to pass through the bucket if there is space available. If the bucket is full, the request is rejected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Token bucket algorithm:&lt;/strong&gt; This algorithm is similar to the leaky bucket algorithm, but instead of a bucket, it maintains a counter of tokens. Tokens are added to the counter at a constant rate, and requests are allowed to pass through if there is at least one token available. If the counter is empty, requests are rejected.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Simple Rate Limiter
&lt;/h3&gt;

&lt;p&gt;We created a new Go project, RateLimiter. And installed Gin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get -u github.com/gin-gonic/gin

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

&lt;/div&gt;



&lt;p&gt;For this rate limiter we are going to use the rate package. Which implements the token bucket algorithm.&lt;/p&gt;

&lt;p&gt;And in the &lt;code&gt;main.go&lt;/code&gt; file, we start creating a handler that will be used to limit the number of requests made to the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;main.go&lt;/strong&gt;&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;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;"github.com/gin-gonic/gin"&lt;/span&gt;

    &lt;span class="s"&gt;"golang.org/x/time/rate"&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;RateLimiter&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="n"&gt;limiter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewLimiter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;limiter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Allow&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="k"&gt;else&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;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusTooManyRequests&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;H&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Limite exceed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;This is the &lt;code&gt;RateLimiter()&lt;/code&gt; function, we create a NewLimiter instance that allows up to 4 requests per second. According to the &lt;a href="https://pkg.go.dev/golang.org/x/time/rate#pkg-functions"&gt;documentation&lt;/a&gt;, the NewLimiter function returns a new Limiter, &lt;code&gt;func NewLimiter(r Limit, b int) *Limiter&lt;/code&gt; and a Limiter controls how frequently events are allowed to happen. It implements a "token bucket" of size &lt;code&gt;b&lt;/code&gt;, initially full and refilled at the rate of &lt;code&gt;r&lt;/code&gt; tokens per second.&lt;/p&gt;

&lt;p&gt;The zero value is a valid Limiter, but it will reject all events. Use NewLimiter to create non-zero Limiters.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Allow()&lt;/code&gt; method of the rate limiter returns a boolean value indicating whether the request is allowed. If the request is allowed, the &lt;code&gt;Next()&lt;/code&gt; method of the &lt;code&gt;gin.Context&lt;/code&gt; object is called to continue processing the request. Otherwise, the &lt;code&gt;JSON()&lt;/code&gt; method of the &lt;code&gt;gin.Context&lt;/code&gt; object is called to send a response with the status code &lt;code&gt;429&lt;/code&gt; (Too Many Requests).&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;r&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;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RateLimiter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;r&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;"/"&lt;/span&gt;&lt;span class="p"&gt;,&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&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;H&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"pong"&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;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")&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 Gin instance and use the &lt;code&gt;Use()&lt;/code&gt; function to register the &lt;code&gt;RateLimiter()&lt;/code&gt; middleware.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;RateLimiter()&lt;/code&gt; handler is called every time a request is made to the server.&lt;/p&gt;

&lt;p&gt;In our command line, we run this file using &lt;code&gt;go run main.go&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8QIoBLPE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1688866852782/649b3cc0-c74a-4739-a0b0-9642a97eeff2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8QIoBLPE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1688866852782/649b3cc0-c74a-4739-a0b0-9642a97eeff2.png" alt="" width="800" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test this rate limiter, we created another project, &lt;code&gt;TestLimiter&lt;/code&gt;. And use the following code in its &lt;code&gt;main.go&lt;/code&gt; file.&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;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;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io/ioutil"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&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;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;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&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;Timeout&lt;/span&gt;&lt;span class="o"&gt;:&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="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&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;Second&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;resp&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;c&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;"http://localhost:8080/"&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;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 %s"&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;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;"Count is: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&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="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&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;body&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;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&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;"Body : %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In this file, we define the &lt;code&gt;c&lt;/code&gt; variable, which is a &lt;code&gt;http.Client&lt;/code&gt; object that is used to make HTTP requests. The &lt;code&gt;limit&lt;/code&gt; variable is an integer variable that specifies the number of times the &lt;code&gt;Get()&lt;/code&gt; method will be called.&lt;/p&gt;

&lt;p&gt;Then we created a loop that iterates &lt;code&gt;limit&lt;/code&gt; times. In each iteration of the loop, the code makes a &lt;code&gt;Get()&lt;/code&gt; request to the URL &lt;a href="http://localhost:8080/"&gt;&lt;code&gt;http://localhost:8080/&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;Get()&lt;/code&gt; method returns an &lt;code&gt;*http.Response&lt;/code&gt; object, which contains the response from the server.&lt;/p&gt;

&lt;p&gt;The code then checks the &lt;code&gt;err&lt;/code&gt; variable to see if there was an error in making the request. If there is an error, the code prints the error message and the current value of the &lt;code&gt;i&lt;/code&gt; variable. Otherwise, the code reads the body of the response and prints it to the console.&lt;/p&gt;

&lt;p&gt;We run this file, using &lt;code&gt;go run main.go&lt;/code&gt; in another terminal.&lt;/p&gt;

&lt;p&gt;The Rate Limiter terminal should show these messages:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OYkxYj5m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1688866983625/86117957-a105-45d2-bdc7-81160945aef3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OYkxYj5m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1688866983625/86117957-a105-45d2-bdc7-81160945aef3.png" alt="" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the &lt;code&gt;TestLimiter&lt;/code&gt; terminal should show these messages:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lt3NqFod--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1688867028465/460f53c9-79b7-483c-a699-da66484bb2d5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lt3NqFod--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1688867028465/460f53c9-79b7-483c-a699-da66484bb2d5.png" alt="" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we built a rate limiter for a Gin server. Building a rate limiter for your Gin server is an important step in protecting your application from abuse. By limiting the number of requests that can be made to your server, you can help improve the performance and reliability of your application for all users.&lt;/p&gt;

&lt;p&gt;The source code is &lt;a href="https://github.com/carlosm27/rateLimiter"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.cloudflare.com/learning/bots/what-is-rate-limiting/"&gt;What is rate limiting&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://builtin.com/software-engineering-perspectives/rate-limiter"&gt;What Is a Rate Limiter?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.logrocket.com/rate-limiting-go-application/"&gt;Rate limiting your Go application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pkg.go.dev/golang.org/x/time/rate"&gt;Rate package documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>middleware</category>
      <category>rate</category>
      <category>limiter</category>
    </item>
    <item>
      <title>Seven Alternatives To FastAPI To Explore and Contribute To | Python</title>
      <dc:creator>Carlos Armando Marcano Vargas</dc:creator>
      <pubDate>Mon, 30 Oct 2023 23:56:28 +0000</pubDate>
      <link>https://dev.to/carlosm27/seven-alternatives-to-fastapi-to-explore-and-contribute-to-python-3884</link>
      <guid>https://dev.to/carlosm27/seven-alternatives-to-fastapi-to-explore-and-contribute-to-python-3884</guid>
      <description>&lt;p&gt;First of all, there is nothing wrong with FastAPI. FastAPI is a good web framework. I have used it many times in my hobby projects and works fine.&lt;/p&gt;

&lt;p&gt;This article is about the alternatives of FastAPI for anyone who wants to try another web framework, for the sake of exploring a new software. To anyone who finds joy in trying new things or different things. Also, if you want to contribute to Open Source Software and you are interested in web frameworks.&lt;/p&gt;

&lt;p&gt;Also, this is a biased article, because in the list below are seven Python web frameworks that we can use as FastAPI alternatives, and I'm excluding from this list Flask and Django. Not because they are bad, but because I think they are well known.&lt;/p&gt;

&lt;h3&gt;
  
  
  Robyn
&lt;/h3&gt;

&lt;p&gt;Robyn is a fast async Python web framework coupled with a web server written in Rust.&lt;/p&gt;

&lt;p&gt;Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Authentication and Authorization&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Middlewares&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CORS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSockets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Subrouters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Views&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Templating with Jinja2&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;We create the &lt;code&gt;app.py&lt;/code&gt; file and paste the following code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from robyn import Robyn

app = Robyn( __file__ )

@app.get("/")
async def h(request):
    return "Hello, world!"

app.start(port=8080)

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 app.py

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://beta.robyn.tech/documentation"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sparckles/Robyn"&gt;Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discord.gg/rkERZ5eNU8"&gt;Discord&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Blacksheep
&lt;/h3&gt;

&lt;p&gt;BlackSheep is an asynchronous web framework for building event-based web applications with Python. It is inspired by Flask, ASP.NET Core, and the work of Yury Selivanov.&lt;/p&gt;

&lt;p&gt;It has a similar syntax as FastAPI and also it could be used with Uvicorn. As the documentation says, it has been tested to use Hypercorn as an ASGI HTTP server too.&lt;/p&gt;

&lt;p&gt;According to its documentation, BlackSheep offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A rich code API, based on dependency injection and inspired by Flask and ASP.NET Core&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A typing-friendly codebase, which enables a comfortable development experience thanks to hints when coding with IDEs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Built-in generation of OpenAPI Documentation, supporting version 3, YAML, and JSON&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A cross-platform framework, using the most modern versions of Python&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Good performance&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install blacksheep uvicorn

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

&lt;/div&gt;



&lt;p&gt;Create the &lt;code&gt;server.py&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from datetime import datetime
from blacksheep import Application

app = Application()

@app.route("/")
def home():
    return f"Hello, World! {datetime.utcnow().isoformat()}"

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uvicorn server:app --port 44777 --reload

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.neoteroi.dev/blacksheep/"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Neoteroi/BlackSheep"&gt;Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.gitter.im/#/room/#Neoteroi_BlackSheep:gitter.im"&gt;Gitter&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  LiteStar
&lt;/h3&gt;

&lt;p&gt;Litestar is a powerful, flexible, highly performant, and opinionated ASGI framework.&lt;/p&gt;

&lt;p&gt;The Litestar framework supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Plugins&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ships with dependency injection&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Security primitives&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OpenAPI schema generation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MessagePack, middlewares&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A great CLI experience&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install litestar[standard]

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

&lt;/div&gt;



&lt;p&gt;We create the &lt;code&gt;app.py&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from litestar import Litestar, get

@get("/")
async def index() -&amp;gt; str:
    return "Hello, world!"

@get("/books/{book_id:int}")
async def get_book(book_id: int) -&amp;gt; dict[str, int]:
    return {"book_id": book_id}

app = Litestar([index, get_book])

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;litestar run
# Or you can run Uvicorn directly:
uvicorn app:app --reload

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.litestar.dev/2/"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/litestar-org/litestar"&gt;Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discord.gg/X3FJqy8d2j"&gt;Discord&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Sanic
&lt;/h3&gt;

&lt;p&gt;Sanic is a Python 3.8+ web server and web framework thats written to go fast. It allows the usage of the async/await syntax added in Python 3.5, which makes your code non-blocking and speedy.&lt;/p&gt;

&lt;p&gt;Sanic intentionally aims for a clean and unopinionated feature list. The project does not want to require you to build your application in a certain way and tries to avoid prescribing specific development patterns. Several third-party plugins are built and maintained by the community to add additional features that do not otherwise meet the requirements of the core repository.&lt;/p&gt;

&lt;p&gt;However, to help API developers, the Sanic organization maintains an official plugin called Sanic Extensions to provide all sorts of goodies, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;OpenAPI documentation with Redoc and/or Swagger&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CORS protection&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dependency injection into route handlers&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Request query arguments and body input &lt;strong&gt;validation&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Auto create &lt;code&gt;HEAD&lt;/code&gt;, &lt;code&gt;OPTIONS&lt;/code&gt;, and &lt;code&gt;TRACE&lt;/code&gt; endpoints&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Predefined, endpoint-specific response serializers&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;We create the &lt;code&gt;server.py&lt;/code&gt; file and paste the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from sanic import Sanic
from sanic.response import text

app = Sanic("MyHelloWorldApp")

@app.get("/")
async def hello_world(request):
    return text("Hello, world.")

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command in the console:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://sanic.dev/en/guide/getting-started.html"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sanic-org/sanic"&gt;Repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discord.com/invite/FARQzAEMAA"&gt;Discord&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Falcon
&lt;/h3&gt;

&lt;p&gt;Falcon is a reliable, high-performance Python web framework for building large-scale app backends and microservices. It works with any WSGI server, encourages the REST architectural style, and tries to do as little as possible while remaining highly effective.&lt;/p&gt;

&lt;p&gt;Falcon tries to do as little as possible while remaining highly effective.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ASGI, WSGI, and WebSocket support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Native &lt;code&gt;asyncio&lt;/code&gt; support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No reliance on magic globals for routing and state management&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stable interfaces with an emphasis on backwards-compatibility&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simple API modeling through centralized RESTful routing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Highly-optimized, extensible code base&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easy access to headers and bodies through request and response objects&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DRY request processing via middleware components and hooks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Strict adherence to RFCs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Idiomatic HTTP error responses&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Straightforward exception handling&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Snappy testing with WSGI/ASGI helpers and mocks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CPython 3.5+ and PyPy 3.5+ support&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install falcon, uvicorn

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

&lt;/div&gt;



&lt;p&gt;We create a &lt;code&gt;server.py&lt;/code&gt; file and paste the content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import falcon
import falcon.asgi

# Falcon follows the REST architectural style, meaning (among
# other things) that you think in terms of resources and state
# transitions, which map to HTTP verbs.
class ThingsResource:
    async def on_get(self, req, resp):
        """Handles GET requests"""
        resp.status = falcon.HTTP_200 # This is the default status
        resp.content_type = falcon.MEDIA_TEXT # Default is JSON, so override
        resp.text = (
            '\nTwo things awe me most, the starry sky '
            'above me and the moral law within me.\n'
            '\n'
            ' ~ Immanuel Kant\n\n'
        )

# falcon.asgi.App instances are callable ASGI apps...
# in larger applications the app is created in a separate file
app = falcon.asgi.App()

# Resources are represented by long-lived class instances
things = ThingsResource()

# things will handle all requests to the '/things' URL path
app.add_route('/things', things)

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uvicorn server:app

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://falcon.readthedocs.io/en/stable/user/index.html"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/falconry/falcon"&gt;Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Emmett
&lt;/h3&gt;

&lt;p&gt;Emmett is a full-stack Python web framework designed with simplicity in mind.&lt;/p&gt;

&lt;p&gt;Emmett aims to be understandable, easy to learn and to be used, so you can focus completely on your product's features.&lt;/p&gt;

&lt;p&gt;Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Routing system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Templating system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Websockets&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Validations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Forms generations&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sesions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authorization system&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Caching&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ORM&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;We create an &lt;code&gt;app.py&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from emmett import App

app = App( __name__ )

@app.route("/")
async def hello():
    return "Hello world!"

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command in the console:&lt;br&gt;
&lt;/p&gt;

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

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://emmett.sh/docs/2.5.x"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/emmett-framework/emmett"&gt;Repository&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Starlette
&lt;/h3&gt;

&lt;p&gt;Starlette is a lightweight ASGI framework/toolkit, which is ideal for building async web services in Python.&lt;/p&gt;

&lt;p&gt;It gives you the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A lightweight, low-complexity HTTP web framework.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WebSocket support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In-process background tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Startup and shutdown events.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test client built on &lt;code&gt;httpx&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CORS, GZip, Static Files, Streaming responses.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Session and Cookie support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;100% test coverage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;100% type annotated codebase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Few hard dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compatible with &lt;code&gt;asyncio&lt;/code&gt; and &lt;code&gt;trio&lt;/code&gt; backends.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install starlette uvicorn

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

&lt;/div&gt;



&lt;p&gt;We create a &lt;code&gt;server.py&lt;/code&gt; file and paste the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route

async def homepage(request):
    return JSONResponse({'hello': 'world'})

routes = [
    Route("/", endpoint=homepage)
]

app = Starlette(debug=True, routes=routes)

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

&lt;/div&gt;



&lt;p&gt;Then, we run the following command in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uvicorn server:app

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/encode/starlette"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.starlette.io/"&gt;Repository&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;In this article, we explore seven web frameworks for people who are willing to try something different from FastAPI. Many of them have a similar syntax and make it easy to use for people who are used to using FastAPI or Flask.&lt;/p&gt;

&lt;p&gt;Thank you for taking the time to read this article.&lt;/p&gt;

&lt;p&gt;If you have any recommendations about other packages, architectures, how to improve my code, my English, or anything; please leave a comment or contact me through &lt;a href="https://twitter.com/Carlos_marcv"&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;, or &lt;a href="https://www.linkedin.com/in/carlos-marcano-a2135a134/"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://sparckles.github.io/robyn/#/"&gt;Robyn Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sparckles/robyn"&gt;Robyn Github Page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sanic.dev/en/guide/getting-started.html"&gt;Sanic Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.neoteroi.dev/blacksheep/"&gt;Blacksheep Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.litestar.dev/latest/"&gt;LiteStar Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.starlette.io/"&gt;Starlette Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://falcon.readthedocs.io/en/stable/user/index.html"&gt;Falcon Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://emmett.sh/docs/2.5.x"&gt;Emmett Documentation&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>web</category>
      <category>frameworks</category>
    </item>
  </channel>
</rss>
