<?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: chamupathi mendis</title>
    <description>The latest articles on DEV Community by chamupathi mendis (@chamupathi_mendis_cdd19da).</description>
    <link>https://dev.to/chamupathi_mendis_cdd19da</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%2F2660282%2F3425b386-bda9-4e23-a08e-a0699a17e346.png</url>
      <title>DEV Community: chamupathi mendis</title>
      <link>https://dev.to/chamupathi_mendis_cdd19da</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chamupathi_mendis_cdd19da"/>
    <language>en</language>
    <item>
      <title>How I Made an Unreliable External API Reliable;</title>
      <dc:creator>chamupathi mendis</dc:creator>
      <pubDate>Mon, 14 Jul 2025 23:44:31 +0000</pubDate>
      <link>https://dev.to/chamupathi_mendis_cdd19da/how-i-made-an-unreliable-external-api-reliable-1bjo</link>
      <guid>https://dev.to/chamupathi_mendis_cdd19da/how-i-made-an-unreliable-external-api-reliable-1bjo</guid>
      <description>&lt;h2&gt;
  
  
  Using Supabase Edge Functions (Without Breaking the External API Quota)
&lt;/h2&gt;



&lt;h3&gt;
  
  
  Third-party APIs are great… until they’re not.
&lt;/h3&gt;

&lt;p&gt;Recently, I ran into a challenge where I had to integrate with a flaky external API, while also respecting a limited quota and dealing with internal service calls that weren't smart enough to stop hitting it over and over. And to make things worse, implementing internal caching wasn’t an option.&lt;/p&gt;

&lt;p&gt;So, I came up with a simple and cost-effective solution using Supabase Edge Functions and a Supabase Database — and it worked like a charm.&lt;/p&gt;

&lt;p&gt;Here’s how I did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚧 The Problem
&lt;/h2&gt;

&lt;p&gt;Here's what I was dealing with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I needed to fetch data from a third-party API.&lt;/li&gt;
&lt;li&gt;The API was unreliable — it sometimes timed out or returned errors.&lt;/li&gt;
&lt;li&gt;We had a quota for how many times we could call it.&lt;/li&gt;
&lt;li&gt;Our internal services would sometimes call the same endpoint multiple times in quick succession.&lt;/li&gt;
&lt;li&gt;Implementing caching inside each internal service was too expensive or complex at the moment.&lt;/li&gt;
&lt;li&gt;We were okay serving slightly stale data if the API was unavailable.&lt;/li&gt;
&lt;li&gt;I needed a reliable, cost-effective buffer in front of the API.&lt;/li&gt;
&lt;li&gt;I wanted the proxy service  to &lt;strong&gt;cache it all&lt;/strong&gt; without configuring&lt;/li&gt;
&lt;/ul&gt;



&lt;blockquote&gt;
&lt;p&gt;“Sounds familiar? You have a quota-limited external API, but your backend services love hammering it as if rate limits don’t exist. And caching it internally? A refactor no one wants to touch right now.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;br&gt;
&lt;/h2&gt;

&lt;h2&gt;
  
  
  🧠 The Idea
&lt;/h2&gt;

&lt;p&gt;I didn’t want to spin up Redis or deploy a new caching layer just for this. But I did need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A place to store cached API responses.&lt;/li&gt;
&lt;li&gt;A lightweight service that could check if data was cached, and only fetch from the external API when needed.&lt;/li&gt;
&lt;li&gt;A fallback mechanism to return cached data when the API fails.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 That's when I thought: Why not use Supabase Edge Functions (which are serverless and fast) with a Supabase Postgres table to cache results?&lt;/p&gt;

&lt;h2&gt;
  
  
  🗺️ Solution Architecture
&lt;/h2&gt;

&lt;p&gt;Here's the basic flow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tel5ngmp3wc1z2zcj4s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6tel5ngmp3wc1z2zcj4s.png" alt=" " width="503" height="713"&gt;&lt;/a&gt;&lt;br&gt;
Internal services now hit my Edge Function instead of calling the external API directly.&lt;/p&gt;

&lt;p&gt;The Edge Function:&lt;/p&gt;

&lt;p&gt;Checks if the data exists in the Supabase DB and is still fresh.&lt;/p&gt;

&lt;p&gt;If fresh: returns it.&lt;/p&gt;

&lt;p&gt;If stale: tries to fetch from the external API.&lt;/p&gt;

&lt;p&gt;If successful: updates the DB and returns the data.&lt;/p&gt;

&lt;p&gt;If the API fails: returns the last known cached value.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  ✅ Why Supabase Was a Great Fit
&lt;/h2&gt;

&lt;p&gt;Free for small projects.&lt;/p&gt;

&lt;p&gt;Edge Functions are super fast and easy to deploy.&lt;/p&gt;

&lt;p&gt;Postgres + JSONB = flexible caching store.&lt;/p&gt;

&lt;p&gt;No need for external infra like Redis or server instances.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 Lessons Learned
&lt;/h2&gt;

&lt;p&gt;When you can’t control the external API, build a reliable layer around it.&lt;/p&gt;

&lt;p&gt;Supabase gives you the speed of serverless and the power of a real database.&lt;/p&gt;

&lt;p&gt;Sometimes, the best solution is not the fanciest, but the most practical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/chamupathi/proxy-cache-supabase" rel="noopener noreferrer"&gt;https://github.com/chamupathi/proxy-cache-supabase&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>distributedsystems</category>
      <category>supabase</category>
      <category>costmatters</category>
    </item>
    <item>
      <title>Integrate Microsoft Clarity with Your Next.js App</title>
      <dc:creator>chamupathi mendis</dc:creator>
      <pubDate>Sun, 23 Mar 2025 00:28:02 +0000</pubDate>
      <link>https://dev.to/chamupathi_mendis_cdd19da/integrate-ms-clarity-to-nextjs-app-app-router--241o</link>
      <guid>https://dev.to/chamupathi_mendis_cdd19da/integrate-ms-clarity-to-nextjs-app-app-router--241o</guid>
      <description>&lt;h2&gt;
  
  
  Why Track User Behavior?
&lt;/h2&gt;

&lt;p&gt;Understanding how users interact with your website is crucial for improving the user experience. Behavior analytics tools help identify friction points, optimize conversions, and provide insights into how users navigate your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Microsoft Clarity?
&lt;/h2&gt;

&lt;p&gt;Microsoft Clarity is a free behavior analytics tool that offers:&lt;br&gt;
✅ Heatmaps – Visualize where users click and scroll.&lt;br&gt;
✅ Session Recordings – Replay user sessions to understand their journey.&lt;br&gt;
✅ Insights – Identify pain points and engagement metrics.&lt;br&gt;
✅ Google Analytics Integration – Combine behavioral data with standard analytics.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll integrate Microsoft Clarity into a Next.js app and ensure it runs efficiently without affecting server-side rendering (SSR). Let’s get started!&lt;/p&gt;


&lt;h2&gt;
  
  
  Video of Clarity dashboard
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Ud90k8bCyf8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Create a next app
&lt;/h2&gt;

&lt;p&gt;If you don’t have an existing Next.js project, create one using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app ms-clarity-example

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Install Microsoft Clarity
&lt;/h2&gt;

&lt;p&gt;Install the Clarity package using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @microsoft/clarity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Get Your Clarity Project ID
&lt;/h2&gt;

&lt;p&gt;2.1. Create a project via&lt;br&gt;
&lt;a href="https://clarity.microsoft.com/" rel="noopener noreferrer"&gt;https://clarity.microsoft.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2.2. Navigate to Settings → Overview and copy the Project ID.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Initializing Clarity in Next.js
&lt;/h2&gt;

&lt;p&gt;To start tracking user sessions, we need to initialize Clarity using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clarity.init(&amp;lt;project-id&amp;gt;);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🚨 The Catch: Run Clarity on the Client Side Only
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clarity should not run on the server side, as it relies on window.&lt;/li&gt;
&lt;li&gt;We must use useEffect to ensure it runs only in the browser.&lt;/li&gt;
&lt;li&gt;The best place to initialize Clarity is in the top-level layout.tsx file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ Initial Approach: Adding it Directly in RootLayout.tsx
&lt;/h3&gt;



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

/* Rest of the code */
export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {

    useEffect(() =&amp;gt; {
        if (typeof window !== "undefined") {
            Clarity.init(process.env.NEXT_PUBLIC_CLARITY_ID!);
        }
      }, []);

/* Rest of the code */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Problem: RootLayout Becomes a Client Component&lt;/p&gt;

&lt;p&gt;By adding &lt;code&gt;"use client"&lt;/code&gt;, we force &lt;code&gt;RootLayout&lt;/code&gt; to be client-rendered, preventing Next.js from utilizing SSR and static generation. Instead, we should move Clarity initialization to a separate component.&lt;/p&gt;




&lt;h4&gt;
  
  
  Hook vs a component
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Hook
&lt;/h5&gt;

&lt;p&gt;If the logic is moved to a hook we have the same problem &lt;code&gt;RootLayout&lt;/code&gt; needs to be a &lt;code&gt;client component&lt;/code&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 Clarity from "@microsoft/clarity";
import { useEffect } from "react";

export default function useMsClarity() {
    useEffect(() =&amp;gt; {
        if (typeof window !== "undefined") {
            Clarity.init(process.env.NEXT_PUBLIC_CLARITY_ID!);
        }
      }, []);
}

// Rootlayout.tsx
"use client"
export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {

    // use hook
      useMsClarity();

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

&lt;/div&gt;






&lt;h5&gt;
  
  
  Component
&lt;/h5&gt;

&lt;p&gt;Lets create a client component to hold the logic&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import Clarity from "@microsoft/clarity";
import { useEffect } from "react";

export default function useMsClarity() {
    useEffect(() =&amp;gt; {
        if (typeof window !== "undefined") {
            Clarity.init(&amp;lt;project-id&amp;gt;);
        }
      }, []);

    return null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the component in root layout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import BaseLayout from "./ui/layouts/BaseLayout";
import ClarityInit from "./ui/analytics/clarity-init";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
    title: "MS clarity test",
    description: "MS clarity test",
};

export default function RootLayout({
    children,
}: {
    children: React.ReactNode;
}) {

    return (
        &amp;lt;html lang="en"&amp;gt;
            &amp;lt;body className={inter.className}&amp;gt;
                &amp;lt;ClarityInit /&amp;gt;
                &amp;lt;BaseLayout&amp;gt;
                    {children}
                &amp;lt;/BaseLayout&amp;gt;
            &amp;lt;/body&amp;gt;
        &amp;lt;/html&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;By moving the logic to a separate component (ClarityInit), we avoid making RootLayout a client component, allowing the rest of the app to benefit from Server-Side Rendering (SSR).&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Store the Clarity Project ID Securely
&lt;/h2&gt;

&lt;p&gt;finally move the &lt;code&gt;clarity-project-id&lt;/code&gt; to an env variable to make sure that it doe not pushed to version control system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clarity.init(process.env.NEXT_PUBLIC_CLARITY_ID!);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; &lt;br&gt;
The &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix is needed so that nextjs can read the env variable.&lt;/p&gt;

&lt;p&gt;add it to .env file as well&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_CLARITY_ID=&amp;lt;clarity-project-id&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Ms-clarity will start to collect sessions and you can see them in the dashboard.&lt;/p&gt;

&lt;p&gt;git hub link with code example.&lt;br&gt;
&lt;a href="https://github.com/chamupathi/ms-clarity" rel="noopener noreferrer"&gt;https://github.com/chamupathi/ms-clarity&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>clarity</category>
      <category>userbehaviour</category>
      <category>heatmaps</category>
    </item>
    <item>
      <title>🚀 Building a RESTful API in Go: A Practical Guide</title>
      <dc:creator>chamupathi mendis</dc:creator>
      <pubDate>Sun, 05 Jan 2025 16:31:48 +0000</pubDate>
      <link>https://dev.to/chamupathi_mendis_cdd19da/building-a-restful-api-in-go-a-practical-guide-b4p</link>
      <guid>https://dev.to/chamupathi_mendis_cdd19da/building-a-restful-api-in-go-a-practical-guide-b4p</guid>
      <description>&lt;h2&gt;
  
  
  From Setup to CRUD Operations with Gin &amp;amp; PostgreSQL
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Golang&lt;/strong&gt; is a fantastic programming language! If you're just starting out, the official documentation is a great resource. But when it comes to practical application, things can get tricky.&lt;/p&gt;

&lt;p&gt;Many developers have asked me how to build a RESTful API using Go, so I decided to create a minimal working REST API with step-by-step guidance. Let's dive in! 🔥&lt;/p&gt;




&lt;p&gt;🛠 Step-by-Step Guide to Building a REST API in Go&lt;br&gt;
We'll be using: &lt;br&gt;
✅ Gin (Fast &amp;amp; lightweight web framework)&lt;br&gt;
✅ GORM (ORM for PostgreSQL)&lt;br&gt;
✅ Docker (To run PostgreSQL in a container)&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: Setting Up the Project &amp;amp; Installing Dependencies
&lt;/h2&gt;

&lt;p&gt;First, create a new Go project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir go-rest-api &amp;amp;&amp;amp; cd booking-app
go mod init booking-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, install 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;Create the initial route&lt;/p&gt;

&lt;p&gt;create file &lt;code&gt;routes/booking.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func GetBookings(c *gin.Context) {
    var bookings []string = []string{"abc"}
    c.JSON(http.StatusOK, bookings)
}

func SetupRoutes(router *gin.Engine) {
    router.GET("v1/bookings", GetBookings)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use above route in main.go, create a file &lt;code&gt;main.go&lt;/code&gt;&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 (
    "booking-app/routes"
    "fmt"

    "github.com/gin-gonic/gin"
)

func main() {
    // Setup Gin Router
    router := gin.Default()

    routes.SetupRoutes(router)

    // Start the server
    port := ":8080"
    fmt.Println("Server is running on port", port)
    router.Run(port)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;run&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;visit &lt;code&gt;http://localhost:8080/v1/bookings&lt;/code&gt; you should get a response with &lt;code&gt;["abc"]&lt;/code&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Setting Up PostgreSQL Using Docker:
&lt;/h2&gt;

&lt;p&gt;Instead of installing PostgreSQL manually, let's use Docker.&lt;/p&gt;

&lt;p&gt;To make sure that the starting docker container lives with the application itself, let create a docker-compose &lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.8'

services:
  postgres:
    image: postgres:latest
    container_name: postgres_container
    restart: always
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: yourpassword
      POSTGRES_DB: booking_db
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;make sure that the docker is running in background&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  adding DB connectivity to the app
&lt;/h3&gt;

&lt;p&gt;create the file &lt;code&gt;config/database.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

import (
    "fmt"
    "log"

    "gorm.io/driver/postgres"
    "gorm.io/gorm"
)

var DB *gorm.DB

func ConnnetDatabse() {
    dsn := "host=localhost user=postgres password=yourpassword dbname=booking_db port=5432 sslmode=disable"
    db, err := gorm.Open(postgres.Open(dsn), &amp;amp;gorm.Config{})

    if err != nil {
        // log.Fatal will log the message to terminal and exit the program
        log.Fatal("Failed to connect to the database:", err)
    }

    DB = db

    fmt.Println("Connected to PostgreSQL!")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;add following line at the top of the function &lt;code&gt;main&lt;/code&gt; in main.go to initialise the database connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    config.ConnnetDatabse()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The step 2 &lt;a href="https://github.com/chamupathi/go-gin-REST-API/tree/02-add-postgress-docker-db" rel="noopener noreferrer"&gt;github link&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Adding a Data Model &amp;amp; First Route (Create a Resource)
&lt;/h2&gt;

&lt;p&gt;Let's define a model for our API.&lt;/p&gt;

&lt;p&gt;First, install GORM and the PostgreSQL driver:&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 gorm.io/gorm
go get -u gorm.io/driver/postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;create the modal&lt;br&gt;
&lt;/p&gt;

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

import "gorm.io/gorm"

type User struct {
    gorm.Model
    Name  string `json:"name" binding:"required" gorm:"not null"`
    Email string `json:"email" binding:"required,email" gorm:"not null"`
    Date  string `json:"date"`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;gorm.Modal&lt;/code&gt; adds few default fields to the struct as &lt;code&gt;ID&lt;/code&gt;, &lt;code&gt;CreatedAt&lt;/code&gt;, &lt;code&gt;UpdatedAt&lt;/code&gt;, &lt;code&gt;DeletedAt&lt;/code&gt; and managed by it self.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bindind&lt;/code&gt; make sure that the fileds are validate when the json is binded to the modal.&lt;/p&gt;

&lt;h3&gt;
  
  
  lets create a controller to for bookings
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;controller/bookings.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

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

import (
    "booking-app/config"
    "booking-app/models"
    "net/http"

    "github.com/gin-gonic/gin"
)

func GetBookings(c *gin.Context) {
    var bookings []models.Booking
    config.DB.Find(&amp;amp;bookings)
    c.JSON(http.StatusOK, bookings)
}

func CreateBooking(c *gin.Context) {
    var booking models.Booking

    if err := c.ShouldBindJSON(&amp;amp;booking); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    config.DB.Create(&amp;amp;booking)
    c.JSON(http.StatusCreated, booking)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created a handler for creating bookings and moved the logic to get bookings from routes file as well.&lt;/p&gt;

&lt;p&gt;now update the &lt;code&gt;routes/booking.go&lt;/code&gt; as well.&lt;br&gt;
&lt;/p&gt;

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

import (
    "booking-app/controllers"

    "github.com/gin-gonic/gin"
)

func SetupRoutes(router *gin.Engine) {
    router.GET("v1/bookings", controllers.GetBookings)
    router.POST("v1/bookings", controllers.CreateBooking)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now let's test the endpoint&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST http://localhost:8080/v1/bookings -H "Content-Type: application/json" -d '{"name":"John Doe", "email":"john@example.com"}'

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

&lt;/div&gt;



&lt;p&gt;code for &lt;a href="https://github.com/chamupathi/go-gin-REST-API/tree/03-data-model-and-create-route" rel="noopener noreferrer"&gt;step 3&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Get a booking by ID
&lt;/h2&gt;

&lt;p&gt;Lets update the controller to get a booking&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func GetBookingsById(c *gin.Context) {
    id := c.Param("id")

    var booking models.Booking

    err := config.DB.First(&amp;amp;booking, id).Error
    if err != nil {
        c.JSON(http.StatusNotFound, nil)
        return
    }

    c.JSON(http.StatusOK, booking)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;lets add the route to &lt;code&gt;routes/booking.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;router.GET("v1/bookings/:id", controllers.GetBookingsById)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By re-running the application you should be able to fetch booking by id&lt;/p&gt;

&lt;p&gt;code for &lt;a href="https://github.com/chamupathi/go-gin-REST-API/tree/04-get-by-id" rel="noopener noreferrer"&gt;step 04&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Update &amp;amp; Delete Operations
&lt;/h2&gt;

&lt;p&gt;now you guessed right. know the drill. update the controller, ad it to route.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;controllers/booking.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type UpdateBookingRequest struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Date  string `json:"date"`
}

func UpdateBooking(c *gin.Context) {
    var id = c.Param("id")
    var booking models.Booking
    var updateRequest UpdateBookingRequest

    err := config.DB.First(&amp;amp;booking, id)

    if err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": "Booking not found"})
    }

    if err := c.ShouldBindJSON(&amp;amp;updateRequest); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

    if err := config.DB.Model(&amp;amp;booking).Updates(updateRequest).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
        return
    }

    c.JSON(http.StatusOK, booking)
}

func DeleteBooking(c *gin.Context) {
    var id = c.Param("id")
    var booking models.Booking

    if err := config.DB.First(&amp;amp;booking, id).Error; err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": "Booking not found"})
        return
    }

    if err := config.DB.Delete(&amp;amp;booking).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
        return
    }

    c.JSON(http.StatusOK, booking)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;wait, there is a new struct for updates, Yes. that's because we have already added validations for bookings modal as both &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; are required, but for an update we might only need to update one of it not both. other than that this enables adding custom validations only for update request if needed.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;routes/booking.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    router.PUT("v1/bookings/:id", controllers.UpdateBooking)
    router.DELETE("v1/bookings/:id", controllers.DeleteBooking)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/chamupathi/go-gin-REST-API/tree/05-update-delete-operations" rel="noopener noreferrer"&gt;here&lt;/a&gt; is the code after adding delete and update operations.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  Congratulations! You’ve built a fully functional RESTful API in Go with: ✅ Gin for routing
&lt;/h3&gt;

&lt;p&gt;✅ PostgreSQL (via Docker) for storage&lt;br&gt;
✅ GORM for database interactions&lt;br&gt;
✅ CRUD operations&lt;/p&gt;




&lt;p&gt;Hope you enjoyed this guide! Let me know your thoughts in the comments. 🚀&lt;/p&gt;

</description>
      <category>restapi</category>
      <category>go</category>
      <category>gin</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
