<?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: Eric Santana</title>
    <description>The latest articles on DEV Community by Eric Santana (@ericbsantana).</description>
    <link>https://dev.to/ericbsantana</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%2F679352%2F8ad33356-f499-4ed7-88b5-4b1161211616.png</url>
      <title>DEV Community: Eric Santana</title>
      <link>https://dev.to/ericbsantana</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ericbsantana"/>
    <language>en</language>
    <item>
      <title>Stop Your AI Agents from Clashing: Meet Worktree Wizardry 🧙‍♂️</title>
      <dc:creator>Eric Santana</dc:creator>
      <pubDate>Sun, 29 Mar 2026 23:17:26 +0000</pubDate>
      <link>https://dev.to/ericbsantana/stop-your-ai-agents-from-clashing-meet-worktree-wizardry-2anh</link>
      <guid>https://dev.to/ericbsantana/stop-your-ai-agents-from-clashing-meet-worktree-wizardry-2anh</guid>
      <description>&lt;p&gt;If you are experimenting with AI coding agents, or maybe you are just trying to work with many feature branches at the same time, you probably found this big problem. It is very hard to start local environments for multiple git worktrees in the exact same repository. &lt;/p&gt;

&lt;p&gt;What usually happens? You try to run the project and you get that terrible error: &lt;code&gt;bind: address already in use&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;When multiple agents or developers try to run different stacks at the same time, they conflict with each other. The ports are the same, the database state gets messy, and your automated workflow turns into a big headache. &lt;/p&gt;

&lt;p&gt;To solve this, I developed something called &lt;strong&gt;Worktree Wizardry&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is it exactly?
&lt;/h1&gt;

&lt;p&gt;Worktree Wizardry is a public starter kit for creating isolated per-worktree environments that uses Docker Compose. It is designed specifically for AI-assisted workflows where each worktree can run and test a different stack without collisions.&lt;/p&gt;

&lt;p&gt;It is basically a skill. It is simply a public repository that allows people to run agents to create for them a worktree sandbox. This makes it possible for them to test multiple worktrees at the same time without worrying with port numbers and other environment conflicts.&lt;/p&gt;

&lt;h1&gt;
  
  
  Main Features
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolated Port Allocation:&lt;/strong&gt; Every worktree gets a different port automatically. No more conflicts!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Git State:&lt;/strong&gt; It safely manages the shared state in the git common dir while keeping the execution isolated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart Integrations:&lt;/strong&gt; It comes with optional lifecycle hooks for AI tools like Conductor and Superset.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom Scripts:&lt;/strong&gt; Repo-specific setup and archive behavior is controlled via simple bash hook scripts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  How to use it
&lt;/h1&gt;

&lt;p&gt;It is very easy to start. You just use the &lt;code&gt;template/&lt;/code&gt; folder as the starting point for your new or existing repository. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Copy the template into your target repo:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash install.sh /path/to/target-repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Customize your environment:&lt;/strong&gt;&lt;br&gt;
In the target repo, you just need to edit these files for your app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;worktree-wizardry/config.env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;worktree-wizardry/hooks/setup.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;worktree-wizardry/hooks/archive.sh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker-compose.worktree-wizardry.yml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Run the magic:&lt;/strong&gt;&lt;br&gt;
It is one setup command per worktree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash scripts/worktree-wizardry-setup.sh
&lt;span class="c"&gt;# And to clean up later:&lt;/span&gt;
bash scripts/worktree-wizardry-archive.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see an example, the repository has a &lt;code&gt;demo/&lt;/code&gt; folder that shows a concrete adapter. It runs Postgres on &lt;code&gt;port + 1&lt;/code&gt; and an Nginx demo app on &lt;code&gt;port + 2&lt;/code&gt;, generates the &lt;code&gt;.env.local&lt;/code&gt; files automatically, and cleans up everything in the archive step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it out!
&lt;/h3&gt;

&lt;p&gt;If you are building tools for agents or just want a better way to test multiple worktrees locally, please take a look.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://github.com/ericbsantana/worktree-wizardry" rel="noopener noreferrer"&gt;https://github.com/ericbsantana/worktree-wizardry&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agentskills</category>
      <category>git</category>
    </item>
    <item>
      <title>Creating a free-tier AWS RDS PostgreSQL instance using Terraform</title>
      <dc:creator>Eric Santana</dc:creator>
      <pubDate>Sat, 06 Apr 2024 19:14:25 +0000</pubDate>
      <link>https://dev.to/ericbsantana/creating-a-free-tier-aws-rds-postgresql-instance-using-terraform-1nbo</link>
      <guid>https://dev.to/ericbsantana/creating-a-free-tier-aws-rds-postgresql-instance-using-terraform-1nbo</guid>
      <description>&lt;p&gt;One of the most powerful tools to use as a DevOps engineer is Terraform. Simply put, Terraform is&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;an infrastructure as code (IaC) tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means that you can actually create a whole infrastructure using declarative code. In other words, you are able to manage resources like storages as well as databases using only code.&lt;/p&gt;

&lt;p&gt;One of the main benefits in using IaC is to have a better control of your resources and make them scalable. Even more: if you are interested in using multiple cloud services, you can control all the resources from one place, saving you time from clicking and opening multiple tabs in your browser to access each cloud service console.&lt;/p&gt;

&lt;p&gt;In this article, I will provide a guide on how to &lt;strong&gt;create a free-tier AWS RDS database instance using Terraform&lt;/strong&gt;. I write for you who want to create a RDS database instance but don't want to deal too much with AWS console. Or if you just want to learn a bit the basics of Terraform. Let's get started!&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;First of all, you need to have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AWS account eligible to use RDS free-tier&lt;/li&gt;
&lt;li&gt;An AWS Access Key and Secret Key with Full Access to RDS associated to a new IAM user (&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user_manage_add-key.html"&gt;or use root at your own risk&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://developer.hashicorp.com/terraform/install"&gt;Terraform CLI&lt;/a&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Selecting and pointing to AWS provider
&lt;/h2&gt;

&lt;p&gt;First of all, let's create a folder to initialize a Terraform template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;terraform-aws-free-rds
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform-aws-free-rds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In this folder, create a &lt;code&gt;provider.tf&lt;/code&gt; that is going to be our entry point to declare which provider are we going to use. This is where we define  For now, its content will be:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5.43.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will tell Terraform: "I want to use the AWS provider to start my infrastructure!"&lt;/p&gt;

&lt;p&gt;But still Terraform don't know to which region we are going to create our resources or even how to access our AWS account to make these changes. In order to do so, we are going to create an AWS access key and secret key. &lt;/p&gt;

&lt;p&gt;We also do not want to use it explicitly in our &lt;code&gt;provider.tf&lt;/code&gt; file. So we are going to create a &lt;code&gt;variables.tf&lt;/code&gt; file and put this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_access_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS access key"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_secret_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS secret key"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will declare to Terraform that we have these two variables that can be accessed using &lt;code&gt;var.aws_secret_key&lt;/code&gt; and &lt;code&gt;var.aws_access_key&lt;/code&gt; and set using a &lt;code&gt;terraform.tfvars&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Create your &lt;code&gt;terraform.tfvars&lt;/code&gt; and put your credentials:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;aws_access_key&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your-access-key"&lt;/span&gt;
&lt;span class="nx"&gt;aws_secret_key&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"your-secret-keys"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This way, Terraform will know which values these variables have and use it on our &lt;code&gt;provider.tf&lt;/code&gt; file. Terraform will able to create, update or delete resources using this account. One more step and our provider will be completely set:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# on `provider.tf`&lt;/span&gt;

&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"5.43.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_access_key&lt;/span&gt;
  &lt;span class="nx"&gt;secret_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_secret_key&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now Terraform will indeed know to which region and which account the resources are going to be created.&lt;/p&gt;
&lt;h2&gt;
  
  
  Declare your RDS resource
&lt;/h2&gt;

&lt;p&gt;Now we just need to tell Terraform to create a RDS resource. As we are using aws provider to Terraform, &lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance"&gt;you may check this documentation&lt;/a&gt; to understand a bit more of what we are going to write on the &lt;code&gt;main.tf&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt; &lt;span class="s2"&gt;"my-free-rds-db-instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allocated_storage&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="nx"&gt;engine&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"postgres"&lt;/span&gt;
  &lt;span class="nx"&gt;engine_version&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"16.1"&lt;/span&gt;
  &lt;span class="nx"&gt;identifier&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-free-rds-db-instance"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_class&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"db.t3.micro"&lt;/span&gt;
  &lt;span class="nx"&gt;storage_encrypted&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;publicly_accessible&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;delete_automated_backups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;skip_final_snapshot&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;db_name&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"database_name"&lt;/span&gt;
  &lt;span class="nx"&gt;username&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"database_username"&lt;/span&gt;
  &lt;span class="nx"&gt;password&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"database_password"&lt;/span&gt;
  &lt;span class="nx"&gt;apply_immediately&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;multi_az&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Basically, what is happening here is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We tell Terraform to use AWS provider, using the credentials we set in &lt;code&gt;terraform.tfvars&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We declare a &lt;code&gt;aws_db_instance&lt;/code&gt; called &lt;code&gt;my-free-rds-db-instance&lt;/code&gt; and specify all the instance configuration that would be also specified via AWS console if we were creating using it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This configuration is known to be a free tier in RDS for 12 months. There are other configurations but for the PostgreSQL is this one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;750 hours of Amazon RDS Single-AZ db.t2.micro, db.t3.micro, and db.t4g.micro Instances usage running MySQL, MariaDB, PostgreSQL databases each month. ... 20 GB of General Purpose SSD (gp2) storage per month.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you check in the &lt;code&gt;main.tf&lt;/code&gt; file, we define this configuration in a declarative way.&lt;/p&gt;
&lt;h2&gt;
  
  
  Spinning up your Terraform resources
&lt;/h2&gt;

&lt;p&gt;Now run&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init &lt;span class="c"&gt;# to initialize the dependency lock file&lt;/span&gt;

Initializing the backend...

Initializing provider plugins...
- Finding hashicorp/aws versions matching &lt;span class="s2"&gt;"5.43.0"&lt;/span&gt;...
- Installing hashicorp/aws v5.43.0...
- Installed hashicorp/aws v5.43.0 &lt;span class="o"&gt;(&lt;/span&gt;signed by HashiCorp&lt;span class="o"&gt;)&lt;/span&gt;

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file &lt;span class="k"&gt;in &lt;/span&gt;your version control repository
so that Terraform can guarantee to make the same selections by default when
you run &lt;span class="s2"&gt;"terraform init"&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;the future.

Terraform has been successfully initialized!

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

&lt;/div&gt;


&lt;p&gt;Now we have our AWS provider set in our Terraform context, which will enable us to tell our local Terraform CLI commands which version of the provider to use.&lt;/p&gt;

&lt;p&gt;We may see the execution plan of the resources we have declared:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The output should be something like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  &lt;span class="c"&gt;# aws_db_instance.my-free-rds-db-instance will be created&lt;/span&gt;
  + resource &lt;span class="s2"&gt;"aws_db_instance"&lt;/span&gt; &lt;span class="s2"&gt;"my-free-rds-db-instance"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      + address                               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + allocated_storage                     &lt;span class="o"&gt;=&lt;/span&gt; 20
      + apply_immediately                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + arn                                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + auto_minor_version_upgrade            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + availability_zone                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + backup_retention_period               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + backup_target                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + backup_window                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + ca_cert_identifier                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + character_set_name                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + copy_tags_to_snapshot                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + db_name                               &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"database_name"&lt;/span&gt;
      + db_subnet_group_name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + delete_automated_backups              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + domain_fqdn                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + endpoint                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + engine                                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"postgres"&lt;/span&gt;
      + engine_version                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"16.1"&lt;/span&gt;
      + engine_version_actual                 &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + hosted_zone_id                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + &lt;span class="nb"&gt;id&lt;/span&gt;                                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + identifier                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-free-rds-db-instance"&lt;/span&gt;
      + identifier_prefix                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + instance_class                        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"db.t3.micro"&lt;/span&gt;
      + iops                                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + kms_key_id                            &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + latest_restorable_time                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + license_model                         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + listener_endpoint                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + maintenance_window                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + master_user_secret                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + master_user_secret_kms_key_id         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + monitoring_interval                   &lt;span class="o"&gt;=&lt;/span&gt; 0
      + monitoring_role_arn                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + multi_az                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + nchar_character_set_name              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + network_type                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + option_group_name                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + parameter_group_name                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + password                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;sensitive value&lt;span class="o"&gt;)&lt;/span&gt;
      + performance_insights_enabled          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + performance_insights_kms_key_id       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + performance_insights_retention_period &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + port                                  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + publicly_accessible                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + replica_mode                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + replicas                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + resource_id                           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + skip_final_snapshot                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
      + snapshot_identifier                   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + status                                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + storage_encrypted                     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;
      + storage_throughput                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + storage_type                          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + tags_all                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + timezone                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
      + username                              &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"database_username"&lt;/span&gt;
      + vpc_security_group_ids                &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;known after apply&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

Plan: 1 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This tells us which resources we declared and that are going to be created, changed or destroyed using the code we wrote.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=RBE4fZLqrVQ"&gt;As Bane said in The Dark Knight Rises: "It doesn't matter who we are, what matters is our plan"&lt;/a&gt;, after reviewing and check the plan Terraform showed us, it is time to apply it:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will apply the plan and should take like three minutes to AWS make the health check of the instance available.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws_db_instance.my-free-rds-db-instance: Creating...
aws_db_instance.my-free-rds-db-instance: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;10s elapsed]
aws_db_instance.my-free-rds-db-instance: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;20s elapsed]
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you log in to your AWS account and go to RDS, you'll see that the instance is going to be in the creating state:&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%2Fohi7xrjm27kmts8eng5v.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%2Fohi7xrjm27kmts8eng5v.png" alt="my-free-rds-db-instance showing the creating state in AWS" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few minutes, it will be available and Terraform will tell us:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
aws_db_instance.my-free-rds-db-instance: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;3m30s elapsed]
aws_db_instance.my-free-rds-db-instance: Still creating... &lt;span class="o"&gt;[&lt;/span&gt;3m40s elapsed]
aws_db_instance.my-free-rds-db-instance: Creation &lt;span class="nb"&gt;complete &lt;/span&gt;after 3m49s &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db-UZADWSYL266LYV2W6D6H7CUM4U]

Apply &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; Resources: 1 added, 0 changed, 0 destroyed.
&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%2Fr91gkfe9zwqantye8vh0.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%2Fr91gkfe9zwqantye8vh0.png" alt="my-free-rds-db-instance showing the available state in AWS" width="727" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may check the endpoint to the instance via AWS Console or just accessing your &lt;code&gt;terraform.tfstate&lt;/code&gt; and looking for the &lt;code&gt;address&lt;/code&gt;. To connect, you just connect like it was a usual database. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember to use SSL to make the connection, otherwise you'll get an error. You could also add a parameter group in your Terraform files to make the &lt;code&gt;force_ssl&lt;/code&gt; to 0 in your db instance.&lt;/p&gt;
&lt;/blockquote&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%2Fclt4twt3e4rocp2xj5lk.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%2Fclt4twt3e4rocp2xj5lk.png" alt="Connection test of my-free-rds-db-instance working" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This was a simple guide to introduce the readers on Terraform and also help them to create a free PostgreSQL RDS instance in AWS.&lt;/p&gt;

&lt;p&gt;I hope this helps you to spin up a free PostgresQL to simulate a production-ready application and learn a bit more about using Terraform.&lt;/p&gt;

&lt;p&gt;This project code is available on Github: &lt;a href="https://github.com/ericbsantana/terraform-aws-free-rds"&gt;terraform-aws-free-rds&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/ericbsantana"&gt;
        ericbsantana
      &lt;/a&gt; / &lt;a href="https://github.com/ericbsantana/terraform-aws-free-rds"&gt;
        terraform-aws-free-rds
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      💰 A PostgreSQL free tier RDS instance using Terraform
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>aws</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>A testable Go API using Gin + MongoDB + Testcontainers</title>
      <dc:creator>Eric Santana</dc:creator>
      <pubDate>Tue, 02 Apr 2024 01:46:43 +0000</pubDate>
      <link>https://dev.to/ericbsantana/a-testable-go-api-using-gin-mongodb-testcontainers-1bd2</link>
      <guid>https://dev.to/ericbsantana/a-testable-go-api-using-gin-mongodb-testcontainers-1bd2</guid>
      <description>&lt;p&gt;At my day-to-day, I work mainly with Node.js. Installing &lt;code&gt;npm&lt;/code&gt; packages, using webpack to bundle stuff and importing named exports in a lot of &lt;code&gt;.ts&lt;/code&gt; files. NoSQL databases are not too far away from me too.&lt;/p&gt;

&lt;p&gt;But in my free time I like to explore new technologies. The Go realm is one of them. When doing this, I feel like I am in a new town: getting to know new places and meeting new people.&lt;/p&gt;

&lt;p&gt;After years working with MongoDB and having few experiences when reading and learning about Go, I thought it was time to create something to the Go community. So here it is: a minimal Go boilerplate that is equipped with Gin + MongoDB and testable with Testcontainers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why &lt;em&gt;another-boilerplate-in-an-open-source-repository&lt;/em&gt;?
&lt;/h2&gt;

&lt;p&gt;Starting a project from scratch in Go can be a bit painful, especially when you came from other programming languages like TypeScript.&lt;/p&gt;

&lt;p&gt;Considering this and how I read "learn in public" sentence as a moral rule, I have decided to create a Go repository for me which contains Gin + MongoDB to accelerate the creation of APIs using these tools.&lt;/p&gt;

&lt;p&gt;Feel free to use this repository to learn more about Testcontainers, which I consider to be the more powerful thing in this boilerplate. If you want to know more about the template, keep reading. Want to jump directly to the code? &lt;a href="https://github.com/ericbsantana/gogimon"&gt;Feel free to do so!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are these Testcontainers and why should I use them?
&lt;/h2&gt;

&lt;p&gt;The chef's kiss of this template is the usage of &lt;a href="https://golang.testcontainers.org/"&gt;Testcontainers&lt;/a&gt; to make integration tests in the application's handlers.&lt;/p&gt;

&lt;p&gt;This will help me in the future when building a more complex structure using another services like Kafka or Redis. &lt;/p&gt;

&lt;p&gt;The best thing about using Testcontainers is to able to make integration tests and maintain your codebase fully testable if you use it correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What were your difficulties when developing this project?
&lt;/h2&gt;

&lt;p&gt;The most painful one was that I was not able to make &lt;a href="https://github.com/cosmtrek/air"&gt;Air&lt;/a&gt; work well with Docker: for some reason, my docker-compose file was not making the live-reload work correctly.&lt;/p&gt;

&lt;p&gt;Still, the project works fine running &lt;code&gt;go run main.go&lt;/code&gt; or install Air and run with &lt;code&gt;air&lt;/code&gt; natively.&lt;/p&gt;

&lt;p&gt;I have also tried to follow a dependency injection pattern in this boilerplate, which helped a lot when creating the tests for the handlers.&lt;/p&gt;

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

&lt;p&gt;We could indeed use other tools to make tests. You could mock the repositories or find an library that helps to do them. I honestly do not know if there is a &lt;code&gt;mongodb-memory-server&lt;/code&gt; like or equivalent to Go, but would be very happy to know if that is the case. &lt;/p&gt;

&lt;p&gt;If you are interested in using Testcontainers in Go and do not want to do all the groundwork, feel free to use my template that is available here: &lt;a href="https://github.com/ericbsantana/gogimon"&gt;gogimon&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>mongodb</category>
      <category>api</category>
      <category>learning</category>
    </item>
    <item>
      <title>Build your own curl in Golang</title>
      <dc:creator>Eric Santana</dc:creator>
      <pubDate>Wed, 27 Mar 2024 23:05:00 +0000</pubDate>
      <link>https://dev.to/ericbsantana/build-your-own-curl-in-go-2p71</link>
      <guid>https://dev.to/ericbsantana/build-your-own-curl-in-go-2p71</guid>
      <description>&lt;p&gt;In this tutorial, we'll walk through the process of creating a simple command-line tool similar to &lt;code&gt;curl&lt;/code&gt; using Go and &lt;a href="https://github.com/spf13/cobra"&gt;Cobra&lt;/a&gt;, a CLI library for Go.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Here we are going to use the &lt;code&gt;net&lt;/code&gt; package instead of the &lt;code&gt;http&lt;/code&gt; that is available natively in Go. The reason for using &lt;code&gt;net&lt;/code&gt; is to get a bit into the basics of creating a HTTP request to a server from scrach. You could easily use &lt;code&gt;http&lt;/code&gt; package to enjoy all that stuff that is a pain to make from scratch. For instance, &lt;code&gt;http&lt;/code&gt; already handles HTTPS requests.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a challenge because handling TCP connections and HTTP requests can be complex, but we'll keep it simple and focus on the basics. But will be a good start to understand how &lt;code&gt;curl&lt;/code&gt; works under the hood and how to build a simple HTTP client using Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, make sure you have the following installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go (version 1.22)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting Up Your Project
&lt;/h2&gt;

&lt;p&gt;First, let's create a new Go module for our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;build-your-own-curl
&lt;span class="nb"&gt;cd &lt;/span&gt;build-your-own-curl
go mod init build-your-own-curl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's install Cobra:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get &lt;span class="nt"&gt;-u&lt;/span&gt; github.com/spf13/cobra/cobra
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's initialize Cobra in our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cobra-cli init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the structure for our CLI application with the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── cmd/
│   ├── root.go
├── go.mod
├── go.sum
├── main.go
├── LICENSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;cmd&lt;/code&gt; directory contains the root command file, which is where we'll define our whole application. It is also possible to create subcommands in separate files within the &lt;code&gt;cmd&lt;/code&gt; directory. For now, we'll keep it simple and define everything in the &lt;code&gt;root.go&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the Root Command
&lt;/h2&gt;

&lt;p&gt;Now that we have our project set up, let's define our CLI commands using Cobra. We'll create a simple command to make HTTP GET requests.&lt;/p&gt;

&lt;p&gt;To achieve this, we need to parse the URL provided as an argument, extract the hostname, port, and path, and then make an HTTP GET request to the specified URL. Firstly, we should parse the URL and extract the necessary information to create our TCP connection.&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="c"&gt;// cmd/root.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;cmd&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;"net/url"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/spf13/cobra"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootCmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"build-your-own-curl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Short&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A brief description of your application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;`A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExactArgs&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="n"&gt;Run&lt;/span&gt;&lt;span class="o"&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;cmd&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;u&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;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="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="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hostname&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="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;

        &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Port:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&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="c"&gt;// rest of the code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Args&lt;/code&gt; field specifies the number of arguments the command expects. In this case, we expect exactly one argument, which is the URL we want to make a request to. You may want to add custom validators or other &lt;code&gt;Cobra&lt;/code&gt; built-in validators if you want to expand this functionality. If you run the application without an argument, you should see an error message printed to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go

Error: accepts 1 arg&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt;, received 0
Usage:
  build-your-own-curl &lt;span class="o"&gt;[&lt;/span&gt;flags]

Flags:
  &lt;span class="nt"&gt;-h&lt;/span&gt;, &lt;span class="nt"&gt;--help&lt;/span&gt;     &lt;span class="nb"&gt;help &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;build-your-own-curl
  &lt;span class="nt"&gt;-t&lt;/span&gt;, &lt;span class="nt"&gt;--toggle&lt;/span&gt;   Help message &lt;span class="k"&gt;for &lt;/span&gt;toggle

&lt;span class="nb"&gt;exit &lt;/span&gt;status 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if you run the application with an URL as an argument, you should see that hostname, port, and path are printed to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go https://example.com/get

Host: example.com
Port:
Path: /get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you probably have noticed, the port is not being extracted correctly. This is because the &lt;code&gt;url.Parse&lt;/code&gt; function does not return the port if it is not specified in the URL.&lt;/p&gt;

&lt;p&gt;Most of us do not specify the port when making a day-to-day HTTP request using &lt;code&gt;curl&lt;/code&gt; or a browser. To make our UX better, let's set the default port to 80 if it is not specified in the URL. In this tutorial, I will not handle HTTPS requests, which is why we are going to use only port HTTP (80) for now.&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="c"&gt;// cmd/root.go&lt;/span&gt;
&lt;span class="c"&gt;// ...&lt;/span&gt;
  &lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="o"&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;cmd&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;u&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;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="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="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hostname&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="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&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;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"80"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;

    &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Host:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Port:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Path:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&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="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the application to see the default port bring printed to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go https://example.com/get

Host: example.com
Port: 80
Path: /get
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the necessary information to create a TCP connection, let's make an HTTP GET request to the specified URL.&lt;/p&gt;

&lt;p&gt;A basic HTTP GET request header consists of the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request line: &lt;code&gt;GET /path HTTP/1.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Host header: &lt;code&gt;Host: hostname&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will send this request to the server using &lt;code&gt;net.Dial&lt;/code&gt; and read the response. We will then print the response to the console.&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="c"&gt;// cmd/root.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;cmd&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;"net"&lt;/span&gt;
    &lt;span class="s"&gt;"net/url"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/spf13/cobra"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rootCmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"build-your-own-curl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Short&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A brief description of your application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;`A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ExactArgs&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="n"&gt;Run&lt;/span&gt;&lt;span class="o"&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;cmd&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cobra&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;u&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;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="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="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hostname&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="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"80"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;conn&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;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tcp"&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;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s:%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&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="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;conn&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"GET %s HTTP/1.0&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;Host: %s&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;n&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;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="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="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;n&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="c"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we create a TCP connection to the specified host and port. We then send an HTTP GET request to the server and read the response. Finally, we print the response to the console. If you run the application with a valid URL, you should see the HTTP response printed to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go http://eu.httpbin.org/get
HTTP/1.1 200 OK
Date: Wed, 27 Mar 2024 22:35:13 GMT
Content-Type: application/json
Content-Length: 203
Connection: close
Server: gunicorn/19.9.0
Access-Control-Allow-Origin: &lt;span class="k"&gt;*&lt;/span&gt;
Access-Control-Allow-Credentials: &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"args"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;,
  &lt;span class="s2"&gt;"headers"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"Host"&lt;/span&gt;: &lt;span class="s2"&gt;"eu.httpbin.org"&lt;/span&gt;,
    &lt;span class="s2"&gt;"X-Amzn-Trace-Id"&lt;/span&gt;: &lt;span class="s2"&gt;"Root=1-66049f21-305d0735393fd4ae2bc554a0"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"url"&lt;/span&gt;: &lt;span class="s2"&gt;"http://eu.httpbin.org/get"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you have it! A command-line tool to make GET requests to any URL using Go and Cobra. Additional changes can be made to handle different HTTP methods, headers, and more.&lt;/p&gt;

&lt;p&gt;A challenge for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for different HTTP methods (e.g., POST, PUT, DELETE).&lt;/li&gt;
&lt;li&gt;Add support for custom headers.&lt;/li&gt;
&lt;li&gt;Add support for HTTPS requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first and second were made in my project &lt;a href="https://github.com/ericbsantana/gurl"&gt;gurl&lt;/a&gt;. You can check it out for more inspiration or even to contribute and improve it!&lt;/p&gt;

&lt;p&gt;Another project that worth to mention here is &lt;a href="https://github.com/cdwiegand/go-curling"&gt;go-curling&lt;/a&gt; from &lt;a class="mentioned-user" href="https://dev.to/chriswiegand"&gt;@chriswiegand&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;It uses &lt;code&gt;http&lt;/code&gt; package instead of &lt;code&gt;net&lt;/code&gt; which shows a more simple and fast way to implement a cURL client in Go without making requests from scratch using &lt;code&gt;net.Dial&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;Congratulations! You've built a simple command-line tool similar to &lt;code&gt;curl&lt;/code&gt; using Go and Cobra. Feel free to expand upon this project by adding more features like handling different HTTP methods, headers, and more.&lt;/p&gt;

&lt;p&gt;I have made a project called &lt;a href="https://github.com/ericbsantana/gurl"&gt;gurl&lt;/a&gt; that is a simple CLI tool that can make HTTP requests using Go. You can check it out for more inspiration.&lt;/p&gt;

</description>
      <category>curl</category>
      <category>go</category>
    </item>
  </channel>
</rss>
