Client‐Server - netfoor/AmplifyWorkshop-RetailStore GitHub Wiki
En aplicaciones full-stack (como las construidas con Next.js), existen dos patrones para manejar datos:
- Llamadas directas desde el cliente (Frontend → Backend).
- Rutas de API intermedias (Frontend → API Route → Backend).
Este documento explica cuándo y por qué usar cada uno, basado en buenas prácticas de AWS y Next.js.
// Ejemplo: Frontend accede directamente a DynamoDB
const { data } = await client.models.Product.list({ limit: 5 });
- Rápido de implementar: Ideal para prototipos.
- Menos código: Amplify maneja la conexión.
- Seguridad limitada: Credenciales temporales expuestas en el cliente.
- Acoplamiento fuerte: Cambios en el esquema de DB afectan al frontend.
- Poca flexibilidad: Difícil añadir lógica intermedia (ej: filtrar datos).
// Ejemplo: Frontend llama a una ruta de API
const res = await fetch('/api/products');
const data = await res.json();
- Seguridad: El servidor valida permisos y filtra datos.
- Flexibilidad: Modificas la lógica sin cambiar el frontend.
- Performance: Caching, batching, y optimizaciones en el servidor.
- Escalabilidad: Puedes migrar a microservicios fácilmente.
- Más código: Requiere definir rutas de API.
- Latencia adicional: Un hop más en la red.
Criterio | Llamada Directa (Cliente) | API Route (Servidor) |
---|---|---|
Tipo de App | Prototipos/MVP | Apps empresariales |
Seguridad Requerida | Baja (datos públicos) | Alta (datos privados) |
Flexibilidad | Limitada | Alta (transformaciones en servidor) |
Mantenibilidad | Frágil ante cambios | Robustecida |
src/
├── app/
│ ├── api/
│ │ └── products/
│ │ └── route.ts # API Route (Server)
├── components/
│ └── ProductList.tsx # Componente (Client)
import { NextResponse } from 'next/server';
import { cookiesClient } from '@/utils/server-utils';
export async function GET() {
try {
const { data, errors } = await cookiesClient.models.Product.list({ limit: 5 });
if (errors) throw new Error('Error fetching products');
return NextResponse.json(data);
} catch (error) {
return NextResponse.json({ error: 'Server error' }, { status: 500 });
}
}
'use client';
import { useState, useEffect } from 'react';
export default function ProductList() {
const [products, setProducts] = useState([]);
useEffect(() => {
const fetchProducts = async () => {
const res = await fetch('/api/products');
const data = await res.json();
setProducts(data);
};
fetchProducts();
}, []);
return <div>{/* Renderizar productos */}</div>;
}
sequenceDiagram
participant Client
participant APIRoute
participant DynamoDB
Client->>APIRoute: GET /api/products
APIRoute->>DynamoDB: client.models.Product.list()
DynamoDB-->>APIRoute: { data }
APIRoute-->>Client: Response.json(data)
-
Usa API Routes para:
- Autenticación/autorización.
- Acceso a bases de datos.
- Procesamiento pesado (ej: generación de PDFs).
-
Usa llamadas directas solo para:
- Datos públicos no sensibles.
- Prototipos rápidos.
-
Documenta tus APIs:
- Usa Swagger o OpenAPI para rutas complejas.
### 🔥 **Conclusión**
Elegir entre llamadas directas y API Routes depende de la **seguridad**, **escalabilidad** y **flexibilidad** que necesites. Para apps profesionales, **siempre prefiere API Routes**.