# IMPACTO DE CAMBIOS EN API
**Version:** 1.0.0
**Fecha:** 2025-12-08
**Prioridad:** OBLIGATORIA - Consultar antes de modificar endpoints
**Sistema:** SIMCO + CAPVED
---
## PROPOSITO
Documentar el impacto de modificar endpoints REST (Controllers), rutas, metodos HTTP, y contratos de API en el sistema.
---
## 1. CLASIFICACION DE CAMBIOS API
```
╔══════════════════════════════════════════════════════════════════════╗
║ TIPOS DE CAMBIOS EN API ║
╠══════════════════════════════════════════════════════════════════════╣
║ ║
║ BREAKING CHANGES (⚠️ ROMPEN CLIENTES): ║
║ • Eliminar endpoint ║
║ • Cambiar ruta de endpoint ║
║ • Cambiar metodo HTTP ║
║ • Eliminar campo de response ║
║ • Cambiar tipo de campo en response ║
║ • Agregar campo requerido a request ║
║ • Cambiar formato de error ║
║ ║
║ NON-BREAKING CHANGES (✅ COMPATIBLES): ║
║ • Agregar endpoint nuevo ║
║ • Agregar campo opcional a request ║
║ • Agregar campo a response ║
║ • Agregar nuevo codigo de error ║
║ • Mejorar mensaje de error ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
```
---
## 2. CAMBIOS EN RUTAS (BREAKING)
### 2.1 Cambiar Ruta de Endpoint
```typescript
// ANTES
@Get('users/by-email/:email')
// DESPUES
@Get('users/search/email/:email')
```
**Impacto:**
| Componente | Impacto | Accion Requerida |
|------------|---------|------------------|
| Frontend Service | ROMPE | Actualizar URL |
| Frontend Hooks | ROMPE | Actualizar llamadas |
| Documentacion | Desactualizada | Actualizar |
| Clientes externos | ROMPE | Notificar + migrar |
| Tests e2e | ROMPE | Actualizar URLs |
**Proceso de Migracion:**
```
OPCION A: Migracion Inmediata (proyectos internos)
═══════════════════════════════════════════════════════════════
1. Actualizar Frontend primero (cambiar URL)
2. Actualizar Backend (cambiar ruta)
3. Deploy coordinado
OPCION B: Deprecacion Gradual (APIs publicas)
═══════════════════════════════════════════════════════════════
1. Agregar nueva ruta (mantener vieja)
2. Marcar vieja como @Deprecated
3. Notificar clientes
4. Periodo de gracia (30-90 dias)
5. Eliminar ruta vieja
```
**Implementacion Deprecacion:**
```typescript
// Mantener ambas rutas temporalmente
@Get('users/by-email/:email')
@ApiOperation({
summary: 'Buscar por email (DEPRECATED)',
deprecated: true,
description: 'Use GET /users/search/email/:email instead'
})
async findByEmailDeprecated(@Param('email') email: string) {
return this.findByEmail(email);
}
@Get('users/search/email/:email')
@ApiOperation({ summary: 'Buscar usuario por email' })
async findByEmail(@Param('email') email: string) {
return this.userService.findByEmail(email);
}
```
### 2.2 Cambiar Prefijo de Controller
```typescript
// ANTES
@Controller('products')
// DESPUES
@Controller('catalog/products')
```
**Impacto:** TODOS los endpoints del controller cambian de ruta.
```
ANTES:
GET /api/products
POST /api/products
GET /api/products/:id
DESPUES:
GET /api/catalog/products
POST /api/catalog/products
GET /api/catalog/products/:id
```
**Checklist:**
```
[ ] 1. Buscar en frontend: grep -rn "/products" apps/
[ ] 2. Actualizar TODOS los services que usan estos endpoints
[ ] 3. Actualizar tests e2e
[ ] 4. Actualizar documentacion
[ ] 5. Verificar Swagger refleja cambios
```
---
## 3. CAMBIOS EN METODO HTTP (BREAKING)
### Cambiar Metodo
```typescript
// ANTES: Actualizar con POST
@Post(':id/update')
async update() {}
// DESPUES: Actualizar con PUT (RESTful correcto)
@Put(':id')
async update() {}
```
**Impacto Frontend:**
```typescript
// ANTES
const response = await api.post(`/users/${id}/update`, data);
// DESPUES
const response = await api.put(`/users/${id}`, data);
```
**Checklist:**
```
[ ] 1. Identificar todos los lugares que llaman al endpoint
[ ] 2. Actualizar metodo HTTP en frontend service
[ ] 3. Actualizar tests
[ ] 4. Si API publica: periodo de deprecacion
```
---
## 4. CAMBIOS EN REQUEST DTO
### 4.1 Agregar Campo Requerido (BREAKING)
```typescript
// ANTES
export class CreateOrderDto {
productId: string;
quantity: number;
}
// DESPUES - Nuevo campo requerido
export class CreateOrderDto {
productId: string;
quantity: number;
@IsNotEmpty()
deliveryAddress: string; // ← BREAKING: requerido
}
```
**Impacto:**
| Componente | Impacto | Accion |
|------------|---------|--------|
| Frontend Form | ROMPE | Agregar campo |
| Frontend Zod | ROMPE | Agregar validacion |
| Clientes existentes | ROMPE | Falla validacion |
| Tests | ROMPE | Actualizar fixtures |
**Mitigacion:**
```typescript
// OPCION 1: Hacer opcional con default
@IsOptional()
@IsString()
deliveryAddress?: string = 'Por definir';
// OPCION 2: Migrar en fases
// Fase 1: Agregar como opcional
// Fase 2: Poblar datos existentes
// Fase 3: Hacer requerido
```
### 4.2 Agregar Campo Opcional (NON-BREAKING)
```typescript
// Agregar campo opcional - NO rompe
export class CreateOrderDto {
productId: string;
quantity: number;
@IsOptional()
@IsString()
notes?: string; // ← NON-BREAKING: opcional
}
```
**Impacto:**
| Componente | Impacto | Accion |
|------------|---------|--------|
| Frontend | Ninguno inmediato | Agregar si se quiere usar |
| Clientes existentes | Ninguno | Siguen funcionando |
| Tests | Ninguno | Siguen pasando |
### 4.3 Eliminar Campo de Request (BREAKING si requerido)
```typescript
// ANTES
export class CreateUserDto {
email: string;
password: string;
legacyCode: string; // ← Se va a eliminar
}
// DESPUES
export class CreateUserDto {
email: string;
password: string;
// legacyCode eliminado
}
```
**Proceso:**
```
1. Hacer campo opcional primero (si era requerido)
2. Marcar como @Deprecated en Swagger
3. Ignorar en backend (no procesar)
4. Notificar clientes
5. Eliminar despues de periodo de gracia
```
### 4.4 Cambiar Validacion (Puede ser BREAKING)
```typescript
// ANTES: email cualquier formato
@IsString()
email: string;
// DESPUES: email debe ser valido
@IsEmail()
email: string;
```
**Impacto:** Requests que antes pasaban ahora fallan.
**Mitigacion:**
```typescript
// Agregar transformacion para casos edge
@IsEmail()
@Transform(({ value }) => value?.toLowerCase().trim())
email: string;
```
---
## 5. CAMBIOS EN RESPONSE DTO
### 5.1 Eliminar Campo de Response (BREAKING)
```typescript
// ANTES
export class UserResponseDto {
id: string;
email: string;
legacyCode: string; // ← Se elimina
}
// DESPUES
export class UserResponseDto {
id: string;
email: string;
// legacyCode eliminado
}
```
**Impacto Frontend:**
```typescript
// Si frontend usa el campo, ROMPE
const UserCard = ({ user }) => {
return
{user.legacyCode}
; // ← TypeError: undefined
};
```
**Proceso Seguro:**
```
1. Buscar uso en frontend: grep -rn "legacyCode" apps/
2. Eliminar uso en frontend primero
3. Luego eliminar de ResponseDto
4. Deploy coordinado
```
### 5.2 Agregar Campo a Response (NON-BREAKING)
```typescript
// Agregar campo - NO rompe
export class UserResponseDto {
id: string;
email: string;
createdAt: Date; // ← Nuevo campo
}
```
**Impacto:**
- Frontend puede ignorar campos nuevos
- TypeScript mostrara warning si interface no coincide
- Actualizar interface en frontend eventualmente
### 5.3 Cambiar Tipo de Campo (BREAKING)
```typescript
// ANTES
export class ProductResponseDto {
price: number; // 99
}
// DESPUES
export class ProductResponseDto {
price: string; // "99.00"
}
```
**Impacto Frontend:**
```typescript
// ANTES funcionaba
const total = product.price * quantity;
// DESPUES rompe (string * number = NaN)
const total = product.price * quantity; // NaN!
// Debe cambiar a
const total = parseFloat(product.price) * quantity;
```
### 5.4 Cambiar Estructura de Response (BREAKING)
```typescript
// ANTES: Array directo
@Get()
async findAll(): Promise {
return this.users;
}
// Response: [{ id: 1 }, { id: 2 }]
// DESPUES: Objeto paginado
@Get()
async findAll(): Promise> {
return { data: this.users, total: 100, page: 1 };
}
// Response: { data: [...], total: 100, page: 1 }
```
**Impacto Frontend:**
```typescript
// ANTES
const users = await api.get('/users');
users.map(u => ...);
// DESPUES
const response = await api.get('/users');
response.data.map(u => ...); // Acceder a .data
```
---
## 6. CAMBIOS EN CODIGOS DE ERROR
### 6.1 Agregar Nuevo Codigo (NON-BREAKING)
```typescript
// Agregar nueva excepcion - generalmente no rompe
throw new ConflictException('Email already registered');
// HTTP 409 - nuevo codigo
```
**Impacto:** Frontend deberia manejar, pero no rompe si no lo hace.
### 6.2 Cambiar Codigo Existente (BREAKING)
```typescript
// ANTES: 400 para duplicado
throw new BadRequestException('Email exists');
// DESPUES: 409 para duplicado (correcto)
throw new ConflictException('Email exists');
```
**Impacto:** Frontend que verifica status code especifico rompe.
```typescript
// Frontend que rompe
if (error.status === 400 && error.message.includes('Email')) {
// Ya no entra aqui
}
// Frontend robusto
if (error.status === 409 || error.message.includes('Email')) {
// Maneja ambos casos
}
```
### 6.3 Cambiar Formato de Error (BREAKING)
```typescript
// ANTES
{
"statusCode": 400,
"message": "Validation failed"
}
// DESPUES
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [...]
}
}
```
**Impacto:** TODO el manejo de errores en frontend debe cambiar.
---
## 7. VERSIONADO DE API
### Cuando Usar Versionado
```
USA VERSIONADO CUANDO:
• API es consumida por clientes externos
• Cambios breaking frecuentes
• Necesitas mantener compatibilidad largo plazo
NO NECESITAS VERSIONADO CUANDO:
• Solo frontend interno consume la API
• Puedes coordinar deploys
• Equipo pequeno con comunicacion directa
```
### Implementacion de Versiones
```typescript
// Opcion 1: En URL
@Controller('v1/users')
export class UsersV1Controller {}
@Controller('v2/users')
export class UsersV2Controller {}
// Opcion 2: En Header
@Controller('users')
@ApiHeader({ name: 'Api-Version', enum: ['1', '2'] })
export class UsersController {
@Get()
findAll(@Headers('Api-Version') version: string) {
if (version === '2') {
return this.findAllV2();
}
return this.findAllV1();
}
}
```
---
## 8. CHECKLIST POR TIPO DE CAMBIO
### Agregar Endpoint Nuevo
```
[ ] 1. Crear metodo en Controller
[ ] 2. Agregar decoradores Swagger (@ApiOperation, @ApiResponse)
[ ] 3. Crear/actualizar DTOs si necesario
[ ] 4. Implementar en Service
[ ] 5. Agregar tests
[ ] 6. Actualizar Frontend service
[ ] 7. Crear Frontend hook si necesario
[ ] 8. Verificar Swagger
```
### Modificar Endpoint Existente (NON-BREAKING)
```
[ ] 1. Verificar que cambio es non-breaking
[ ] 2. Actualizar Controller
[ ] 3. Actualizar DTOs si necesario
[ ] 4. Actualizar Swagger decoradores
[ ] 5. Actualizar tests
[ ] 6. Actualizar Frontend si aprovecha nuevas features
```
### Modificar Endpoint Existente (BREAKING)
```
[ ] 1. Evaluar: ¿se puede hacer non-breaking?
[ ] 2. Buscar todos los consumidores: grep -rn "endpoint" apps/
[ ] 3. Planear estrategia de migracion
[ ] 4. Si API publica: crear version nueva, deprecar vieja
[ ] 5. Si API interna: coordinar con frontend
[ ] 6. Actualizar Frontend PRIMERO (apuntar a nuevo)
[ ] 7. Actualizar Backend
[ ] 8. Actualizar tests
[ ] 9. Deploy coordinado
[ ] 10. Eliminar codigo deprecated despues de periodo
```
### Eliminar Endpoint
```
[ ] 1. Buscar todos los usos: grep -rn "endpoint" apps/ tests/
[ ] 2. Eliminar uso en Frontend
[ ] 3. Eliminar tests del endpoint
[ ] 4. Marcar como @Deprecated (si API publica)
[ ] 5. Periodo de gracia
[ ] 6. Eliminar de Controller
[ ] 7. Limpiar Service si metodos quedan sin usar
```
---
## 9. FRONTEND: ADAPTARSE A CAMBIOS API
### Service Layer Pattern
```typescript
// Encapsular llamadas API en service
// Facilita cambiar cuando API cambia
// user.service.ts
export const userService = {
// Si cambia la ruta, solo cambiar aqui
getAll: () => api.get('/users'),
// Si cambia estructura response
getAllPaginated: async (page: number) => {
const response = await api.get>('/users', { params: { page } });
return response.data; // Extraer data aqui
},
// Si cambia metodo HTTP
update: (id: string, data: UpdateUserDto) =>
api.put(`/users/${id}`, data), // Cambiar post→put aqui
};
```
### Type Guards para Cambios de Estructura
```typescript
// Manejar diferentes versiones de response
interface UserV1 {
name: string;
}
interface UserV2 {
firstName: string;
lastName: string;
}
function isUserV2(user: UserV1 | UserV2): user is UserV2 {
return 'firstName' in user;
}
function getDisplayName(user: UserV1 | UserV2): string {
if (isUserV2(user)) {
return `${user.firstName} ${user.lastName}`;
}
return user.name;
}
```
---
## 10. MATRIZ DE DECISION
```
¿Es BREAKING CHANGE?
│
▼
┌────────────┐
│ ¿Cambio de │───NO───▶ Implementar directamente
│ ruta? │ Actualizar Swagger
└────────────┘ Actualizar Frontend (opcional)
│
YES
│
▼
┌────────────┐
│ ¿API │───NO───▶ Actualizar Frontend PRIMERO
│ publica? │ Luego actualizar Backend
└────────────┘ Deploy coordinado
│
YES
│
▼
┌────────────────────────────────────────┐
│ 1. Crear endpoint nuevo │
│ 2. Deprecar endpoint viejo │
│ 3. Notificar clientes │
│ 4. Periodo de gracia (30-90 dias) │
│ 5. Eliminar endpoint viejo │
└────────────────────────────────────────┘
```
---
## 11. COMANDOS UTILES
```bash
# Ver todos los endpoints actuales
curl http://localhost:3000/api-json | jq '.paths | keys'
# Ver detalle de un endpoint
curl http://localhost:3000/api-json | jq '.paths["/users/{id}"]'
# Buscar uso de endpoint en frontend
grep -rn "users" apps/*/services/
grep -rn "/api/users" apps/
# Comparar Swagger entre versiones
diff <(curl -s http://localhost:3000/api-json | jq -S) \
<(curl -s http://production/api-json | jq -S)
```
---
**Version:** 1.0.0 | **Sistema:** SIMCO | **Tipo:** Guia de Impacto