Request Flow
Dokumentasi lengkap alur request dari client hingga response, termasuk authentication, authorization, dan error handling.🔄 Complete Request Flow
📊 Request Lifecycle Stages
1. Client Request Preparation
Copy
// Flutter Client
Future<Response> createOrder(CreateOrderRequest request) async {
// Generate unique request ID
final requestId = Uuid().v4();
// Get JWT token from secure storage
final token = await secureStorage.read(key: 'auth_token');
// Prepare headers
final headers = {
'Authorization': 'Bearer $token',
'X-Request-ID': requestId,
'Content-Type': 'application/json',
};
// Make request
try {
final response = await dio.post(
'/orders',
data: request.toJson(),
options: Options(headers: headers),
);
return response;
} catch (e) {
// Handle error
throw handleError(e);
}
}
2. API Gateway Processing
Copy
// API Gateway Middleware Chain
func (gw *Gateway) HandleRequest(c echo.Context) error {
// 1. Request ID
requestID := c.Request().Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
c.Set("request_id", requestID)
// 2. Logging
logger := gw.logger.With(
zap.String("request_id", requestID),
zap.String("method", c.Request().Method),
zap.String("path", c.Request().URL.Path),
)
logger.Info("request started")
// 3. Rate Limiting
if err := gw.rateLimiter.Check(c); err != nil {
return c.JSON(429, ErrorResponse{
Error: Error{
Code: "RATE_LIMIT_EXCEEDED",
Message: "Too many requests",
TraceID: requestID,
},
})
}
// 4. Authentication
token := extractToken(c)
userCtx, err := gw.authService.ValidateToken(c.Request().Context(), token)
if err != nil {
return c.JSON(401, ErrorResponse{
Error: Error{
Code: "UNAUTHORIZED",
Message: "Invalid or expired token",
TraceID: requestID,
},
})
}
c.Set("user", userCtx)
// 5. Forward to service
return gw.forwardToService(c)
}
3. Service Processing
Copy
// Business Service Handler
func (s *OrderService) CreateOrder(c echo.Context) error {
ctx := c.Request().Context()
requestID := c.Get("request_id").(string)
user := c.Get("user").(*UserContext)
// Start tracing
ctx, span := s.tracer.Start(ctx, "CreateOrder")
defer span.End()
// Parse request
var req CreateOrderRequest
if err := c.Bind(&req); err != nil {
return c.JSON(400, ErrorResponse{
Error: Error{
Code: "INVALID_REQUEST",
Message: "Invalid request body",
TraceID: requestID,
},
})
}
// Validate
if err := c.Validate(&req); err != nil {
return c.JSON(400, ValidationErrorResponse(err, requestID))
}
// Check authorization
if !s.authz.CanCreateOrder(user) {
return c.JSON(403, ErrorResponse{
Error: Error{
Code: "FORBIDDEN",
Message: "Insufficient permissions",
TraceID: requestID,
},
})
}
// Business logic
order, err := s.orderUseCase.CreateOrder(ctx, user.ID, &req)
if err != nil {
s.logger.Error("failed to create order",
zap.Error(err),
zap.String("request_id", requestID),
)
return handleServiceError(c, err, requestID)
}
// Publish event (async)
go s.eventPublisher.Publish(OrderCreatedEvent{
OrderID: order.ID,
UserID: user.ID,
})
// Metrics
s.metrics.OrderCreated.Inc()
return c.JSON(201, SuccessResponse{
Data: order,
})
}
🔐 Authentication Flow
⚡ Caching Strategy
Cache Flow
Cache Implementation
Copy
func (s *ProductService) GetProduct(ctx context.Context, id string) (*Product, error) {
// Check cache
cacheKey := fmt.Sprintf("product:%s", id)
var product Product
err := s.cache.Get(ctx, cacheKey, &product)
if err == nil {
s.metrics.CacheHit.Inc()
return &product, nil
}
s.metrics.CacheMiss.Inc()
// Query database
product, err = s.repo.FindByID(ctx, id)
if err != nil {
return nil, err
}
// Store in cache (15 minutes TTL)
_ = s.cache.Set(ctx, cacheKey, product, 15*time.Minute)
return &product, nil
}
🚨 Error Handling Flow
Error Response Format
Copy
{
"error": {
"code": "ERROR_CODE",
"message": "Human readable message",
"details": [
{
"field": "field_name",
"message": "Field specific error"
}
],
"traceId": "550e8400-e29b-41d4-a716-446655440000"
}
}
📈 Performance Metrics
Key Metrics Tracked
Copy
# Request Metrics
http_requests_total: counter
labels: [method, path, status]
http_request_duration_seconds: histogram
labels: [method, path]
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5]
# Cache Metrics
cache_hits_total: counter
labels: [cache_type]
cache_misses_total: counter
labels: [cache_type]
# Database Metrics
db_query_duration_seconds: histogram
labels: [operation, table]
buckets: [0.001, 0.01, 0.1, 0.5, 1]
# Error Metrics
errors_total: counter
labels: [type, service]
🔍 Observability
Distributed Tracing
Copy
// Trace propagation
func (s *Service) HandleRequest(c echo.Context) error {
// Extract trace context from headers
ctx := otel.GetTextMapPropagator().Extract(
c.Request().Context(),
propagation.HeaderCarrier(c.Request().Header),
)
// Start span
ctx, span := s.tracer.Start(ctx, "HandleRequest",
trace.WithAttributes(
attribute.String("http.method", c.Request().Method),
attribute.String("http.url", c.Request().URL.Path),
),
)
defer span.End()
// Process request
result, err := s.processRequest(ctx, c)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return err
}
span.SetStatus(codes.Ok, "success")
return c.JSON(200, result)
}