
Building REST APIs in Go: A Practical Guide
Setting Up the Project
Before diving into code, ensure Go is installed on your system. You can verify the installation with:
go versionCreate a new Go module:
go mod init rest-api-exampleNext, install the Gin framework:
go get -u github.com/gin-gonic/ginBasic 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.goVisit 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/3Performance 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.
| Feature | In-Memory Store | Database (e.g. PostgreSQL) |
|---|---|---|
| Speed | \fast | \slightly slower |
| Persistence | No | Yes |
| Scalability | Low | High |
| Data Consistency | No | Yes |
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.
