# 🔗 Integração BFF ↔ Laravel API

Documentação completa da integração entre o BFF NestJS e a API Laravel, com autenticação JWT unificada e proxy transparente.

## 🎯 Visão Geral

A integração permite que o BFF atue como um proxy inteligente para a API Laravel, mantendo a autenticação JWT e fornecendo funcionalidades adicionais como cache, agregação de dados e validações.

### ✨ Funcionalidades

- 🔐 **JWT Compartilhado** - Mesmo token funciona em ambos os sistemas
- 🔄 **Proxy Transparente** - Repassa requisições mantendo autenticação
- 📊 **Agregação de Dados** - Combina dados de múltiplas fontes
- 🎯 **Validação Centralizada** - Valida tokens localmente no BFF
- 📝 **Debug Avançado** - Ferramentas para troubleshooting

## ⚙️ Configuração

### Variáveis de Ambiente

```env
# Configuração da API Laravel
LARAVEL_API_URL=http://localhost:8000

# JWT (deve ser igual ao da API Laravel)
JWT_SECRET=sua-chave-secreta-jwt-compartilhada
JWT_ALGORITHM=HS256
JWT_TTL=3600
```

### Serviço de Integração

O `LaravelApiService` é responsável por toda comunicação com a API Laravel:

```typescript
@Injectable()
export class LaravelApiService {
  constructor(private readonly httpService: HttpService) {}

  // Proxy genérico para qualquer endpoint Laravel
  async proxyRequest(
    token: string,
    method: string,
    endpoint: string,
    data?: any,
    headers?: Record<string, string>
  ): Promise<any> {
    // Implementação com tratamento de erros e logs
  }

  // Validar token diretamente com Laravel
  async validateTokenWithLaravel(token: string): Promise<any> {
    // Validação remota
  }

  // Buscar dados do usuário no Laravel
  async getUserFromLaravel(token: string): Promise<any> {
    // Busca dados completos do usuário
  }
}
```

## 🚀 Endpoints Disponíveis

### 1. Health Check da Integração

```bash
GET /laravel-api/health
Authorization: Bearer seu-token-jwt
```

Verifica se a integração com Laravel está funcionando.

**Resposta:**
```json
{
  "status": "OK",
  "laravel": {
    "connected": true,
    "responseTime": "45ms"
  },
  "token": {
    "valid": true,
    "expiresIn": "2h 30m"
  }
}
```

### 2. Validação de Token

```bash
POST /laravel-api/validate-token
Authorization: Bearer seu-token-jwt
```

Valida o token JWT diretamente com a API Laravel.

**Resposta:**
```json
{
  "valid": true,
  "user": {
    "id": "123",
    "email": "user@example.com",
    "name": "João Silva"
  },
  "tokenInfo": {
    "expiresAt": "2024-01-01T15:00:00Z",
    "issuedAt": "2024-01-01T12:00:00Z"
  }
}
```

### 3. Perfil do Usuário

```bash
GET /laravel-api/user/profile
Authorization: Bearer seu-token-jwt
```

Busca o perfil completo do usuário na API Laravel.

### 4. Proxy Genérico

```bash
POST /laravel-api/proxy/{endpoint}
Authorization: Bearer seu-token-jwt
Content-Type: application/json

{
  "method": "GET|POST|PUT|DELETE|PATCH",
  "params": { "key": "value" },
  "headers": { "Custom-Header": "value" }
}
```

**Exemplo - Listar Portfolios:**
```bash
POST /laravel-api/proxy/api/user/portfolios
Authorization: Bearer seu-token-jwt
Content-Type: application/json

{
  "method": "GET",
  "params": { "limit": 10, "page": 1 }
}
```

### 5. Debug do Token

```bash
GET /auth/token/debug
Authorization: Bearer seu-token-jwt
```

Fornece informações completas sobre o token JWT.

**Resposta:**
```json
{
  "message": "Debug completo do token JWT",
  "authentication": {
    "id": "123",
    "type": "user",
    "expiresAt": "2024-01-01T15:00:00Z",
    "issuedAt": "2024-01-01T12:00:00Z"
  },
  "rawToken": {
    "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
    "length": 245,
    "parts": 3
  },
  "tokenData": {
    "issuer": "http://localhost:8000/api/login",
    "jwtId": "abc123",
    "provider": "users",
    "uuid": "user-uuid-123",
    "email": "user@example.com",
    "name": "João Silva"
  },
  "laravelCompatible": true,
  "instructions": {
    "message": "Este token pode ser usado diretamente com a API Laravel",
    "example": "curl -H \"Authorization: Bearer TOKEN\" http://localhost:8000/api/user"
  }
}
```

## 💻 Usando nos Controllers

### Exemplo Básico

```typescript
import { Controller, Get, UseGuards } from '@nestjs/common';
import { UserJwtGuard } from '../auth/guards/user-jwt.guard';
import { User } from '../auth/decorators/user.decorator';
import { Token } from '../auth/decorators/token.decorator';
import { LaravelApiService } from '../laravel-api/laravel-api.service';

@Controller('meu-controller')
export class MeuController {
  constructor(private readonly laravelApiService: LaravelApiService) {}

  @Get('exemplo')
  @UseGuards(UserJwtGuard)
  async exemploIntegracao(
    @User() user: AuthenticatedUser,
    @Token() token: string,
  ) {
    // 1. Dados do BFF (extraídos do token)
    const dadosBFF = {
      id: user.id,
      type: user.type,
      expiresAt: user.expiresAt
    };

    // 2. Requisição para Laravel mantendo autenticação
    const responseLaravel = await this.laravelApiService.proxyRequest(
      token,
      'GET',
      '/api/user/portfolios'
    );

    // 3. Combinar dados de ambas as fontes
    return {
      bff: dadosBFF,
      laravel: responseLaravel.data,
      timestamp: new Date().toISOString()
    };
  }
}
```

### Exemplo Avançado com Agregação

```typescript
@Get('dashboard-completo')
@UseGuards(UserJwtGuard)
async getDashboardCompleto(
  @Token() token: string,
  @UserUuid() uuid: string,
  @UserEmail() email: string,
) {
  try {
    // Requisições paralelas para Laravel
    const [portfolios, assets, transactions] = await Promise.all([
      this.laravelApiService.proxyRequest(token, 'GET', '/api/user/portfolios'),
      this.laravelApiService.proxyRequest(token, 'GET', '/api/user/assets'),
      this.laravelApiService.proxyRequest(token, 'GET', '/api/user/transactions')
    ]);

    // Dados locais do BFF (cache, cálculos, etc.)
    const localData = await this.getLocalUserData(uuid);

    // Agregação e processamento
    const dashboard = {
      user: { uuid, email },
      portfolios: portfolios.data,
      assets: assets.data,
      transactions: transactions.data.slice(0, 10), // Últimas 10
      summary: this.calculateSummary(portfolios.data, assets.data),
      localData,
      lastUpdate: new Date().toISOString()
    };

    return dashboard;
  } catch (error) {
    throw new BadRequestException('Erro ao buscar dados do dashboard');
  }
}
```

## 🎨 Decorators Específicos

### Decorators Disponíveis

```typescript
// Token JWT completo
@Token() token: string

// Dados extraídos do token
@TokenData() allData: Record<string, any>
@TokenData('email') email: string
@TokenData('uuid') uuid: string

// Payload JWT completo
@JwtPayload() payload: JwtPayload
@JwtPayload('sub') userId: string

// Campos específicos (shortcuts)
@UserUuid() uuid: string
@UserEmail() email: string  
@UserName() name: string

// Usuário completo
@User() user: AuthenticatedUser
@User('id') userId: string
```

### Exemplo de Uso

```typescript
@Get('user-details')
@UseGuards(UserJwtGuard)
async getUserDetails(
  @Token() token: string,
  @UserUuid() uuid: string,
  @UserEmail() email: string,
  @UserName() name: string,
  @TokenData('role') role: string,
  @JwtPayload('exp') expiresAt: number,
) {
  return {
    tokenInfo: {
      hasToken: !!token,
      expiresAt: new Date(expiresAt * 1000)
    },
    userInfo: { uuid, email, name, role },
    canAccessLaravel: true
  };
}
```

## 🔄 Fluxo de Integração

```mermaid
sequenceDiagram
    participant F as Frontend
    participant B as BFF
    participant L as Laravel API
    
    F->>B: Request + JWT Token
    B->>B: Validate JWT locally
    B->>B: Extract user data
    B->>L: Proxy request + JWT Token
    L->>L: Validate JWT
    L->>L: Process request
    L->>B: Response
    B->>B: Aggregate/Process data
    B->>F: Combined response
```

### Detalhes do Fluxo

1. **Frontend** envia requisição com token JWT para BFF
2. **BFF** valida o token localmente usando a mesma chave
3. **BFF** extrai dados do usuário do token
4. **BFF** pode processar dados localmente ou fazer proxy para Laravel
5. **Laravel** recebe requisição com o mesmo token JWT
6. **Laravel** valida token e processa requisição
7. **BFF** recebe resposta e pode agregar com dados locais
8. **BFF** retorna resposta otimizada para o frontend

## 🧪 Testando a Integração

### 1. Teste Básico de Conectividade

```bash
# Health check da integração
curl -H "Authorization: Bearer SEU_TOKEN" \
  http://localhost:3333/laravel-api/health
```

### 2. Teste de Validação de Token

```bash
# Validar token com Laravel
curl -X POST \
  -H "Authorization: Bearer SEU_TOKEN" \
  http://localhost:3333/laravel-api/validate-token
```

### 3. Teste de Proxy

```bash
# Listar usuários via proxy
curl -X POST \
  -H "Authorization: Bearer SEU_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"method": "GET"}' \
  http://localhost:3333/laravel-api/proxy/api/user
```

### 4. Debug Completo

```bash
# Debug do token
curl -H "Authorization: Bearer SEU_TOKEN" \
  http://localhost:3333/auth/token/debug

# Compatibilidade com Laravel
curl -H "Authorization: Bearer SEU_TOKEN" \
  http://localhost:3333/auth/laravel/compatibility
```

## 🚨 Tratamento de Erros

### Códigos de Erro Comuns

| Código | Descrição | Causa Provável |
|--------|-----------|----------------|
| **401** | Unauthorized | Token inválido ou expirado |
| **403** | Forbidden | Token válido mas sem permissão |
| **502** | Bad Gateway | Laravel API indisponível |
| **504** | Gateway Timeout | Laravel API não respondeu |

### Exemplo de Tratamento

```typescript
async proxyToLaravel(token: string, endpoint: string) {
  try {
    const response = await this.laravelApiService.proxyRequest(
      token, 'GET', endpoint
    );
    return response.data;
  } catch (error) {
    if (error.response?.status === 401) {
      throw new UnauthorizedException('Token inválido na API Laravel');
    }
    if (error.response?.status === 502) {
      throw new BadGatewayException('API Laravel indisponível');
    }
    throw new InternalServerErrorException('Erro na integração com Laravel');
  }
}
```

## 🔧 Configuração Avançada

### Cache de Respostas

```typescript
@Injectable()
export class CachedLaravelService {
  private cache = new Map<string, any>();
  
  async getCachedUserData(token: string, userId: string) {
    const cacheKey = `user_${userId}`;
    
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }
    
    const userData = await this.laravelApiService.getUserFromLaravel(token);
    this.cache.set(cacheKey, userData);
    
    // Expira cache em 5 minutos
    setTimeout(() => this.cache.delete(cacheKey), 5 * 60 * 1000);
    
    return userData;
  }
}
```

### Retry Logic

```typescript
async proxyWithRetry(token: string, endpoint: string, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await this.laravelApiService.proxyRequest(token, 'GET', endpoint);
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
      
      // Espera exponencial
      await new Promise(resolve => 
        setTimeout(resolve, Math.pow(2, attempt) * 1000)
      );
    }
  }
}
```

## 📊 Monitoramento e Logs

### Logs Estruturados

```typescript
@Injectable()
export class LaravelApiService {
  private readonly logger = new Logger(LaravelApiService.name);

  async proxyRequest(token: string, method: string, endpoint: string) {
    const startTime = Date.now();
    
    this.logger.log(`Proxy request: ${method} ${endpoint}`);
    
    try {
      const response = await this.httpService.request({
        method,
        url: `${this.laravelApiUrl}${endpoint}`,
        headers: { Authorization: `Bearer ${token}` }
      }).toPromise();
      
      const duration = Date.now() - startTime;
      this.logger.log(`Proxy success: ${method} ${endpoint} (${duration}ms)`);
      
      return response;
    } catch (error) {
      const duration = Date.now() - startTime;
      this.logger.error(
        `Proxy error: ${method} ${endpoint} (${duration}ms)`,
        error.message
      );
      throw error;
    }
  }
}
```

### Métricas

```typescript
@Injectable()
export class MetricsService {
  private requests = 0;
  private errors = 0;
  private totalResponseTime = 0;

  recordRequest(responseTime: number, isError = false) {
    this.requests++;
    this.totalResponseTime += responseTime;
    if (isError) this.errors++;
  }

  getStats() {
    return {
      totalRequests: this.requests,
      totalErrors: this.errors,
      averageResponseTime: this.totalResponseTime / this.requests,
      errorRate: (this.errors / this.requests) * 100
    };
  }
}
```

## 🔐 Segurança

### Validação de Token

```typescript
async validateToken(token: string): Promise<boolean> {
  try {
    // Validação local (rápida)
    const payload = jwt.verify(token, process.env.JWT_SECRET);
    
    // Validação remota (opcional, para casos críticos)
    if (this.requireRemoteValidation) {
      await this.laravelApiService.validateTokenWithLaravel(token);
    }
    
    return true;
  } catch (error) {
    this.logger.warn(`Token validation failed: ${error.message}`);
    return false;
  }
}
```

### Rate Limiting

```typescript
@Injectable()
export class RateLimitService {
  private requests = new Map<string, number[]>();

  isRateLimited(userId: string, maxRequests = 100, windowMs = 60000): boolean {
    const now = Date.now();
    const userRequests = this.requests.get(userId) || [];
    
    // Remove requisições antigas
    const validRequests = userRequests.filter(time => now - time < windowMs);
    
    if (validRequests.length >= maxRequests) {
      return true;
    }
    
    validRequests.push(now);
    this.requests.set(userId, validRequests);
    return false;
  }
}
```

## 🎯 Melhores Práticas

### Performance

1. **Use cache** para dados que não mudam frequentemente
2. **Implemente retry logic** para requisições críticas
3. **Faça requisições paralelas** quando possível
4. **Monitore tempo de resposta** da API Laravel

### Segurança

1. **Valide tokens localmente** sempre que possível
2. **Use HTTPS** em produção
3. **Implemente rate limiting** para prevenir abuso
4. **Monitore tentativas de acesso inválidas**

### Manutenibilidade

1. **Documente endpoints customizados**
2. **Use logs estruturados** para debugging
3. **Implemente health checks** para monitoramento
4. **Mantenha versioning** da API

---

## 📚 Referências

- [NestJS HTTP Module](https://docs.nestjs.com/techniques/http-module)
- [Laravel JWT Auth](https://jwt-auth.readthedocs.io/)
- [Axios Documentation](https://axios-http.com/docs/intro)
- [JWT Best Practices](https://auth0.com/blog/a-look-at-the-latest-draft-for-jwt-bcp/)
