Building a Secure REST API with GORM, Echo, JWT Token Authentication, and PostgreSQL in Go.

Combining GORM, a robust ORM library for Go, with Echo, a high-performance web framework, along with JWT token-based authentication and PostgreSQL, offers a solid foundation for developing secure and scalable RESTful APIs. In this detailed tutorial, we'll create a practical example showcasing the integration of GORM, Echo, JWT token authentication, and PostgreSQL to build a fully functional and secure REST API in Go.

Prerequisites

Ensure you have the following prerequisites:

  • Go installed on your machine

  • PostgreSQL installed and running

  • Basic knowledge of Go, SQL, and RESTful APIs

Setting Up the Project

Initializing the Project

Start by initializing a new Go module:

go mod init your_project_name

Install the required dependencies:

go get -u github.com/labstack/echo/v4
go get -u github.com/labstack/echo/v4/middleware
go get -u github.com/dgrijalva/jwt-go
go get -u github.com/jinzhu/gorm
go get -u github.com/jinzhu/gorm/dialects/postgres

Creating Database

Create a PostgreSQL database and define a users table with columns id, username, password, and email.

Building the Secure REST API

Setting Up Echo Server

Create a new Go file, e.g., main.go, and set up the Echo server:

package main

import (
    "time"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    "github.com/dgrijalva/jwt-go"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres"
)

var (
    db *gorm.DB
)

func init() {
    var err error
    dsn := "postgresql://user:password@localhost/database_name?sslmode=disable" // Update with your database credentials
    db, err = gorm.Open("postgres", dsn)
    if err != nil {
        panic("failed to connect to database")
    }

    // Auto migrate the User model
    db.AutoMigrate(&User{})
}

type User struct {
    gorm.Model
    Username string `json:"username"`
    Password string `json:"password"`
    Email    string `json:"email"`
}

func login(c echo.Context) error {
    username := c.FormValue("username")
    password := c.FormValue("password")

    var user User
    if err := db.Where("username = ?", username).First(&user).Error; err != nil {
        return echo.ErrUnauthorized
    }

    if user.Password != password {
        return echo.ErrUnauthorized
    }

    // Create JWT token
    token := jwt.New(jwt.SigningMethodHS256)
    claims := token.Claims.(jwt.MapClaims)
    claims["username"] = user.Username
    claims["exp"] = time.Now().Add(time.Hour * 24).Unix()

    t, err := token.SignedString([]byte("secret")) // Replace with your secret key
    if err != nil {
        return err
    }

    return c.JSON(200, map[string]string{
        "token": t,
    })
}

func restricted(c echo.Context) error {
    user := c.Get("user").(*jwt.Token)
    claims := user.Claims.(jwt.MapClaims)
    username := claims["username"].(string)
    return c.String(200, "Welcome "+username+"!")
}

func main() {
    e := echo.New()

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // Routes
    e.POST("/login", login)
    r := e.Group("/restricted")
    r.Use(middleware.JWT([]byte("secret"))) // Replace with your secret key
    r.GET("", restricted)

    // Start server
    e.Logger.Fatal(e.Start(":8080"))
}

Implementing JWT Token Authentication

Login Endpoint

The POST /login endpoint authenticates users and generates a JWT token:

func login(c echo.Context) error {
    // Authenticating user credentials
    // ...

    // Creating JWT token
    token := jwt.New(jwt.SigningMethodHS256)
    claims := token.Claims.(jwt.MapClaims)
    claims["username"] = user.Username
    claims["exp"] = time.Now().Add(time.Hour * 24).Unix()

    t, err := token.SignedString([]byte("secret")) // Replace with your secret key
    if err != nil {
        return err
    }

    return c.JSON(200, map[string]string{
        "token": t,
    })
}

Restricted Endpoint

The GET /restricted endpoint is restricted and requires a valid JWT token for access:

func restricted(c echo.Context) error {
    user := c.Get("user").(*jwt.Token)
    claims := user.Claims.(jwt.MapClaims)
    username := claims["username"].(string)
    return c.String(200, "Welcome "+username+"!")
}

Registering API Endpoints

Add the new API endpoints to the Echo server:

func main() {
    e := echo.New()

    // Middleware
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // Routes
    e.POST("/login", login)
    r := e.Group("/restricted")
    r.Use(middleware.JWT([]byte("secret"))) // Replace with your secret key
    r.GET("", restricted)

    // Start server
    e.Logger.Fatal(e.Start(":8080"))
}

Testing the Secure API

Build and run the application:

go run main.go

Testing Endpoints

Conclusion

The integration of GORM with Echo, along with JWT token-based authentication and PostgreSQL, enables the creation of secure and scalable RESTful APIs in Go. This example demonstrates the implementation of JWT token authentication to secure endpoints and provides a foundation for building authentication-based APIs in Go.

By extending this example, developers can implement user registration, authorization, token expiration, and further functionalities, leveraging the capabilities of GORM, Echo, and JWT tokens to build robust and secure APIs in Go.

I hope this helps, you!!

More such articles:

https://medium.com/techwasti

https://www.youtube.com/@maheshwarligade

https://techwasti.com/series/spring-boot-tutorials

https://techwasti.com/series/go-language

0
Subscribe to my newsletter

Read articles from Maheshwar Ligade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maheshwar Ligade
Maheshwar Ligade

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI