Go: Integrated Project: E-commerce API (Part 3)
Last updated: 2026-08-26
We have the architecture, we have the security—now, let's get it live.
From the first line of code to production-ready: Swagger documentation, graceful shutdown, Docker deployment, pprof tuning, and Prometheus monitoring. Bob's e-commerce API is ready to welcome real users.
1. You will learn
- Automatic documentation generation for Swagger / OpenAPI
خادم.ShutdownGraceful shutdown- Docker multi-stage build + Docker Compose full stack
- pprof Performance Analysis Endpoint Integration
- Prometheus Metrics Exposure
- Design of Health Check Endpoints
2. Story: The Night Before Launch
(1) Pain Points: No documentation, no monitoring, and manual deployment
Bob's e-commerce API is fully functional, but the operations team has refused to accept it:
"The operations team said, 'Without API documentation, the front end can't integrate with it. Without health checks, we don't know if the service is running. Without monitoring, we wouldn't even know if it went down. And we still have to manually SCP the binaries for deployment—it's way too primitive.'"
Launch Checklist:
❌ API docs → Frontend has to ask "what fields does this endpoint return?" every time
❌ Graceful shutdown → Kill process causes in-flight orders to be lost
❌ Docker deploy → SCP binary to server, start manually
❌ Performance monitoring → Don't know which part of the API is slow
(2) Lesson Objective: Production-Ready
✅ Swagger docs → Frontend self-service browsing
✅ Graceful Shutdown → Signal handling + wait for connections to close
✅ Docker Compose → One command to start the full stack
✅ pprof + Prometheus → Performance visualization
3. Full Implementation
▶ Example: Graceful Shutdown
⚙️ Prerequisite: Run
go get github.com/mattn/go-sqlite3(requires CGO; alternatively usemodernc.org/sqlitefor a pure Go driver)
// cmd/server/main.go
package main
import (
"context"
"database/sql"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"ecommerce/internal/handler"
"ecommerce/internal/repository"
"ecommerce/internal/service"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "./ecommerce.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(5 * time.Minute)
// Dependency injection
userRepo := repository.NewUserRepository(db)
productRepo := repository.NewProductRepository(db)
orderRepo := repository.NewOrderRepository(db)
userSvc := service.NewUserService(userRepo)
productSvc := service.NewProductService(productRepo)
orderSvc := service.NewOrderService(orderRepo, productRepo, userRepo)
userHandler := handler.NewUserHandler(userSvc)
productHandler := handler.NewProductHandler(productSvc)
orderHandler := handler.NewOrderHandler(orderSvc)
mux := http.NewServeMux()
userHandler.Register(mux)
productHandler.Register(mux)
orderHandler.Register(mux)
// Health check endpoint
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
mux.HandleFunc("GET /ready", func(w http.ResponseWriter, r *http.Request) {
if err := db.Ping(); err != nil {
w.WriteHeader(http.StatusServiceUnavailable)
json.NewEncoder(w).Encode(map[string]string{"status": "not ready"})
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ready"})
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
// Timeout settings (prevent slowloris attacks)
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
}
// Graceful Shutdown
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
sig := <-sigCh
log.Printf("Received signal %v, shutting down...", sig)
// Give in-flight requests up to 30 seconds to complete
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Printf("Forced shutdown: %v", err)
}
}()
log.Println("E-commerce API listening on :8080")
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
log.Println("Server shut down safely")
}
▶ Example: pprof + Prometheus Integration
⚙️ Prerequisite: Run
go get github.com/prometheus/client_golang/prometheusandgo get github.com/prometheus/client_golang/prometheus/promhttp
// monitoring.go
package main
import (
"net/http"
"net/http/pprof"
"runtime"
"strconv"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// Prometheus metrics
var (
httpRequestsTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total HTTP requests",
},
[]string{"method", "path", "status"},
)
httpRequestDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP request duration in seconds",
Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5},
},
[]string{"method", "path"},
)
activeGoroutines = prometheus.NewGaugeFunc(
prometheus.GaugeOpts{
Name: "go_goroutines_active",
Help: "Current number of goroutines",
},
func() float64 {
return float64(runtime.NumGoroutine())
},
)
)
func init() {
prometheus.MustRegister(httpRequestsTotal)
prometheus.MustRegister(httpRequestDuration)
prometheus.MustRegister(activeGoroutines)
}
// Prometheus middleware
func prometheusMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Wrap ResponseWriter to get status code
wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(wrapped, r)
duration := time.Since(start)
httpRequestsTotal.WithLabelValues(
r.Method, r.URL.Path, strconv.Itoa(wrapped.statusCode),
).Inc()
httpRequestDuration.WithLabelValues(
r.Method, r.URL.Path,
).Observe(duration.Seconds())
})
}
type responseWriter struct {
http.ResponseWriter
statusCode int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}
// Register pprof and Prometheus endpoints in main
func registerMonitoring(mux *http.ServeMux) {
// pprof endpoints
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
// Prometheus metrics
mux.Handle("/metrics", promhttp.Handler())
}
func main() {
mux := http.NewServeMux()
registerMonitoring(mux)
// Example business endpoint
mux.HandleFunc("GET /api/products", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"products":[]}`))
})
// Health check
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ok"}`))
})
log.Println("Monitoring + API server listening on :8080")
http.ListenAndServe(":8080", prometheusMiddleware(mux))
}
▶ Example: Docker Deployment
# Dockerfile (Multi-stage Build)
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Dependency cache
COPY go.mod go.sum ./
RUN go mod download
# Source + compile
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# === Run stage ===
FROM alpine:3.19
RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/server /server
EXPOSE 8080
EXPOSE 6060 # pprof
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget -qO- http://localhost:8080/health || exit 1
CMD ["/server"]
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "8080:8080"
- "6060:6060" # pprof
environment:
- DB_PATH=/data/ecommerce.db
- GIN_MODE=release
volumes:
- data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 5s
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
volumes:
data:
prometheus_data:
grafana_data:
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'ecommerce-api'
static_configs:
- targets: ['api:8080']
metrics_path: '/metrics'
▶ Example: Swagger Documentation
// Generate OpenAPI docs from comments (use with swag tool)
// Package handler handles HTTP requests.
//
// E-commerce API
//
// Schemes: http
// Host: localhost:8080
// BasePath: /api/v1
// Version: 1.0.0
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// swagger:meta
package handler
import "ecommerce/pkg/model"
// User information
// swagger:model
type User struct {
ID int `json:"id"`
Email سلسلة `json:"email"`
Name سلسلة `json:"name"`
}
// RegisterUser registers a new user
// swagger:route POST /api/v1/register users registerUser
//
// Register a new user
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Responses:
// 201: User
// 400: ErrorResponse
// 409: ErrorResponse
func (h *UserHandler) RegisterUser(w http.ResponseWriter, r *http.Request) {
// handler logic
}
// swagger:parameters registerUser
type RegisterUserParams struct {
// in: body
Body model.RegisterRequest
}
// ErrorResponse خطأ استجابة
// swagger:استجابة
type ErrorResponse struct {
// in: body
Body struct {
Error سلسلة `json:"خطأ"`
}
}
# Install swag CLI
$ go install github.com/swaggo/swag/cmd/swag@latest
# Generate docs
$ swag init -g cmd/server/main.go
# Access docs
# http://localhost:8080/swagger/index.html
(5) Launch Checklist
✅ Graceful Shutdown — wait for requests to complete after kill
✅ Health Check — /health and /ready endpoints
✅ pprof — /debug/pprof/ performance analysis
✅ Prometheus — /metrics endpoint exposed
✅ Swagger — Auto-generated API documentation
✅ Docker — Multi-stage build, image < 20 MB
✅ docker-compose — One command to start the full stack
✅ ReadTimeout / WriteTimeout — prevent slowloris attacks
✅ DB connection pool — MaxOpenConns=25, MaxIdleConns=10
✅ server.Shutdown — SIGINT/SIGTERM handling
❓ FAQ
server.Shutdown waits for all active HTTP requests to complete (up to the specified timeout) before closing the listener. If the process is killed immediately, requests currently being processed will be interrupted—resulting in inconsistent order data./debug/pprof/ path with auth middleware; (3) Enable it only in the staging environment. In the production environment, it is recommended to enable profiling temporarily only when needed.promhttp.Handler() to expose metrics at the /metrics endpoint. The Prometheus server periodically scrapes this endpoint. Grafana connects to the Prometheus data source and creates visualizations. Standard metrics: number of requests, latency distribution, error rate, number of goroutines, and number of GC events.swaggo/swag tool—write comments in a specific format within your handler code, then run swag init to generate the docs/ directory. Use swaggo/http-swagger to mount the documentation to the /swagger/ endpoint. Comment format: // swagger:route, // swagger:model, // swagger:parameters.depends_on to control the startup order, and use volumes to persist data. Run docker-compose up -d to start all services with a single command. In a production environment, use Docker Stack or Kubernetes.📖 Summary
- Graceful Shutdown:
server.Shutdown(ctx)+signal.Notify - Timeout settings: ReadTimeout / WriteTimeout / IdleTimeout
- pprof:
/debug/pprof/endpoint (not exposed externally) - Prometheus:
/metricsendpoint + custom metrics - Swagger:
swaggo/swaggenerates OpenAPI from comments - Docker: Multi-stage build + HEALTHCHECK
- Docker Compose: API + Prometheus + Grafana
- Health checks:
/health(alive) +/ready(ready)
📝 Exercises
-
Basic (Difficulty ⭐): Add graceful shutdown and timeout settings to the e-commerce API covered in this lesson. Use
curlto test the/healthand/readyendpoints. Usekill -SIGTERM <pid>to test the graceful shutdown. -
Advanced (Difficulty ⭐⭐): Implement a complete Docker-based deployment. Requirements: (1) Dockerfile with multi-stage build; (2) docker-compose.yml (API + Prometheus + Grafana); (3) HEALTHCHECK; (4) pprof endpoint (internal port only); (5) Verify that all services start normally after running
docker-compose up. -
Challenge (Difficulty ⭐⭐⭐): Implement Prometheus monitoring + Grafana dashboards. Requirements: (1) Add Prometheus middleware to the API (request count, latency, error rate); (2) Add business metrics (e.g., number of orders created, number of registered users); (3) Include Prometheus and Grafana in the Docker Compose file; (4) Import or create a Grafana dashboard to display QPS, P99 latency, error rate, and number of goroutines; (5) Verify that the metrics are correct using Grafana after load testing.
🎉 Congratulations! You've completed all 30 lessons of the Go tutorial! From "Hello World" to a production-ready e-commerce API, you've mastered the full skill set for Go backend development.