jobs-monorepo/internal/utils/http.go
Elshimy Ziad Magdy Taha 24b5bf1d08 microservices
2025-10-04 17:18:49 +05:00

87 lines
2.3 KiB
Go

package utils
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
)
// APIError represents an API error with status code and message
type APIError struct {
StatusCode int `json:"statusCode"`
Msg any `json:"msg"`
}
// Error implements the error interface for APIError
func (e APIError) Error() string {
return fmt.Sprintf("api error: %d", e.StatusCode)
}
// NewAPIError creates a new APIError with the given status code and error message
func NewAPIError(statusCode int, err error) APIError {
return APIError{
StatusCode: statusCode,
Msg: err.Error(),
}
}
// InvalidRequestData creates an APIError for validation errors with a map of field errors
func InvalidRequestData(msg string) APIError {
return APIError{
StatusCode: http.StatusUnprocessableEntity,
Msg: msg,
}
}
// InvalidJSON creates an APIError for invalid JSON format
func InvalidJSON() APIError {
return NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid JSON request data"))
}
// APIResponse represents a standardized API response wrapper
type APIResponse struct {
Success bool `json:"success"`
Data any `json:"data,omitempty"`
Error any `json:"error,omitempty"`
}
// WriteJSON writes the response as JSON with given status code, wrapped in a data object
func WriteJSON(w http.ResponseWriter, status int, data any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
response := APIResponse{
Success: status >= 200 && status < 300,
}
if response.Success {
response.Data = data
} else {
response.Error = data
}
json.NewEncoder(w).Encode(response)
}
// APIFunc is the signature for API handler functions
type APIFunc func(w http.ResponseWriter, r *http.Request) error
// Make wraps an APIFunc and handles errors consistently
func Make(h APIFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := h(w, r); err != nil {
if apiErr, ok := err.(APIError); ok {
WriteJSON(w, apiErr.StatusCode, apiErr)
} else {
errResp := map[string]any{
"statusCode": http.StatusInternalServerError,
"msg": "internal server error",
}
WriteJSON(w, http.StatusInternalServerError, errResp)
// You could add logging here
slog.Error("HTTP API error", "err", err.Error(), "path", r.URL.Path)
}
}
}
}