← Back to Cecep Azhar
Cecep Azhar #golang#rest-api#backend#best-practices#http

Building RESTful API dengan Go: Best Practices Production-Ready

Cecep Saeful Azhar • 16 Oct 2026
Building RESTful API dengan Go: Best Practices Production-Ready
Tutorial building RESTful API dengan Go yang production-ready. Best practices untuk error handling, authentication, database, dan deployment.

API yang Production-Ready

Membuat REST API yang berjalan di local itu mudah. Tapi membuat API yang production-ready - yang handle errors gracefully, punya proper authentication, logging, dan monitoring - itu tantangan yang berbeda. Dari pengalaman saya membangun API untuk berbagai startup di Bandung, berikut best practices yang saya ikuti.

Saya akan menggunakan framework Echo karena sudah terbukti reliable dan performa-tinggi di production.

Project Structure

Struktur project yang clean adalah fondasi dari API yang maintainable:

api/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── handler/
│   │   ├── user.go
│   │   └── order.go
│   ├── middleware/
│   │   ├── auth.go
│   │   ├── logging.go
│   │   └── ratelimit.go
│   ├── model/
│   │   ├── user.go
│   │   └── order.go
│   ├── repository/
│   │   ├── user.go
│   │   └── order.go
│   └── service/
│       ├── user.go
│       └── order.go
├── pkg/
│   ├── response/
│   └── validator/
├── migrations/
├── config/
└── docker-compose.yml

Pattern ini memisahkan concerns dengan jelas: handler untuk HTTP, service untuk business logic, dan repository untuk data access.

Structured Error Handling

Error handling yang konsisten adalah kunci API yang profesional:

// Custom error types
type AppError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Detail  string `json:"detail,omitempty"`
}

func (e *AppError) Error() string {
    return e.Message
}

// Error constructors
func NotFound(msg string) *AppError {
    return &AppError{Code: 404, Message: msg}
}

func BadRequest(msg string) *AppError {
    return &AppError{Code: 400, Message: msg}
}

func InternalError(msg string) *AppError {
    return &AppError{Code: 500, Message: msg}
}

// Handler dengan error handling yang clean
func (h *UserHandler) GetUser(c echo.Context) error {
    id := c.Param("id")
    
    user, err := h.service.GetUserByID(c.Request().Context(), id)
    if err != nil {
        switch {
        case errors.Is(err, ErrUserNotFound):
            return NotFound("User tidak ditemukan")
        default:
            return InternalError("Gagal mengambil data user")
        }
    }
    
    return c.JSON(200, map[string]interface{}{
        "status": "success",
        "data":   user,
    })
}

Middleware Stack

Middleware adalah tempat kita handle cross-cutting concerns. Berikut middleware stack yang saya gunakan di hampir semua project:

func SetupMiddleware(e *echo.Echo) {
    // Recovery middleware
    e.Use(middleware.Recover())
    
    // CORS
    e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
        AllowOrigins: []string{"https://*.example.com"},
        AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
    }))
    
    // Custom logging
    e.Use(LoggingMiddleware)
    
    // Rate limiting
    e.Use(RateLimitMiddleware(100, time.Minute))
    
    // Request ID
    e.Use(RequestIDMiddleware)
}

func LoggingMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        start := time.Now()
        
        err := next(c)
        
        log.Info().
            Str("method", c.Request().Method).
            Str("path", c.Request().URL.Path).
            Int("status", c.Response().Status).
            Dur("duration", time.Since(start)).
            Str("request_id", c.Response().Header().Get(echo.HeaderXRequestID)).
            Msg("request completed")
        
        return err
    }
}

Database Connection Pool

Untuk database, saya selalu konfigurasi connection pool dengan benar. Ini sangat berpengaruh terhadap performa di production:

db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
    log.Fatal(err)
}

db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(1 * time.Minute)

Kesimpulan

Build REST API yang production-ready butuh attention terhadap detail. Dengan struktur yang clean, error handling yang konsisten, dan middleware yang tepat, API Go kamu akan siap untuk scale. Kalau kamu butuh bantuan dalam membangun API untuk bisnis kamu, feel free untuk menghubungi saya.

← More articles from Cecep Azhar Central Content Hub Home →