API REST — Materiales
docs/api/materials.md
API REST — Materiales
Documentación del endpoint REST para consulta del catálogo de materiales. Opera en el contexto de servidor (Next.js Route Handler) y aplica control de acceso por rol.
Descripción General
El endpoint GET /api/materials expone el catálogo de materiales con soporte para paginación, búsqueda por texto y filtros de categorización. La respuesta varía según el rol del usuario que realiza la petición:
- Rol
admin: consulta en vivo a Firestore con filtros compuestos aplicados directamente en la base de datos. - Otros roles / sin token: devuelve el catálogo desde caché ISR de Next.js con filtrado en memoria.
Archivo
app/api/materials/route.ts
🔌 Endpoints
GET /api/materials
Retorna una lista paginada de materiales activos.
Query Parameters
| Parámetro | Tipo | Default | Descripción |
|---|---|---|---|
page | number | 1 | Número de página (mínimo: 1) |
limit | number | 50 | Items por página (mínimo: 1, máximo: 200) |
search | string | "" | Filtro de búsqueda por nombre (normalizado, sin diacríticos) |
groupId | string | — | Filtra por ID de rubro |
categoryId | string | — | Filtra por ID de categoría |
subcategoryId | string | — | Filtra por ID de subcategoría |
Headers
| Header | Descripción |
|---|---|
Authorization | Bearer <idToken> — opcional. Si se provee un token válido con rol admin, se salta la caché. |
Respuesta exitosa (200 OK)
{
"data": [
{
"id": "mat-abc123",
"name": "Cemento Portland",
"groupId": "grp-001",
"groupName": "Materiales estructurales",
"categoryId": "cat-002",
"categoryName": "Cementos",
"subcategoryId": "sub-003",
"subcategoryName": "Portland tipo I",
"isActive": true,
"createdAt": "...",
"updatedAt": "..."
}
],
"meta": {
"totalItems": 120,
"totalPages": 3,
"currentPage": 1,
"limit": 50
}
}
Respuesta de error (500)
{ "error": "Error al obtener los materiales" }
🔐 Lógica de Autorización
Request → resolveIsAdmin(request)
├─ Sin header Authorization → isAdmin = false
├─ Token inválido / expirado → isAdmin = false (nunca lanza error)
└─ Token válido + role === 'admin' → isAdmin = true
La función resolveIsAdmin nunca lanza una excepción: ante cualquier error de verificación retorna false y el handler continúa con el path de caché.
🔄 Flujo de Datos por Rol
┌─────────────────────────────────────┐
│ GET /api/materials │
└──────────────────┬──────────────────┘
│
resolveIsAdmin(request)
│
┌──────────────────────┴──────────────────────┐
│ isAdmin = true isAdmin = false │
▼ ▼
getMaterials(filters) getCachedMaterials()
(Firestore live query) ('use cache' ISR)
(Admin SDK, sin rules) (cacheTag: 'materials')
│ │
│ applyFilters(materials, filters)
│ (en memoria)
└──────────────────────┬──────────────────────┘
│
search ? filter by name (normalizeText)
│
paginate
│
NextResponse.json(...)
📊 Índices de Firestore Requeridos
Los filtros compuestos en el path de admin requieren índices en Firestore (configurados en firestore.indexes.json):
| Campos del índice | Uso |
|---|---|
isActive + groupId | Filtro solo por rubro |
isActive + categoryId | Filtro solo por categoría |
isActive + subcategoryId | Filtro solo por subcategoría |
isActive + groupId + categoryId | Filtro por rubro y categoría |
isActive + groupId + categoryId + subcategoryId | Filtro por los tres niveles de categorización |
Nota: Los índices deben estar desplegados en el proyecto de Firebase antes de que las consultas admin con múltiples filtros funcionen en producción. Se despliegan con
firebase deploy --only firestore:indexes.
Notas de Diseño
- Sin caché para admin: El rol
adminsiempre recibe datos en tiempo real. Esto permite ver materiales recién creados sin esperar la invalidación de caché. - Búsqueda normalizada: El parámetro
searchcompara textos normalizados (sin acentos, en minúsculas) tanto sobre el término de búsqueda como sobre el nombre del material. Esto permite buscar "cemento" y encontrar "Cémento". - Límite de 200 items: El parámetro
limittiene un tope de 200 para evitar respuestas excesivamente grandes. Para cargas masivas se recomienda la importación Excel directa víacreateMaterialsBatch.
