docs: Create comprehensive API Gateway architecture and security plan
Some checks failed
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled

Create detailed implementation plan for securing backend API while supporting
web, mobile apps, WebSockets, and GraphQL.

## Current State Analysis
- Backend API fully exposed to internet (security risk)
- Direct API calls from browser
- No infrastructure-level rate limiting
- Future mobile apps need direct access
- WebSocket + GraphQL endpoints require special handling

## Proposed Solutions

### Phase 1: MVP - Next.js BFF Pattern (1-2 weeks)
- Next.js API routes as proxy for web app
- Direct backend access for mobile (with API keys)
- Internal API key authentication
- WebSocket remains direct (Next.js limitation)

### Phase 2: Production - Kong Gateway (4-6 weeks post-MVP)
- Centralized API gateway for all clients
- Backend becomes fully internal
- Advanced features: caching, monitoring, GraphQL federation

## Implementation Details

**Files to Create**:
- app/api/proxy/[...path]/route.ts - Generic REST proxy
- app/api/graphql/route.ts - GraphQL proxy
- src/common/guards/internal-api-key.guard.ts - Backend auth

**Security Features**:
- Internal API key for BFF → Backend communication
- Mobile API key for mobile → Backend
- Rate limiting: 100 req/min (web), 60 req/min (mobile)
- Strict CORS configuration
- Nginx reverse proxy with SSL termination

**Architecture Diagrams**:
- BFF pattern with Next.js (recommended for MVP)
- Kong Gateway pattern (production-ready)
- Mobile app integration strategy

**Includes**:
- Step-by-step implementation plan
- Code samples for all components
- Nginx configuration
- Environment variable setup
- Security enhancements
- Performance considerations (+20-40ms latency)
- Monitoring and logging
- Troubleshooting guide
- Deployment timeline

Addresses security concerns while maintaining support for:
 Web app (through BFF)
 Mobile apps (direct with API key)
 Real-time WebSocket
 GraphQL queries and subscriptions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-03 22:49:23 +00:00
parent 648204475d
commit f83d79a5a7

View File

@@ -0,0 +1,942 @@
# 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