Setting Up the Project

Before diving into code, ensure Go is installed on your system. You can verify the installation with:

go version

Create a new Go module:

go mod init rest-api-example

Next, install the Gin framework:

go get -u github.com/gin-gonic/gin

Basic Server Setup

Start by creating a basic HTTP server that responds with a simple message. This will serve as the foundation for our API.

package main

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

func main() {
    r := gin.Default()
    r.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "Welcome to the Go REST API",
        })
    })
    r.Run(":8080")
}

Save this as main.go and run it using:

go run main.go

Visit http://localhost:8080 in your browser to see the response.

Modeling the User Resource

To manage users, we'll define a User struct and a slice to hold user data in memory.

type User struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

We'll maintain a slice of users and use a simple in-memory store for this example.

var users = []User{
    {ID: "1", Name: "Alice", Email: "[email protected]"},
    {ID: "2", Name: "Bob", Email: "[email protected]"},
}

CRUD Endpoints

Create a User

To create a user, we define a POST endpoint that accepts JSON data and appends the user to the users slice.

r.POST("/users", func(c *gin.Context) {
    var newUser User
    if err := c.ShouldBindJSON(&newUser); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    users = append(users, newUser)
    c.JSON(201, newUser)
})

Read All Users

This endpoint returns all users in the slice.

r.GET("/users", func(c *gin.Context) {
    c.JSON(200, users)
})

Read a Single User

This endpoint retrieves a user by ID, using Go’s strings package to match the ID.

r.GET("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    for _, user := range users {
        if user.ID == id {
            c.JSON(200, user)
            return
        }
    }
    c.JSON(404, gin.H{"error": "User not found"})
})

Update a User

The update endpoint allows modifying a user’s information.

r.PUT("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    var updatedUser User
    if err := c.ShouldBindJSON(&updatedUser); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    for i, user := range users {
        if user.ID == id {
            users[i] = updatedUser
            c.JSON(200, users[i])
            return
        }
    }
    c.JSON(404, gin.H{"error": "User not found"})
})

Delete a User

The delete endpoint removes a user from the slice.

r.DELETE("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    for i, user := range users {
        if user.ID == id {
            users = append(users[:i], users[i+1:]...)
            c.JSON(204, gin.H{})
            return
        }
    }
    c.JSON(404, gin.H{"error": "User not found"})
})

Error Handling and Middleware

Gin provides built-in middleware for logging and recovery. We used gin.Default(), which includes both. For better control, you can use gin.New() and add middleware manually.

r.Use(gin.Logger())
r.Use(gin.Recovery())

Testing the API

You can test the API using tools like curl or Postman. Here are a few example requests:

  • GET all users
  curl http://localhost:8080/users
  • POST a new user
  curl -X POST http://localhost:8080/users -d '{"id": "3", "name": "Charlie", "email": "[email protected]"}' -H "Content-Type: application/json"
  • GET a user by ID
  curl http://localhost:8080/users/3
  • PUT to update a user
  curl -X PUT http://localhost:8080/users/3 -d '{"id": "3", "name": "Charlie Smith", "email": "[email protected]"}' -H "Content-Type: application/json"
  • DELETE a user
  curl -X DELETE http://localhost:8080/users/3

Performance Considerations

Using an in-memory store is fine for development and testing but not for production. In real-world applications, consider using a database like PostgreSQL or MongoDB. Go has robust drivers for both.

FeatureIn-Memory StoreDatabase (e.g. PostgreSQL)
Speed\fast\slightly slower
PersistenceNoYes
ScalabilityLowHigh
Data ConsistencyNoYes

Conclusion

This tutorial demonstrated how to build a REST API in Go using the Gin framework. You learned to implement CRUD operations, manage data structures, and handle HTTP requests. While this example uses an in-memory store, you can easily extend it to use a real database for production use.


Learn more with useful resources