# API Gateway Advanced - Detailed Reference

This reference contains detailed code examples for advanced API Gateway patterns.

## API Composition Service

```typescript
// src/modules/gateway/api-composition.service.ts
import { ServiceClient } from '../../core/clients/service-client';
import { logger } from '@goodgo/logger';

interface CreateOrderInput {
  userId: string;
  items: Array<{ productId: string; quantity: number }>;
  paymentMethod: string;
}

interface OrderWithPaymentResult {
  success: boolean;
  data: {
    order: { id: string; total: number };
    payment: { id: string; status: string };
  };
}

export class ApiCompositionService {
  private userClient: ServiceClient;
  private orderClient: ServiceClient;
  private paymentClient: ServiceClient;

  constructor() {
    this.userClient = new ServiceClient({
      baseURL: process.env.USER_SERVICE_URL || 'http://user-service:5002',
      serviceName: 'user-service',
    });

    this.orderClient = new ServiceClient({
      baseURL: process.env.ORDER_SERVICE_URL || 'http://order-service:5003',
      serviceName: 'order-service',
    });

    this.paymentClient = new ServiceClient({
      baseURL: process.env.PAYMENT_SERVICE_URL || 'http://payment-service:5004',
      serviceName: 'payment-service',
    });
  }

  /**
   * Aggregate user profile with orders and payment history
   */
  async getUserProfile(userId: string): Promise<any> {
    try {
      // Fan-out: Call multiple services in parallel
      const [user, orders, payments] = await Promise.all([
        this.userClient.get(`/api/v1/users/${userId}`),
        this.orderClient.get(`/api/v1/orders?userId=${userId}`),
        this.paymentClient.get(`/api/v1/payments?userId=${userId}`),
      ]);

      // Transform and aggregate response
      return {
        success: true,
        data: {
          user: user.data,
          orders: orders.data.orders,
          paymentHistory: payments.data.payments,
          summary: {
            totalOrders: orders.data.total,
            totalSpent: payments.data.totalAmount,
          },
        },
      };
    } catch (error) {
      logger.error('API composition failed', { userId, error });
      throw error;
    }
  }

  /**
   * Chain services: Create order then process payment
   */
  async createOrderWithPayment(orderData: CreateOrderInput): Promise<OrderWithPaymentResult> {
    // Step 1: Create order
    const order = await this.orderClient.post('/api/v1/orders', orderData);

    try {
      // Step 2: Process payment using order data
      const payment = await this.paymentClient.post('/api/v1/payments', {
        orderId: order.data.id,
        amount: order.data.total,
        paymentMethod: orderData.paymentMethod,
      });

      return {
        success: true,
        data: {
          order: order.data,
          payment: payment.data,
        },
      };
    } catch (error) {
      // Compensate: Cancel order if payment fails
      await this.orderClient.delete(`/api/v1/orders/${order.data.id}`);
      throw error;
    }
  }
}
```

## Request/Response Transformation

```typescript
// src/middlewares/gateway-transform.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { logger } from '@goodgo/logger';

export interface TransformRule<T = unknown, R = unknown> {
  path: string;
  requestTransform?: (req: Request) => Request;
  responseTransform?: (res: Response, data: T) => R;
}

export class GatewayTransformMiddleware {
  private rules: TransformRule[] = [];

  addRule(rule: TransformRule): void {
    this.rules.push(rule);
  }

  middleware() {
    return (req: Request, res: Response, next: NextFunction) => {
      // Find matching transform rule
      const rule = this.rules.find((r) => req.path.startsWith(r.path));

      if (rule?.requestTransform) {
        // Transform request
        Object.assign(req, rule.requestTransform(req));
      }

      // Store original json method
      const originalJson = res.json.bind(res);

      // Override json to transform response
      res.json = (data: unknown) => {
        let transformedData = data;
        if (rule?.responseTransform) {
          transformedData = rule.responseTransform(res, data);
        }
        return originalJson(transformedData);
      };

      next();
    };
  }
}

// Usage
const transformer = new GatewayTransformMiddleware();

transformer.addRule({
  path: '/api/v1/users',
  requestTransform: (req) => {
    // Add default pagination
    if (!req.query.page) req.query.page = '1';
    if (!req.query.limit) req.query.limit = '10';
    return req;
  },
  responseTransform: (res, data) => {
    // Standardize response format
    return {
      success: true,
      data: data.data || data,
      metadata: {
        timestamp: new Date().toISOString(),
      },
    };
  },
});
```

## Advanced Traefik Configuration

```yaml
# infra/traefik/dynamic/routes.yml
http:
  routers:
    # API composition route
    user-profile:
      rule: "Path(`/api/v1/user-profile/{userId}`)"
      service: api-composition-service
      middlewares:
        - cors
        - compress
        - rate-limit

    # Versioned routes
    user-service-v1:
      rule: "PathPrefix(`/api/v1/users`)"
      service: user-service-v1
      priority: 10

    user-service-v2:
      rule: "PathPrefix(`/api/v2/users`)"
      service: user-service-v2
      priority: 5

  services:
    api-composition-service:
      loadBalancer:
        servers:
          - url: "http://api-composition-service:5000"

  middlewares:
    # Request transformation
    add-default-headers:
      headers:
        customRequestHeaders:
          X-Request-ID: "{{.RequestHeader.X-Request-ID}}"
          X-Source: "traefik-gateway"

    # Response transformation
    add-response-headers:
      headers:
        customResponseHeaders:
          X-Response-Time: "{{.ResponseTime}}"

    # Circuit breaker at gateway
    circuit-breaker:
      circuitBreaker:
        expression: "NetworkErrorRatio() > 0.50"
```

## Service Mesh Integration

```yaml
# Traefik with Istio integration
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: traefik-gateway
spec:
  hosts:
    - api.goodgo.com
  gateways:
    - traefik-gateway
  http:
    - match:
        - uri:
            prefix: "/api/v1"
      route:
        - destination:
            host: user-service
            port:
              number: 5002
        - destination:
            host: order-service
            port:
              number: 5003
      fault:
        delay:
          percentage:
            value: 0.1
          fixedDelay: 5s
      retries:
        attempts: 3
        perTryTimeout: 2s
```

## Gateway-Level Circuit Breaker

```typescript
// src/core/gateway/circuit-breaker.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { createCircuitBreaker } from '../resilience/circuit-breaker';
import { logger } from '@goodgo/logger';

const serviceCircuitBreakers = new Map<string, ReturnType<typeof createCircuitBreaker>>();

export function gatewayCircuitBreaker(serviceName: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!serviceCircuitBreakers.has(serviceName)) {
      serviceCircuitBreakers.set(
        serviceName,
        createCircuitBreaker(
          async () => {
            return next();
          },
          `gateway-${serviceName}`,
          {
            timeout: 5000,
            errorThresholdPercentage: 50,
            resetTimeout: 30000,
          }
        )
      );
    }

    const breaker = serviceCircuitBreakers.get(serviceName)!;

    breaker.fire().catch((error) => {
      logger.error('Gateway circuit breaker opened', { serviceName, error });
      res.status(503).json({
        success: false,
        error: {
          code: 'SERVICE_UNAVAILABLE',
          message: `Service ${serviceName} is currently unavailable`,
        },
      });
    });
  };
}
```

## Caching at Gateway

```typescript
// src/core/gateway/gateway-cache.middleware.ts
import { Request, Response, NextFunction } from 'express';
import { cacheService } from '../cache/cache.service';
import { logger } from '@goodgo/logger';

export function gatewayCache(ttl: number = 60) {
  return async (req: Request, res: Response, next: NextFunction) => {
    // Only cache GET requests
    if (req.method !== 'GET') {
      return next();
    }

    const cacheKey = `gateway:${req.path}:${JSON.stringify(req.query)}`;

    try {
      // Check cache
      const cached = await cacheService.get(cacheKey);
      if (cached) {
        logger.debug('Gateway cache hit', { path: req.path });
        return res.json(cached);
      }

      // Store original json method
      const originalJson = res.json.bind(res);

      // Override to cache response
      res.json = (data: unknown) => {
        cacheService.set(cacheKey, data, ttl).catch((error) => {
          logger.warn('Gateway cache set failed', { error });
        });
        return originalJson(data);
      };

      next();
    } catch (error) {
      logger.error('Gateway cache error', { error });
      next();
    }
  };
}
```
