Back

Go Lang Basics Part 2

Part 2 Building REST APIs with Fiber in Go: A Detailed Guide with JWT Authentication

Prerequisites:

  • Basic understanding of Go programming
  • Familiarity with HTTP concepts

Step 1: Project Setup

  1. Open your terminal or command prompt.
  2. Create a project directory:
    mkdir go-fiber-api
    cd go-fiber-api
    
  3. Initialize a Go module:
    go mod init github.com/your-username/go-fiber-api
    
    • Replace github.com/your-username/go-fiber-api with the appropriate module path for your project.

Step 2: Folder Structure

Create these subfolders inside go-fiber-api:

  • controllers
  • models
  • middleware
  • utils

Step 3: Install Dependencies

go get -u github.com/gofiber/fiber/v2
  • Install other dependencies as needed (database drivers, JWT library, etc.)

Step 4: Models (models/product.go)

package models

type Product struct {
    ID    int     `json:"id"`
    Name  string  `json:"name"`
    Price float64 `json:"price"`
}

Step 5: Controllers (controllers/products.go)

package controllers

import (
    "fmt"
    "github.com/gofiber/fiber/v2"
    "go-fiber-api/models"
)

// In-memory product data (replace with database later)
var products []models.Product = []models.Product{
    {ID: 1, Name: "T-Shirt", Price: 19.99},
    {ID: 2, Name: "Coffee Mug", Price: 9.99},
}

// GetProducts fetches all products
func GetProducts(c *fiber.Ctx) error {
    return c.JSON(products)
}

// GetProductByID fetches a product by ID
func GetProductByID(c *fiber.Ctx) error {
    id, err := c.ParamsInt("id")
    if err != nil {
        return c.Status(400).SendString("Invalid product ID")
    }

    for _, product := range products {
        if product.ID == id {
            return c.JSON(product)
        }
    }

    return c.Status(404).SendString("Product not found")
}

Step 6: Routing (main.go)

package main

import (
    "github.com/gofiber/fiber/v2"
    "go-fiber-api/controllers"
)

func main() {
    app := fiber.New()

    api := app.Group("/api")
    api.Get("/products", controllers.GetProducts)
    api.Get("/products/:id", controllers.GetProductByID)

    app.Listen(":3000")
}

Step 7: Run the Application

go run main.go

Test by visiting http://localhost:3000/api/products in your browser.

Part 3 JWT Authentication

  1. Install the JWT library:
    go get -u github.com/golang-jwt/jwt/v4
    
  2. Create an authentication middleware function (middleware/auth.go)
package middleware

import (
    "fmt"
    "github.com/dgrijalva/jwt-go/v4"
    "github.com/gofiber/fiber/v2"
)

// Change this to a secure secret key
var jwtSecret = []byte("your_strong_secret_key")

// Authenticate middleware checks for a valid JWT
func Authenticate(c *fiber.Ctx) error {
    tokenString := c.Get("Authorization")
    tokenString = strings.Replace(tokenString, "Bearer ", "", 1) // Remove 'Bearer' prefix

    // Parse the JWT token
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        // Ensure signing method matches expected
        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
        }

        // Return the secret key for validation
        return jwtSecret, nil
    })

    if err != nil {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
            "message": "Invalid or expired token",
        })
    }

    // Extract user data from token (requires defining claims)
    claims, ok := token.Claims.(jwt.MapClaims)
    if !ok || !token.Valid {
        return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
            "message": "Invalid token",
        })
    }

    // Set user data in request context for later access
    c.Locals("userId", claims["userId"]) // Example, adjust as necessary

    return c.Next()
}

3. Generate JWTs During Login (Example - Simplify as needed)

// ... (in your login controller )

// Simulate successful login
userId := 1 // Replace with actual login logic

// Create JWT claims (add more user data if needed)
claims := jwt.MapClaims{
    "userId": userId,
    "exp":    time.Now().Add(time.Hour * 24).Unix(),  // Example 24-hour expiration
}

// Generate token with HMAC-SHA256 signing method
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtSecret)
if err != nil {
    // Handle error
}

// Return the generated token to the client
return c.JSON(fiber.Map{
    "token": tokenString,
})

4. Protect Routes (main.go)

// ... in your main file ...

app.Use("/api/protected", Authenticate) // Apply middleware to group or specific routes

api.Get("/protected/data", func(c *fiber.Ctx) error {
    userId := c.Locals("userId") // Access authenticated user's ID
    return c.JSON(fiber.Map{
        "message": "Access granted!",
        "userId":  userId,
    })
})

Explanation

  • Authenticate Middleware:
    • Extracts the JWT from the Authorization header.
    • Verifies the token's signature and validity.
    • Extracts user data from the JWT claims (you'll need to define appropriate claim fields).
  • Login Example:
    • Replace with your authentication logic.
    • Creates JWT claims containing user info and expiration.
    • Signs the token with your secret key.
  • Protected Route:
    • The middleware ensures only requests with valid JWTs can access protected routes.

Important:

  • Secure Secret Key: Use a strong, unpredictable secret key and store it securely.
  • Error Handling: Implement robust error handling.
  • Custom Claims: Design JWT claims to hold necessary user information for your application.