
# API Gateway Architecture & Security Implementation Plan

**Created**: October 3, 2025  
**Status**: Planning Phase  
**Priority**: High - Security & Scalability

---

## 📋 Executive Summary

### Current State
- **Backend API**: Directly exposed to internet at `https://maternal-api.noru1.ro`
- **Frontend**: Next.js web app making direct API calls from browser
- **Security Risk**: All API endpoints publicly accessible
- **Architecture**: Monolithic - single NestJS backend serving REST, GraphQL, and WebSocket

### Problem Statement
With the current architecture:
1. Backend API is fully exposed to the internet (security risk)
2. No rate limiting at infrastructure level
3. Direct API access bypasses Next.js middleware/auth checks
4. Future mobile apps will need direct API access
5. WebSocket connections need persistent connection handling
6. GraphQL endpoint requires different security model than REST

### Proposed Solution
Implement an **API Gateway pattern** with:
- Next.js API routes as BFF (Backend-for-Frontend) for web app
- Direct backend access for mobile apps (with API key + JWT)
- Nginx/Kong as reverse proxy for rate limiting and SSL termination
- WebSocket gateway for real-time features
- Separate security policies for REST vs GraphQL

---

## 🏗️ Architecture Overview

### Option 1: Next.js API Routes as BFF (Recommended for MVP)

```
┌─────────────────────────────────────────────────────────────┐
│                      Internet (HTTPS)                        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │  Nginx/Cloudflare│
                    │  (SSL, WAF, DDoS) │
                    └─────────────────┘
                              │
                ┌─────────────┴─────────────┐
                │                           │
                ▼                           ▼
    ┌───────────────────────┐   ┌─────────────────────┐
    │  Next.js Frontend     │   │  Mobile Apps        │
    │  (Port 3030)          │   │  (iOS/Android)      │
    │                       │   │                     │
    │  ┌─────────────────┐ │   └─────────────────────┘
    │  │ /api/* Routes   │ │              │
    │  │ (BFF Layer)     │ │              │
    │  └─────────────────┘ │              │
    └───────────────────────┘              │
                │                          │
                │ (Internal Network)       │ (Public API)
                │ localhost:3020           │ api.maternal.com
                │                          │
                └──────────┬───────────────┘
                           ▼
              ┌─────────────────────────┐
              │   NestJS Backend        │
              │   (Port 3020)           │
              │                         │
              │  ┌───────────────────┐  │
              │  │ REST API          │  │
              │  │ /api/v1/*         │  │
              │  └───────────────────┘  │
              │                         │
              │  ┌───────────────────┐  │
              │  │ GraphQL           │  │
              │  │ /graphql          │  │
              │  └───────────────────┘  │
              │                         │
              │  ┌───────────────────┐  │
              │  │ WebSocket         │  │
              │  │ /ws               │  │
              │  └───────────────────┘  │
              └─────────────────────────┘
                           │
                           ▼
                 ┌──────────────────┐
                 │   PostgreSQL     │
                 │   Redis          │
                 │   MongoDB        │
                 └──────────────────┘
```

### Option 2: Kong API Gateway (Production-Ready)

```
┌─────────────────────────────────────────────────────────────┐
│                      Internet (HTTPS)                        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌─────────────────────┐
                    │   Kong API Gateway   │
                    │   (Port 443/8000)    │
                    │                      │
                    │  Plugins:            │
                    │  - Rate Limiting     │
                    │  - JWT Auth          │
                    │  - CORS              │
                    │  - Request Transform │
                    │  - Response Cache    │
                    └─────────────────────┘
                              │
                ┌─────────────┴─────────────┐
                │                           │
                ▼                           ▼
    ┌───────────────────────┐   ┌─────────────────────┐
    │  Next.js Frontend     │   │  Mobile Apps        │
    │  (Internal)           │   │  (External)         │
    └───────────────────────┘   └─────────────────────┘
                │                          │
                └──────────┬───────────────┘
                           ▼
              ┌─────────────────────────┐
              │   NestJS Backend        │
              │   (Internal Network)    │
              │   Not exposed to web    │
              └─────────────────────────┘
```

---

## 🎯 Recommended Architecture: Hybrid Approach

### Phase 1: MVP (Current State + BFF)
1. **Web App**: Next.js API routes as BFF (proxy to backend)
2. **Mobile Apps**: Direct backend access with API keys
3. **Backend**: Exposed only to mobile, hidden from web
4. **Timeline**: 1-2 weeks

### Phase 2: Production (Kong Gateway)
1. **All Clients**: Route through Kong API Gateway
2. **Backend**: Completely internal, not exposed
3. **Security**: Centralized auth, rate limiting, logging
4. **Timeline**: 4-6 weeks (post-MVP)

---

## 📝 Implementation Plan: Phase 1 (BFF Pattern)

### Step 1: Create Next.js API Routes as BFF

**Goal**: Proxy all backend requests through Next.js to hide backend URL

**Files to Create**:
```
maternal-web/
├── app/api/
│   ├── proxy/
│   │   ├── route.ts                    # Generic proxy handler
│   │   └── [...path]/route.ts          # Catch-all proxy
│   ├── graphql/
│   │   └── route.ts                    # GraphQL proxy
│   └── ws/
│       └── route.ts                    # WebSocket upgrade handler
```

**Implementation**:

#### 1.1 Generic REST API Proxy

**File**: `maternal-web/app/api/proxy/[...path]/route.ts`

```typescript
import { NextRequest, NextResponse } from 'next/server';

const BACKEND_URL = process.env.BACKEND_API_URL || 'http://localhost:3020';

export async function GET(
  request: NextRequest,
  { params }: { params: { path: string[] } }
) {
  return proxyRequest(request, params.path, 'GET');
}

export async function POST(
  request: NextRequest,
  { params }: { params: { path: string[] } }
) {
  return proxyRequest(request, params.path, 'POST');
}

export async function PATCH(
  request: NextRequest,
  { params }: { params: { path: string[] } }
) {
  return proxyRequest(request, params.path, 'PATCH');
}

export async function DELETE(
  request: NextRequest,
  { params }: { params: { path: string[] } }
) {
  return proxyRequest(request, params.path, 'DELETE');
}

async function proxyRequest(
  request: NextRequest,
  pathSegments: string[],
  method: string
) {
  const path = pathSegments.join('/');
  const url = new URL(request.url);
  const backendUrl = `${BACKEND_URL}/api/v1/${path}${url.search}`;

  // Forward headers (excluding host)
  const headers = new Headers();
  request.headers.forEach((value, key) => {
    if (key.toLowerCase() !== 'host' && key.toLowerCase() !== 'connection') {
      headers.set(key, value);
    }
  });

  // Add internal API key for backend authentication
  headers.set('X-Internal-API-Key', process.env.INTERNAL_API_KEY || '');

  try {
    const body = method !== 'GET' ? await request.text() : undefined;

    const response = await fetch(backendUrl, {
      method,
      headers,
      body,
    });

    // Forward response headers
    const responseHeaders = new Headers();
    response.headers.forEach((value, key) => {
      responseHeaders.set(key, value);
    });

    const responseBody = await response.text();

    return new NextResponse(responseBody, {
      status: response.status,
      statusText: response.statusText,
      headers: responseHeaders,
    });
  } catch (error) {
    console.error('Proxy error:', error);
    return NextResponse.json(
      { error: 'Internal proxy error' },
      { status: 502 }
    );
  }
}
```

#### 1.2 GraphQL Proxy

**File**: `maternal-web/app/api/graphql/route.ts`

```typescript
import { NextRequest, NextResponse } from 'next/server';

const BACKEND_URL = process.env.BACKEND_API_URL || 'http://localhost:3020';

export async function POST(request: NextRequest) {
  const backendUrl = `${BACKEND_URL}/graphql`;

  const headers = new Headers();
  headers.set('Content-Type', 'application/json');
  headers.set('Authorization', request.headers.get('Authorization') || '');
  headers.set('X-Internal-API-Key', process.env.INTERNAL_API_KEY || '');

  try {
    const body = await request.text();

    const response = await fetch(backendUrl, {
      method: 'POST',
      headers,
      body,
    });

    const responseBody = await response.text();

    return new NextResponse(responseBody, {
      status: response.status,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  } catch (error) {
    console.error('GraphQL proxy error:', error);
    return NextResponse.json(
      { errors: [{ message: 'GraphQL proxy error' }] },
      { status: 502 }
    );
  }
}

// Support GraphQL Playground in development
export async function GET(request: NextRequest) {
  if (process.env.NODE_ENV !== 'production') {
    const backendUrl = `${BACKEND_URL}/graphql`;
    const response = await fetch(backendUrl);
    const html = await response.text();
    return new NextResponse(html, {
      headers: { 'Content-Type': 'text/html' },
    });
  }

  return NextResponse.json(
    { error: 'GraphQL Playground disabled in production' },
    { status: 403 }
  );
}
```

#### 1.3 WebSocket Proxy (Next.js Limitation Workaround)

**Note**: Next.js API routes don't support WebSocket upgrades directly. We need to use a custom server or keep WebSocket on backend.

**Option A**: Keep WebSocket endpoint on backend (simpler)
**Option B**: Use Next.js custom server with `ws` library (complex)

**Recommended**: Keep WebSocket on backend, add authentication layer

**File**: `maternal-app-backend/src/families/families.gateway.ts` (modify)

```typescript
import { WebSocketGateway, WebSocketServer, OnGatewayConnection } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({
  cors: {
    origin: [
      'http://localhost:3030',
      'https://maternal.noru1.ro',
      'https://maternal-web.noru1.ro',
    ],
    credentials: true,
  },
})
export class FamiliesGateway implements OnGatewayConnection {
  @WebSocketServer()
  server: Server;

  async handleConnection(client: Socket) {
    // Verify internal API key or JWT token
    const apiKey = client.handshake.headers['x-internal-api-key'];
    const token = client.handshake.auth.token;

    if (!apiKey && !token) {
      client.disconnect();
      return;
    }

    // Authenticate based on mobile (token) or web (API key)
    const isValid = await this.validateConnection(apiKey, token);
    if (!isValid) {
      client.disconnect();
    }
  }
}
```

---

### Step 2: Update Frontend to Use BFF

**Files to Modify**:
- `lib/api/client.ts`
- `lib/apollo-client.ts`
- All API utility files

**Changes**:

```typescript
// lib/api/client.ts (before)
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3020';

// lib/api/client.ts (after)
const API_BASE_URL = '/api/proxy'; // Use Next.js BFF

// lib/apollo-client.ts (before)
uri: process.env.NEXT_PUBLIC_GRAPHQL_URL || 'http://localhost:3020/graphql',

// lib/apollo-client.ts (after)
uri: '/api/graphql', // Use Next.js GraphQL proxy
```

**Environment Variables**:
```bash
# maternal-web/.env.local
# Remove NEXT_PUBLIC_API_URL (security - don't expose backend URL to browser)
# NEXT_PUBLIC_API_URL=https://maternal-api.noru1.ro  # DELETE THIS

# Add backend URL for server-side only
BACKEND_API_URL=http://localhost:3020
INTERNAL_API_KEY=your-secret-internal-key-12345

# For WebSocket, keep exposed for now (will secure later)
NEXT_PUBLIC_WS_URL=https://maternal-api.noru1.ro
```

---

### Step 3: Add Internal API Key Authentication to Backend

**Goal**: Backend validates requests from Next.js BFF using internal API key

**File**: `maternal-app-backend/src/common/guards/internal-api-key.guard.ts` (new)

```typescript
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class InternalApiKeyGuard implements CanActivate {
  constructor(private configService: ConfigService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const apiKey = request.headers['x-internal-api-key'];
    const expectedKey = this.configService.get('INTERNAL_API_KEY');

    if (!expectedKey) {
      // If not configured, allow (development mode)
      return true;
    }

    if (apiKey !== expectedKey) {
      throw new UnauthorizedException('Invalid internal API key');
    }

    return true;
  }
}
```

**Usage** (apply to all controllers):

```typescript
import { Controller, UseGuards } from '@nestjs/common';
import { InternalApiKeyGuard } from '../common/guards/internal-api-key.guard';

@Controller('api/v1/children')
@UseGuards(InternalApiKeyGuard) // Add this to all controllers
export class ChildrenController {
  // ...
}
```

---

### Step 4: Configure Mobile App Direct Access

**Goal**: Mobile apps bypass BFF and call backend directly with API key + JWT

**Backend Configuration**:

```typescript
// app.module.ts
app.enableCors({
  origin: [
    'http://localhost:3030',              // Next.js dev
    'https://maternal.noru1.ro',          // Production web
    'capacitor://localhost',              // iOS mobile
    'http://localhost',                   // Android mobile
    'ionic://localhost',                  // Ionic mobile
  ],
  credentials: true,
});
```

**Mobile App Configuration** (future):

```typescript
// mobile-app/src/config/api.ts
const API_CONFIG = {
  baseUrl: 'https://api.maternal.noru1.ro', // Direct backend access
  graphqlUrl: 'https://api.maternal.noru1.ro/graphql',
  wsUrl: 'wss://api.maternal.noru1.ro',
  headers: {
    'X-API-Key': process.env.MOBILE_API_KEY, // Different from internal key
  },
};
```

---

### Step 5: Nginx Configuration for Production

**Goal**: Use Nginx as reverse proxy for SSL termination and basic security

**File**: `/etc/nginx/sites-available/maternal-app`

```nginx
# Upstream backends
upstream nextjs_backend {
    server 127.0.0.1:3030;
}

upstream nestjs_backend {
    server 127.0.0.1:3020;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name maternal.noru1.ro;
    return 301 https://$server_name$request_uri;
}

# Main web application (Next.js)
server {
    listen 443 ssl http2;
    server_name maternal.noru1.ro;

    ssl_certificate /etc/letsencrypt/live/maternal.noru1.ro/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/maternal.noru1.ro/privkey.pem;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=web_limit:10m rate=100r/m;
    limit_req zone=web_limit burst=20 nodelay;

    location / {
        proxy_pass http://nextjs_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# API backend (for mobile apps only - optional)
server {
    listen 443 ssl http2;
    server_name api.maternal.noru1.ro;

    ssl_certificate /etc/letsencrypt/live/api.maternal.noru1.ro/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.maternal.noru1.ro/privkey.pem;

    # Stricter rate limiting for API
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=60r/m;
    limit_req zone=api_limit burst=10 nodelay;

    # Only allow mobile app user agents (optional)
    if ($http_user_agent !~* (Maternal-iOS|Maternal-Android|curl)) {
        return 403;
    }

    location / {
        proxy_pass http://nestjs_backend;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # WebSocket support
    location /ws {
        proxy_pass http://nestjs_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}
```

---

## 🔒 Security Enhancements

### 1. API Key Management

**Environment Variables**:
```bash
# Backend (.env.production)
INTERNAL_API_KEY=<strong-random-key-for-nextjs-bff>
MOBILE_API_KEY=<different-key-for-mobile-apps>

# Next.js (.env.local)
INTERNAL_API_KEY=<same-as-backend-internal-key>
BACKEND_API_URL=http://localhost:3020  # or internal network IP
```

**Generation**:
```bash
# Generate secure API keys
openssl rand -base64 32
```

---

### 2. Rate Limiting Strategy

| Client Type | Rate Limit | Enforcement |
|-------------|------------|-------------|
| Web (BFF) | 100 req/min per IP | Nginx |
| Mobile (Direct) | 60 req/min per API key | Nginx + NestJS |
| GraphQL | 30 queries/min | NestJS middleware |
| WebSocket | 10 connections per user | NestJS gateway |

**Implementation** (NestJS):

```typescript
// src/common/middleware/rate-limit.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { Redis } from 'ioredis';

@Injectable()
export class ApiKeyRateLimitMiddleware implements NestMiddleware {
  constructor(private redis: Redis) {}

  async use(req: Request, res: Response, next: NextFunction) {
    const apiKey = req.headers['x-api-key'];
    
    if (!apiKey) {
      return next();
    }

    const key = `rate_limit:api_key:${apiKey}`;
    const count = await this.redis.incr(key);

    if (count === 1) {
      await this.redis.expire(key, 60); // 1 minute window
    }

    if (count > 60) {
      return res.status(429).json({
        error: 'Rate limit exceeded',
        retryAfter: await this.redis.ttl(key),
      });
    }

    next();
  }
}
```

---

### 3. CORS Configuration

**Strict CORS for Production**:

```typescript
// main.ts
app.enableCors({
  origin: (origin, callback) => {
    const allowedOrigins = [
      'https://maternal.noru1.ro',
      'https://maternal-web.noru1.ro',
    ];

    // Allow mobile apps (check user agent)
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else if (origin.includes('capacitor://')) {
      callback(null, true); // Ionic/Capacitor
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
  allowedHeaders: [
    'Content-Type',
    'Authorization',
    'X-API-Key',
    'X-Internal-API-Key',
  ],
});
```

---

## 📱 Mobile App Integration

### React Native / Expo Configuration

```typescript
// mobile-app/src/services/api.ts
import axios from 'axios';
import Constants from 'expo-constants';

const API_CONFIG = {
  baseURL: Constants.expoConfig?.extra?.apiUrl || 'https://api.maternal.noru1.ro',
  headers: {
    'X-API-Key': Constants.expoConfig?.extra?.apiKey,
    'User-Agent': `Maternal-${Platform.OS}/${Constants.expoConfig?.version}`,
  },
};

const apiClient = axios.create(API_CONFIG);

// Add JWT token to requests
apiClient.interceptors.request.use((config) => {
  const token = getAuthToken(); // From AsyncStorage
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
```

**WebSocket Connection** (mobile):

```typescript
import io from 'socket.io-client';

const socket = io('wss://api.maternal.noru1.ro', {
  auth: {
    token: getAuthToken(),
  },
  transports: ['websocket'],
  reconnection: true,
  reconnectionAttempts: 5,
});
```

---

## 🔍 Monitoring & Logging

### 1. Request Logging (Nginx)

```nginx
log_format api_log '$remote_addr - $remote_user [$time_local] '
                   '"$request" $status $body_bytes_sent '
                   '"$http_referer" "$http_user_agent" '
                   'api_key=$http_x_api_key '
                   'request_time=$request_time';

access_log /var/log/nginx/maternal_api_access.log api_log;
```

### 2. Backend Request Tracking

```typescript
// src/common/middleware/request-logger.middleware.ts
import { Injectable, NestMiddleware, Logger } from '@nestjs/common';

@Injectable()
export class RequestLoggerMiddleware implements NestMiddleware {
  private logger = new Logger('HTTP');

  use(req: Request, res: Response, next: NextFunction) {
    const { method, originalUrl, headers } = req;
    const userAgent = headers['user-agent'];
    const apiKey = headers['x-api-key'];
    const isInternal = headers['x-internal-api-key'] ? 'internal' : 'external';

    const start = Date.now();

    res.on('finish', () => {
      const { statusCode } = res;
      const duration = Date.now() - start;

      this.logger.log(
        `${method} ${originalUrl} ${statusCode} ${duration}ms - ${isInternal} - ${apiKey?.substring(0, 8)}...`
      );
    });

    next();
  }
}
```

---

## 🚀 Deployment Strategy

### Phase 1: MVP Deployment (Week 1-2)

**Week 1**:
- [x] Create Next.js API proxy routes
- [x] Update frontend to use BFF
- [x] Add internal API key guard to backend
- [x] Test web app with new architecture

**Week 2**:
- [x] Configure Nginx reverse proxy
- [x] Set up SSL certificates
- [x] Deploy to production
- [x] Monitor and fix issues

### Phase 2: Mobile App Support (Week 3-4)

**Week 3**:
- [ ] Create mobile API keys
- [ ] Configure CORS for mobile
- [ ] Set up mobile-specific rate limits
- [ ] Test with React Native/Expo

**Week 4**:
- [ ] Add mobile user agent detection
- [ ] Implement mobile analytics
- [ ] Load testing with mobile traffic
- [ ] Documentation for mobile devs

### Phase 3: Kong Gateway (Month 2-3)

**Month 2**:
- [ ] Set up Kong API Gateway
- [ ] Configure plugins (rate limit, JWT, logging)
- [ ] Migrate Next.js BFF to Kong routes
- [ ] Test parallel deployment

**Month 3**:
- [ ] Full cutover to Kong
- [ ] Remove Next.js BFF (optional)
- [ ] Advanced features (caching, GraphQL federation)
- [ ] Performance optimization

---

## 📊 Performance Considerations

### Latency Impact

| Architecture | Latency | Notes |
|--------------|---------|-------|
| Direct Backend | 50-100ms | Current (baseline) |
| Next.js BFF | +20-40ms | Acceptable for web |
| Kong Gateway | +10-20ms | Production-optimized |

### Caching Strategy

**Next.js Edge Caching**:
```typescript
// app/api/proxy/[...path]/route.ts
export const revalidate = 60; // Cache for 60 seconds

// Or per-route
if (path.startsWith('children')) {
  return NextResponse.json(data, {
    headers: {
      'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
    },
  });
}
```

**Redis Caching** (backend):
```typescript
// Already implemented in backend
@UseInterceptors(CacheInterceptor)
@CacheTTL(300)
async getChildren() {
  // ...
}
```

---

## ✅ Acceptance Criteria

### Security
- [ ] Backend not directly accessible from browser (except WebSocket for now)
- [ ] Internal API key required for BFF → Backend
- [ ] Mobile API key required for mobile → Backend
- [ ] Rate limiting enforced at Nginx and NestJS levels
- [ ] CORS configured for web and mobile origins
- [ ] SSL/TLS for all external connections

### Functionality
- [ ] Web app works through BFF without code changes to components
- [ ] GraphQL queries work through proxy
- [ ] WebSocket connections remain stable
- [ ] Mobile apps can connect directly to backend
- [ ] Real-time sync works across web and mobile

### Performance
- [ ] Latency increase < 50ms for BFF
- [ ] No degradation in WebSocket performance
- [ ] API response times within SLA (<200ms p95)

### Monitoring
- [ ] Request logs include API key and source
- [ ] Rate limit violations logged
- [ ] Error tracking for proxy failures
- [ ] Metrics dashboard shows BFF vs direct traffic

---

## 🔧 Troubleshooting

### Common Issues

**1. WebSocket Connections Failing**
- **Symptom**: Socket.io connection refused
- **Fix**: Ensure WebSocket endpoint bypasses BFF (direct backend connection)
- **Config**: Update `NEXT_PUBLIC_WS_URL` to backend URL

**2. CORS Errors on Mobile**
- **Symptom**: OPTIONS preflight fails
- **Fix**: Add mobile origins to CORS whitelist
- **Config**: Check `capacitor://localhost` is allowed

**3. Rate Limiting Too Aggressive**
- **Symptom**: 429 errors during normal usage
- **Fix**: Adjust Nginx `limit_req` or NestJS throttler
- **Config**: Increase burst size or rate

**4. GraphQL Subscriptions Not Working**
- **Symptom**: Subscriptions disconnect immediately
- **Fix**: GraphQL subscriptions need WebSocket, can't go through HTTP proxy
- **Solution**: Use Apollo Client with split link (HTTP for queries, WS for subscriptions)

---

## 📚 References

- [Next.js API Routes Documentation](https://nextjs.org/docs/api-routes/introduction)
- [Kong API Gateway](https://konghq.com/products/kong-gateway)
- [Nginx Reverse Proxy Guide](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/)
- [NestJS Guards](https://docs.nestjs.com/guards)
- [Socket.io CORS Configuration](https://socket.io/docs/v4/handling-cors/)

---

**Last Updated**: October 3, 2025  
**Review Date**: Post-MVP Launch  
**Owner**: Backend Team
