Gin Status Codes
Introduction
When building web applications or APIs with Gin, communicating the result of a request is essential for both clients and developers. HTTP status codes are standardized numerical codes that indicate the outcome of an HTTP request. In Gin, handling these status codes properly ensures your API behaves predictably and follows web standards.
This guide will walk you through working with HTTP status codes in the Gin framework, explaining how to set them correctly and use them effectively to communicate with clients consuming your API.
Understanding HTTP Status Codes
HTTP status codes are three-digit numbers grouped into five categories:
- 1xx (Informational): The request was received and the process is continuing
- 2xx (Success): The request was successfully received, understood, and accepted
- 3xx (Redirection): Further action is needed to complete the request
- 4xx (Client Error): The request contains bad syntax or cannot be fulfilled
- 5xx (Server Error): The server failed to fulfill a valid request
In Gin, you have full control over which status codes to send back to clients.
Setting Status Codes in Gin
Basic Usage
The simplest way to set a status code in Gin is using the c.Status()
method:
func handleRequest(c *gin.Context) {
// Set HTTP status code to 200 (OK)
c.Status(http.StatusOK)
}
More commonly, you'll combine the status code with a response body:
func handleRequest(c *gin.Context) {
// Set status and return JSON response
c.JSON(http.StatusOK, gin.H{
"message": "Operation successful",
})
}
Common Status Code Methods
Gin provides several convenience methods for common status codes:
func handleResponse(c *gin.Context) {
// Success responses
c.JSON(http.StatusOK, gin.H{"message": "Success"}) // 200
c.JSON(http.StatusCreated, gin.H{"message": "Resource created"}) // 201
// Client error responses
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"}) // 400
c.JSON(http.StatusUnauthorized, gin.H{"error": "Please login"}) // 401
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"}) // 403
c.JSON(http.StatusNotFound, gin.H{"error": "Resource not found"}) // 404
// Server error responses
c.JSON(http.StatusInternalServerError, gin.H{"error": "Server error"}) // 500
}
Practical Examples
Example 1: Basic CRUD API with Status Codes
Here's how you might implement a simple user API with appropriate status codes:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type User struct {
ID string `json:"id"`
Name string `json:"name"`
}
var users = []User{
{ID: "1", Name: "Alice"},
{ID: "2", Name: "Bob"},
}
func main() {
router := gin.Default()
// GET all users
router.GET("/users", func(c *gin.Context) {
c.JSON(http.StatusOK, users)
})
// GET a specific user
router.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
for _, user := range users {
if user.ID == id {
c.JSON(http.StatusOK, user)
return
}
}
c.JSON(http.StatusNotFound, gin.H{
"error": "User not found",
})
})
// POST a new user
router.POST("/users", func(c *gin.Context) {
var newUser User
if err := c.BindJSON(&newUser); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid user data",
})
return
}
// Check if ID is provided
if newUser.ID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "User ID is required",
})
return
}
users = append(users, newUser)
c.JSON(http.StatusCreated, newUser)
})
router.Run(":8080")
}
Example 2: Custom Error Handling
For more consistent error handling, you can create helper functions:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Error response structure
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
}
// Success response structure
type SuccessResponse struct {
Code int `json:"code"`
Data interface{} `json:"data"`
}
// Helper function for error responses
func respondWithError(c *gin.Context, code int, message string) {
c.JSON(code, ErrorResponse{
Code: code,
Message: message,
})
}
// Helper function for success responses
func respondWithSuccess(c *gin.Context, data interface{}) {
c.JSON(http.StatusOK, SuccessResponse{
Code: http.StatusOK,
Data: data,
})
}
func main() {
router := gin.Default()
router.GET("/products/:id", func(c *gin.Context) {
id := c.Param("id")
// Simulating database lookup failure
if id == "999" {
respondWithError(c, http.StatusInternalServerError, "Database error")
return
}
// Simulating product not found
if id == "0" {
respondWithError(c, http.StatusNotFound, "Product not found")
return
}
// Return mock product data
product := gin.H{
"id": id,
"name": "Sample Product",
"price": 29.99,
}
respondWithSuccess(c, product)
})
router.Run(":8080")
}
Example 3: Authentication Status Codes
Here's how you might handle authentication with appropriate status codes:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// Login endpoint
router.POST("/login", func(c *gin.Context) {
var loginData struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := c.BindJSON(&loginData); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid request body",
})
return
}
// Simple validation example (in real apps, use proper auth)
if loginData.Username == "" || loginData.Password == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Username and password are required",
})
return
}
// Check credentials (simplified example)
if loginData.Username == "admin" && loginData.Password == "secret" {
c.JSON(http.StatusOK, gin.H{
"message": "Login successful",
"token": "sample-jwt-token",
})
} else {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "Invalid credentials",
})
}
})
// Protected endpoint
router.GET("/admin", func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "Authentication required",
})
return
}
if token != "Bearer sample-jwt-token" {
c.JSON(http.StatusForbidden, gin.H{
"error": "Access denied",
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "Welcome to the admin panel",
})
})
router.Run(":8080")
}
Common Status Codes in Web APIs
Here's a reference for commonly used status codes in RESTful APIs:
Code | Status | Description | When to Use |
---|---|---|---|
200 | OK | Success | When request has succeeded |
201 | Created | Resource created | After POST that creates a new resource |
204 | No Content | Success with no content | DELETE operations or updates that return no content |
400 | Bad Request | Invalid input | When request has invalid syntax |
401 | Unauthorized | Authentication required | When authentication is missing or invalid |
403 | Forbidden | Permission denied | When authenticated user lacks required permissions |
404 | Not Found | Resource not found | When requested resource doesn't exist |
405 | Method Not Allowed | HTTP method not allowed | When HTTP method is not supported for the resource |
409 | Conflict | Request conflicts with server state | When there's a conflict with resource state |
422 | Unprocessable Entity | Validation errors | When request is well-formed but has semantic errors |
429 | Too Many Requests | Rate limited | When user has sent too many requests |
500 | Internal Server Error | Server error | When an unexpected condition prevented request fulfillment |
503 | Service Unavailable | Temporary unavailable | During maintenance or overloaded conditions |
Best Practices for Using Status Codes
- Be consistent: Use the same status codes for similar situations throughout your API
- Don't create custom codes: Stick to standard HTTP status codes
- Include detailed error messages: Particularly with 4xx errors, include helpful error messages
- Log 5xx errors: Server errors should always be logged for investigation
- Don't expose sensitive information: Error messages shouldn't reveal implementation details
- Return appropriate content types: Match your response body with the Content-Type header
Summary
HTTP status codes are a crucial part of web API development with Gin. By using them correctly, you communicate clearly with API consumers about request outcomes. Status codes help clients understand whether requests succeeded or failed, and if they failed, what kind of error occurred.
Gin makes it easy to set appropriate status codes through methods like c.JSON()
, c.XML()
, or c.String()
which all accept a status code as their first parameter. For clean, maintainable code, consider creating helper functions that standardize your API responses and error handling.
Additional Resources
Exercises
- Create a simple Gin API with endpoints that return different status codes based on conditions you define
- Implement a middleware that logs all non-200 status codes for debugging
- Build a REST API for a resource of your choice with proper status code handling for all CRUD operations
- Create custom response helper functions that standardize your API's error and success responses
- Implement rate limiting middleware that returns 429 Too Many Requests when appropriate
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)