<?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>Forem: Shivam Pratap Singh</title>
    <description>The latest articles on Forem by Shivam Pratap Singh (@moodydev).</description>
    <link>https://forem.com/moodydev</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%2F3213270%2F3efd5f80-4e7b-4370-bc77-05535c89ee9a.jpg</url>
      <title>Forem: Shivam Pratap Singh</title>
      <link>https://forem.com/moodydev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/moodydev"/>
    <language>en</language>
    <item>
      <title>go webdev series</title>
      <dc:creator>Shivam Pratap Singh</dc:creator>
      <pubDate>Fri, 30 May 2025 11:16:25 +0000</pubDate>
      <link>https://forem.com/moodydev/go-webdev-series-l3a</link>
      <guid>https://forem.com/moodydev/go-webdev-series-l3a</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/moodydev/gin-blog-app-part-1-1443" class="crayons-story__hidden-navigation-link"&gt;Gin Blog App Part 1&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/moodydev" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3213270%2F3efd5f80-4e7b-4370-bc77-05535c89ee9a.jpg" alt="moodydev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/moodydev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Shivam Pratap Singh
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Shivam Pratap Singh
                
              
              &lt;div id="story-author-preview-content-2533095" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/moodydev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3213270%2F3efd5f80-4e7b-4370-bc77-05535c89ee9a.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Shivam Pratap Singh&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/moodydev/gin-blog-app-part-1-1443" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 27 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/moodydev/gin-blog-app-part-1-1443" id="article-link-2533095"&gt;
          Gin Blog App Part 1
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/go"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;go&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/backend"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;backend&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/moodydev/gin-blog-app-part-1-1443" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/moodydev/gin-blog-app-part-1-1443#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>go</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>go web dev series</title>
      <dc:creator>Shivam Pratap Singh</dc:creator>
      <pubDate>Fri, 30 May 2025 11:15:46 +0000</pubDate>
      <link>https://forem.com/moodydev/go-web-dev-series-5bdi</link>
      <guid>https://forem.com/moodydev/go-web-dev-series-5bdi</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/moodydev/gin-blog-app-part-2-4ca9" class="crayons-story__hidden-navigation-link"&gt;Gin Blog App Part 2&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/moodydev" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3213270%2F3efd5f80-4e7b-4370-bc77-05535c89ee9a.jpg" alt="moodydev profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/moodydev" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Shivam Pratap Singh
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Shivam Pratap Singh
                
              
              &lt;div id="story-author-preview-content-2544730" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/moodydev" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3213270%2F3efd5f80-4e7b-4370-bc77-05535c89ee9a.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Shivam Pratap Singh&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/moodydev/gin-blog-app-part-2-4ca9" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 30 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/moodydev/gin-blog-app-part-2-4ca9" id="article-link-2544730"&gt;
          Gin Blog App Part 2
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/go"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;go&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/moodydev/gin-blog-app-part-2-4ca9#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>programming</category>
      <category>go</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Gin Blog App Part 2</title>
      <dc:creator>Shivam Pratap Singh</dc:creator>
      <pubDate>Fri, 30 May 2025 11:11:26 +0000</pubDate>
      <link>https://forem.com/moodydev/gin-blog-app-part-2-4ca9</link>
      <guid>https://forem.com/moodydev/gin-blog-app-part-2-4ca9</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In Part 1, we built user authentication and JWT middleware. Now in Part 2, we’ll implement CRUD operations for blog posts, each protected by JWT authentication. Only the post owner can update/delete their post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Model for Blog Posts
&lt;/h2&gt;

&lt;p&gt;models/post.go&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

type Post struct {
    ID        uint   `json:"id" gorm:"primaryKey"`
    Title     string `json:"title"`
    Content   string `json:"content"`
    UserID    uint   `json:"user_id"`
    CreatedAt time.Time
}

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

&lt;/div&gt;



&lt;p&gt;Make sure this model is auto-migrated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.AutoMigrate(&amp;amp;models.Post{})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create Blog Post
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func CreatePost(c *gin.Context) {
    var post models.Post

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

    // Check if user exists
    var user models.User
    if err := config.DB.First(&amp;amp;user, post.UserID).Error; err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
        return
    }

    if err := config.DB.Create(&amp;amp;post).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create post"})
        return
    }

    c.JSON(http.StatusCreated, post)
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Get All Posts &amp;amp; Single Post
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
type PostResponse struct {
    ID      uint       `json:"id"`
    Title   string     `json:"title"`
    Content string     `json:"content"`
    User    SimpleUser `json:"user"`
}

// SimpleUser represents a simplified user structure for responses
type SimpleUser struct {
    ID       uint   `json:"id"`
    Username string `json:"username"`
}

func GetPosts(c *gin.Context) {
    var posts []models.Post
    if err := config.DB.Preload("User").Find(&amp;amp;posts).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch posts"})
        return
    }

    // PostResponse represents the response structure for a post with user info

    var responsePosts []PostResponse
    for _, post := range posts {
        responsePosts = append(responsePosts, PostResponse{
            ID:      uint(post.ID),
            Title:   post.Title,
            Content: post.Content,
            User: SimpleUser{
                ID:       post.User.ID,
                Username: post.User.Username,
            },
        })
    }

    c.JSON(http.StatusOK, responsePosts)
}

func GetPost(c *gin.Context) {
    idParam := c.Param("id")
    if idParam == "" {
        c.JSON(http.StatusBadRequest, gin.H{"error": "ID parameter is required"})
        return
    }

    id, err := strconv.Atoi(idParam)
    if err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
        return
    }

    var post models.Post
    if err := config.DB.Preload("User").First(&amp;amp;post, id).Error; err != nil {
        c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"})
        return
    }

    response := PostResponse{
        ID:      uint(post.ID),
        Title:   post.Title,
        Content: post.Content,
        User: SimpleUser{
            ID:       post.User.ID,
            Username: post.User.Username,
        },
    }

    c.JSON(http.StatusOK, response)
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Update Blog Post
&lt;/h2&gt;

&lt;p&gt;Only the owner can update the post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func UpdatePost(c *gin.Context) {
    id, err := strconv.Atoi(c.Param("id"))
    if err != nil || id &amp;lt;= 0 {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid post ID"})
        return
    }

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

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

    if post.UserID != updatedPost.UserID {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "You are not the author of this post"})
        return
    }

    // Update the fields
    post.Title = updatedPost.Title
    post.Content = updatedPost.Content

    if err := config.DB.Save(&amp;amp;post).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update post"})
        return
    }

    c.JSON(http.StatusOK, post)
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Delete Blog Post
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func DeletePost(c *gin.Context) {
    id, err := strconv.Atoi(c.Param("id"))
    if err != nil || id &amp;lt;= 0 {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid post ID"})
        return
    }

    var payload struct {
        UserID uint `json:"user_id"`
    }
    if err := c.ShouldBindJSON(&amp;amp;payload); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }

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

    if post.UserID != payload.UserID {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "You are not the author of this post"})
        return
    }

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

    c.JSON(http.StatusOK, gin.H{"message": "Post deleted"})
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Protecting Routes with JWT
&lt;/h2&gt;

&lt;p&gt;Update your routes/routes.go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auth := router.Group("/api")
auth.Use(middleware.JWTAuthMiddleware())

auth.POST("/posts", handlers.CreatePost)
auth.PUT("/posts/:id", handlers.UpdatePost)
auth.DELETE("/posts/:id", handlers.DeletePost)

// Public routes
router.GET("/posts", handlers.GetPosts)
router.GET("/posts/:id", handlers.GetPost)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the Endpoints
&lt;/h2&gt;

&lt;p&gt;Use Postman or ThunderClient:&lt;/p&gt;

&lt;p&gt;POST /api/posts → Add Bearer Token or Cookie&lt;br&gt;
GET /posts → Public&lt;br&gt;
PUT/DELETE /api/posts/:id → Must be owner&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Part Preview
&lt;/h2&gt;

&lt;p&gt;In Part 3&lt;/p&gt;

&lt;p&gt;Search &amp;amp; filter by title/user&lt;br&gt;
Pagination&lt;br&gt;
User-specific blog dashboard&lt;br&gt;
Deployment tips&lt;/p&gt;

&lt;p&gt;Follow the journey on &lt;a href="https://dev.to/moodydev"&gt;Dev.to&lt;/a&gt;, &lt;a href="https://medium.com/@shivam.forapp" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;, and &lt;a href="https://x.com/moodydev" rel="noopener noreferrer"&gt;X&lt;/a&gt;. and &lt;a href="https://x.com/AiMlDsDlEngg" rel="noopener noreferrer"&gt;X&lt;/a&gt; -&amp;gt; dont spam here i'm doin fun.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>go</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Gin Blog App Part 1</title>
      <dc:creator>Shivam Pratap Singh</dc:creator>
      <pubDate>Tue, 27 May 2025 17:15:30 +0000</pubDate>
      <link>https://forem.com/moodydev/gin-blog-app-part-1-1443</link>
      <guid>https://forem.com/moodydev/gin-blog-app-part-1-1443</guid>
      <description>&lt;p&gt;Hey, I'm Shivam, I develop WebApps using NodeJs, Golang and sometimes Django too. Here I'm writing about how to use Gin and res-api-framework in Golang. Gin in popular golang frame work because of it's built-in fuctionality. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What This Part Will Cover:&lt;/li&gt;
&lt;li&gt;Introduction to the series and what the final app will do&lt;/li&gt;
&lt;li&gt;Project setup (Go modules, folder structure)&lt;/li&gt;
&lt;li&gt;Installing Gin and other necessary packages&lt;/li&gt;
&lt;li&gt;Setting up SQLite with GORM&lt;/li&gt;
&lt;li&gt;Creating models (User, maybe Post as a preview)&lt;/li&gt;
&lt;li&gt;Implementing user signup and login with password hashing&lt;/li&gt;
&lt;li&gt;Basic testing using Postman or curl&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this series I will explain everyting from setup to implementation, for coding part I will post in 3 parts and for explaination of code it will be a single and last part. this is a simple blog app where user can post, update and delete their post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;p&gt;create a folder stucture like this:&lt;/p&gt;

&lt;p&gt;gin-blog-api/&lt;br&gt;
│&lt;br&gt;
├── go.mod&lt;br&gt;
├── go.sum&lt;br&gt;
├── main.go&lt;br&gt;
│&lt;br&gt;
├── .env&lt;br&gt;
│&lt;br&gt;
├── config/&lt;br&gt;
│   └── db.go&lt;br&gt;
│&lt;br&gt;
├── middleware/&lt;br&gt;
│   └── middleware.go&lt;br&gt;
│&lt;br&gt;
├── models/&lt;br&gt;
│   └── user.go &lt;br&gt;
│&lt;br&gt;
├── handlers/&lt;br&gt;
│   └── auth.go&lt;br&gt;
│&lt;br&gt;
├── routes/&lt;br&gt;
│   └── routes.go&lt;br&gt;
│&lt;br&gt;
├── utils/&lt;br&gt;
│   └── jwt.go             &lt;/p&gt;

&lt;p&gt;initialize go module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod init gin-blog-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing Gin and other necessary packages
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get github.com/gin-gonic/gin
go get gorm.io/gorm
go get gorm.io/driver/sqlite
go get golang.org/x/crypto/bcrypt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting up SQLite with GORM
&lt;/h2&gt;

&lt;p&gt;add followig in config/database.go&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 (
    "gin-blog-api/models"
    "log"

    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

var DB *gorm.DB

func ConnectDatabase() {
    database, err := gorm.Open(sqlite.Open("blog.db"), &amp;amp;gorm.Config{})
    if err != nil {
        log.Fatal("Failed to connect to database!", err)
    }

    err = database.AutoMigrate(&amp;amp;models.Post{})
    if err != nil {
        log.Fatal("Migration failed!", err)
    }

    database.AutoMigrate(&amp;amp;models.Post{}, &amp;amp;models.User{})

    DB = database
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating models
&lt;/h2&gt;

&lt;p&gt;add following in models/users.go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type User struct {
    ID       uint   `gorm:"primaryKey"`
    Username string `gorm:"unique"`
    Email    string `gorm:"unique"`
    Password string `gorm:"not null"`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing user signup and login with password hashing
&lt;/h2&gt;

&lt;p&gt;add this in handlers/auth.go&lt;br&gt;
&lt;/p&gt;

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

import (
    "fmt"
    "gin-blog-api/config"
    "gin-blog-api/models"
    "net/http"

    "gin-blog-api/utils"

    "github.com/gin-gonic/gin"
    "golang.org/x/crypto/bcrypt"
)

func Signup(c *gin.Context) {
    var user models.User

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

    fmt.Println("RAW password received:", user.Password)

    if user.Password == "" {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Password is required"})
        return
    }

    hashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not hash password"})
        return
    }
    user.Password = string(hashedPassword)

    if err := config.DB.Create(&amp;amp;user).Error; err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create user"})
        return
    }

    c.JSON(http.StatusCreated, gin.H{"message": "User created successfully"})
}

func Login(c *gin.Context) {
    var loginData struct {
        Email    string `json:"email"`
        Password string `json:"password"`
    }
    var user models.User

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

    if err := config.DB.Where("email = ?", loginData.Email).First(&amp;amp;user).Error; err != nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
        return
    }

    if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginData.Password)); err != nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
        return
    }

    token, err := utils.GenerateJWT(user.ID)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
        return
    }

    c.SetCookie("token", token, 24*3600, "/", "localhost", false, true)

    c.JSON(http.StatusOK, gin.H{
        "token": token,
        "user": gin.H{
            "id":       user.ID,
            "username": user.Username,
            "email":    user.Email,
        },
    })

}

func Logout(c *gin.Context) {
    // For stateless JWT authentication, logout is handled on the client side.
    // Here we can just return a success message.
    c.SetCookie("token", "", -1, "/", "localhost", false, true) // Clear the cookie
    c.JSON(http.StatusOK, gin.H{"message": "Logged out successfully"})
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  creating JWT token
&lt;/h3&gt;

&lt;p&gt;utils/jwt.go&lt;br&gt;
&lt;/p&gt;

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

import (
    "fmt"
    "log"
    "os"
    "time"

    "github.com/golang-jwt/jwt/v5"
)

func GenerateJWT(userID uint) (string, error) {

    claims := jwt.MapClaims{
        "user_id": userID,
        "exp":     time.Now().Add(24 * time.Hour).Unix(), // 1 day expiry
    }

    log.Println("JWT_SECRET during sign:", os.Getenv("JWT_SECRET"))
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

    return token.SignedString([]byte(os.Getenv("JWT_SECRET")))
}

func ParseJWT(tokenStr string) (uint, error) {

    token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
        return []byte(os.Getenv("JWT_SECRET")), nil
    })

    if err != nil {
        log.Println("JWT parse error:", err)
        return 0, fmt.Errorf("invalid or expired token")
    }

    if !token.Valid {
        log.Println("JWT invalid token")
        return 0, fmt.Errorf("invalid or expired token")
    }

    claims, ok := token.Claims.(jwt.MapClaims)
    if !ok {
        log.Println("JWT claims cast failed")
        return 0, fmt.Errorf("invalid token claims")
    }

    uidFloat, ok := claims["user_id"].(float64)
    if !ok {
        log.Println("user_id missing in claims")
        return 0, fmt.Errorf("user_id not found in token")
    }
    log.Println("Parsed user_id:", uidFloat)

    // save the user_id in context for later use
    if uidFloat &amp;lt;= 0 {
        log.Println("Invalid user_id in claims:", uidFloat)
        return 0, fmt.Errorf("invalid user_id in token")
    }

    return uint(uidFloat), nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Main server file
&lt;/h2&gt;

&lt;p&gt;/main.go&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 (
    "gin-blog-api/config"
    "gin-blog-api/routes"

    "github.com/gin-gonic/gin"
    "github.com/joho/godotenv"
    "log"
)

func init() {
    err := godotenv.Load()
    if err != nil {
        log.Fatal("Error loading .env file")
    }
}

func main() {
    r := gin.Default()
    // Connect to the database
    config.ConnectDatabase()
    // Register routes
    routes.RegisterRoutes(r)
    // Start the server
    // Listen and serve on
    // http://localhost:8080

    r.Run(":8080")
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Routing
&lt;/h2&gt;

&lt;p&gt;routes/routes.go&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 (
    "gin-blog-api/handlers"
    "gin-blog-api/middleware"

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

func RegisterRoutes(router *gin.Engine) {
    router.POST("/signup", handlers.Signup)
    router.POST("/login", handlers.Login)

    router.GET("/posts", handlers.GetPosts)
    router.GET("/posts/:id", handlers.GetPostByID)

    auth := router.Group("/")
    auth.Use(middleware.JWTAuthMiddleware())
    {
        auth.POST("/posts", handlers.CreatePost)
        auth.PUT("/posts/:id", handlers.UpdatePost)
        auth.DELETE("/posts/:id", handlers.DeletePost)
        router.POST("/logout", handlers.Logout)

    }
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  middleware to protect routes
&lt;/h2&gt;

&lt;p&gt;middleware/auth.go&lt;br&gt;
&lt;/p&gt;

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

import (
    "gin-blog-api/utils"
    "net/http"
    "strings"

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

func JWTAuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Authorization header missing or invalid"})
            return
        }

        tokenString := strings.TrimPrefix(authHeader, "Bearer ")

        userID, err := utils.ParseJWT(tokenString)
        if err != nil {
            c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
            return
        }

        // Attach user ID to context
        c.Set("userID", userID)
        c.Next()
    }
}

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  add .env
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JWT_SECRET=9ad4404902df5dde56b33bb7857e085b24b1903187d4a1b3d7c1b1526d4da4dba7de59dacb68424f52325c1e606b700653eab5c722da394d60b3d81c9de503a8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Basic testing using Postman or curl
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8080/signup/
{
  "username":"codey-singh",
  "email":"ssp@yopmail.com",
  "password":"qwert1234"
}
response: 
{
  "message": "User created successfully"
}


http://localhost:8080/login/
{
  "email":"ssp@yopmail.com",
  "password":"qwert1234"
}
response:
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJleHAiOjE3NDg0NDk0ODEsInVzZXJfaWQiOjM3MjAwMX0.
hgI8nM5RrnnAkpbOfgM4cd0hdIXI9tCVfQAnAn5ahcc",
  "user": {
    "email": "ssp@yopmail.com",
    "id": 372001,
    "username": "codey-singh"
  }
}


http://localhost:8080/protected/
add: bearer + token in auth postman
response:
{
  "message": "Protected content",
  "user": 372001
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run command
&lt;/h2&gt;


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

&lt;/div&gt;
&lt;h1&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  what is in next part:&lt;br&gt;
&lt;/h1&gt;

&lt;p&gt;In next part i will setup a post model and functionality&lt;br&gt;
In third part i will setup a user dashboard&lt;br&gt;
and in final part i will proivde full code explaination &lt;br&gt;
full github code will be public in third part.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>go</category>
      <category>backend</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
