Portfolio API
API REST privada que sirve todo el contenido de jmrg.dev: entradas, proyectos y los documentos del CMS por idioma. Fastify 5 sobre HTTP/2, Mongoose 9 y consultas públicas y de administración separadas, con autenticación de Clerk en todas las rutas salvo el healthcheck y el preflight.
Node.js · TypeScript · Fastify · MongoDB · Mongoose · Clerk · Zod · Docker

Qué resuelve
El portfolio anterior guardaba los datos y la interfaz en el mismo despliegue: cambiar una entrada del blog obligaba a reconstruir la web entera. Aquí el contenido tiene su propio servicio, con su contrato y su ciclo de vida, y el frontal solo lo consume.
Decisiones que vale la pena contar
La API es privada de principio a fin. Cada petición exige un token, incluidas las lecturas públicas como el listado del blog. Quedan exentos el healthcheck y el preflight de CORS, que por definición no puede llevar credenciales. El frontal hace sus peticiones desde el servidor, de modo que ese token no llega nunca al navegador.
Las consultas públicas y las de administración no son la misma. El listado público filtra por publicado y recorta los campos que no consume nadie; el de administración devuelve el documento entero.
Paginación opt-in. Sin parámetros, el listado devuelve un array plano; con page y limit, un
sobre con sus metadatos. El orden lleva desempate por identificador, porque ordenar solo por fecha
repetía documentos entre páginas cuando dos compartían la misma.
Multi-idioma por documento
Cada pieza de contenido existe una vez por idioma, con la pareja identificador e idioma como clave única. Los idiomas no son simétricos a propósito: un artículo puede publicarse en español antes de tener versión inglesa. El frontal declara en sus etiquetas alternativas solo los idiomas que existen de verdad, en lugar de anunciar una traducción que devolvería un 404.
Stack
| Capa | Tecnología |
|---|---|
| Runtime | Node 24, TypeScript estricto |
| HTTP | Fastify 5 sobre HTTP/2 |
| Persistencia | MongoDB con Mongoose 9 |
| Autenticación | Clerk, con lista de correos para el panel |
| Validación | Zod en el borde de cada ruta |
| Errores | Problem Details (RFC 9457) |