API REST desarrollada con Node.js, Express, MongoDB y Dotenv para una plataforma de recetas culinarias donde los usuarios pueden registrarse, agregar recetas con ingredientes, y buscar recetas por ingrediente.
Video : https://youtu.be/KGJ0hbasVx4
- Gestión de usuarios: Registro, consulta, actualización y eliminación
- Gestión de recetas: Creación, edición, eliminación y consulta por usuario
- Gestión de ingredientes: Agregar ingredientes a recetas, buscar recetas por ingrediente
- Búsqueda avanzada: Encontrar recetas que contengan ingredientes específicos
- Datos de prueba: Script de inicialización con datos de ejemplo
- Node.js - Runtime de JavaScript
- Express - Framework web
- MongoDB - Base de datos NoSQL
- Dotenv - Gestión de variables de entorno
- Node.js (versión 14 o superior)
- MongoDB (local o en la nube)
- npm o yarn
-
Clonar el repositorio
git clone <repository-url> cd YummiAPI
-
Instalar dependencias
npm install
-
Configurar variables de entorno Crear un archivo
.enven la raíz del proyecto:PORT=3000 HOST_NAME=localhost MONGO_URI=mongodb://localhost:27017 DB_NAME=yummi_recetas
-
Inicializar datos de prueba
node src/dataset.js
-
Ejecutar el servidor
# Desarrollo npm run dev # Producción npm start
http://localhost:3000
GET /healthRespuesta:
{
"message": "API de Recetas Culinarias activa!!! 🍳"
}GET /usuariosDescripción: Obtiene una lista completa de todos los usuarios registrados en la plataforma. Este endpoint es útil para administradores que necesitan ver un panorama general de la base de usuarios.
Parámetros: Ninguno
Headers requeridos: Ninguno
Respuesta exitosa (200):
{
"message": "Usuarios obtenidos exitosamente",
"count": 3,
"usuarios": [
{
"id": 1,
"nombre": "María García",
"email": "maria.garcia@email.com",
"fechaRegistro": "2024-01-15T10:30:00Z"
}
]
}Casos de uso:
- Panel de administración para ver todos los usuarios
- Estadísticas generales de la plataforma
- Verificación de usuarios registrados
Posibles errores:
500- Error interno del servidor si hay problemas con la base de datos
GET /usuarios/:idDescripción: Obtiene la información detallada de un usuario específico mediante su ID único. Este endpoint es fundamental para perfiles de usuario y operaciones que requieren verificar la existencia de un usuario.
Parámetros de ruta:
id(number, requerido): ID único del usuario a consultar
Headers requeridos: Ninguno
Respuesta exitosa (200):
{
"message": "Usuario obtenido exitosamente",
"usuario": {
"id": 1,
"nombre": "María García",
"email": "maria.garcia@email.com",
"fechaRegistro": "2024-01-15T10:30:00Z"
}
}Casos de uso:
- Perfil de usuario en la interfaz
- Verificación de usuario antes de crear recetas
- Validación de permisos de usuario
- Dashboard personalizado
Posibles errores:
404- Usuario no encontrado si el ID no existe500- Error interno del servidor
Ejemplo de uso con curl:
curl -X GET http://localhost:3000/usuarios/1POST /usuariosDescripción: Registra un nuevo usuario en la plataforma. Este endpoint valida que el ID sea único y que se proporcionen todos los campos obligatorios. La fecha de registro se asigna automáticamente.
Headers requeridos:
Content-Type: application/json
Body (JSON, requerido):
{
"id": 4,
"nombre": "Carlos Rodríguez",
"email": "carlos.rodriguez@email.com"
}Campos del body:
id(number, requerido): ID único del usuario (debe ser único en el sistema)nombre(string, requerido): Nombre completo del usuarioemail(string, requerido): Dirección de correo electrónico del usuario
Respuesta exitosa (201):
{
"message": "Usuario creado exitosamente",
"usuario": {
"id": 4,
"nombre": "Carlos Rodríguez",
"email": "carlos.rodriguez@email.com",
"fechaRegistro": "2024-02-15T10:30:00Z"
}
}Casos de uso:
- Registro de nuevos usuarios en la plataforma
- Migración de usuarios desde otros sistemas
- Creación de usuarios de prueba
Posibles errores:
400- Faltan campos obligatorios o datos inválidos400- Ya existe un usuario con ese ID500- Error interno del servidor
Ejemplo de uso con curl:
curl -X POST http://localhost:3000/usuarios \
-H "Content-Type: application/json" \
-d '{"id": 4, "nombre": "Carlos Rodríguez", "email": "carlos.rodriguez@email.com"}'Validaciones implementadas:
- Verificación de campos obligatorios (id, nombre, email)
- Validación de unicidad del ID
- Asignación automática de fecha de registro
PATCH /usuarios/:idParámetros:
id(number): ID del usuario
Body:
{
"nombre": "María García López",
"email": "maria.lopez@email.com"
}Respuesta:
{
"message": "Usuario actualizado exitosamente"
}DELETE /usuarios/:idParámetros:
id(number): ID del usuario
Respuesta:
{
"message": "Usuario y todas sus recetas eliminados exitosamente"
}GET /recetasDescripción: Obtiene una lista completa de todas las recetas disponibles en la plataforma. Este endpoint es ideal para mostrar un catálogo general de recetas o para operaciones de búsqueda y filtrado.
Parámetros: Ninguno
Headers requeridos: Ninguno
Respuesta exitosa (200):
{
"message": "Recetas obtenidas exitosamente",
"count": 4,
"recetas": [
{
"id": 1,
"titulo": "Pasta Carbonara",
"descripcion": "Deliciosa pasta italiana con huevo, queso parmesano y panceta",
"usuarioId": 1,
"fechaCreacion": "2024-01-16T12:00:00Z",
"ingredientes": []
}
]
}Campos de respuesta:
message(string): Mensaje de confirmacióncount(number): Número total de recetas encontradasrecetas(array): Lista de objetos receta con información básica
Casos de uso:
- Catálogo general de recetas
- Página principal de la aplicación
- Estadísticas de la plataforma
- Operaciones de búsqueda y filtrado
Posibles errores:
500- Error interno del servidor si hay problemas con la base de datos
Ejemplo de uso con curl:
curl -X GET http://localhost:3000/recetasGET /recetas/:idDescripción: Obtiene la información completa de una receta específica, incluyendo todos sus ingredientes. Este endpoint es fundamental para mostrar el detalle de una receta en la interfaz de usuario.
Parámetros de ruta:
id(number, requerido): ID único de la receta a consultar
Headers requeridos: Ninguno
Respuesta exitosa (200):
{
"message": "Receta obtenida exitosamente",
"receta": {
"id": 1,
"titulo": "Pasta Carbonara",
"descripcion": "Deliciosa pasta italiana con huevo, queso parmesano y panceta",
"usuarioId": 1,
"fechaCreacion": "2024-01-16T12:00:00Z",
"ingredientes": [
{
"id": 1,
"nombre": "Pasta",
"recetaId": 1,
"fechaAgregado": "2024-01-16T12:05:00Z"
},
{
"id": 2,
"nombre": "Huevos",
"recetaId": 1,
"fechaAgregado": "2024-01-16T12:05:00Z"
}
]
}
}Campos de respuesta:
message(string): Mensaje de confirmaciónreceta(object): Objeto con información completa de la recetaid(number): ID único de la recetatitulo(string): Título de la recetadescripcion(string): Descripción detalladausuarioId(number): ID del usuario propietariofechaCreacion(string): Fecha de creación en formato ISO 8601ingredientes(array): Lista de ingredientes con información completa
Casos de uso:
- Página de detalle de receta
- Vista de receta completa para cocinar
- Verificación de ingredientes antes de comprar
- Compartir receta con otros usuarios
Posibles errores:
404- Receta no encontrada si el ID no existe500- Error interno del servidor
Ejemplo de uso con curl:
curl -X GET http://localhost:3000/recetas/1Notas técnicas:
- Este endpoint realiza una consulta adicional para obtener los ingredientes
- Los ingredientes se ordenan por fecha de agregado
- La respuesta incluye información completa para renderizado en frontend
GET /recetas/usuario/:usuarioIdParámetros:
usuarioId(number): ID del usuario
Respuesta:
{
"message": "Recetas del usuario 1 obtenidas exitosamente",
"count": 2,
"recetas": [
{
"id": 1,
"titulo": "Pasta Carbonara",
"descripcion": "Deliciosa pasta italiana con huevo, queso parmesano y panceta",
"usuarioId": 1,
"fechaCreacion": "2024-01-16T12:00:00Z",
"ingredientes": []
},
{
"id": 3,
"titulo": "Ensalada César",
"descripcion": "Ensalada fresca con lechuga, pollo, queso parmesano y aderezo especial",
"usuarioId": 1,
"fechaCreacion": "2024-01-25T11:20:00Z",
"ingredientes": []
}
]
}POST /recetasBody:
{
"id": 5,
"titulo": "Risotto de Hongos",
"descripcion": "Cremoso risotto italiano con hongos porcini",
"usuarioId": 2
}Respuesta:
{
"message": "Receta creada exitosamente",
"receta": {
"id": 5,
"titulo": "Risotto de Hongos",
"descripcion": "Cremoso risotto italiano con hongos porcini",
"usuarioId": 2,
"fechaCreacion": "2024-02-15T10:30:00Z",
"ingredientes": []
}
}PATCH /recetas/:idParámetros:
id(number): ID de la receta
Body:
{
"titulo": "Pasta Carbonara Tradicional",
"descripcion": "Auténtica receta italiana de pasta carbonara con guanciale"
}Respuesta:
{
"message": "Receta actualizada exitosamente"
}DELETE /recetas/:idParámetros:
id(number): ID de la receta
Respuesta:
{
"message": "Receta y todos sus ingredientes eliminados exitosamente"
}GET /ingredientes/receta/:recetaIdParámetros:
recetaId(number): ID de la receta
Respuesta:
{
"message": "Ingredientes de la receta 1 obtenidos exitosamente",
"count": 5,
"ingredientes": [
{
"id": 1,
"nombre": "Pasta",
"recetaId": 1,
"fechaAgregado": "2024-01-16T12:05:00Z"
},
{
"id": 2,
"nombre": "Huevos",
"recetaId": 1,
"fechaAgregado": "2024-01-16T12:05:00Z"
}
]
}POST /ingredientesBody:
{
"id": 22,
"nombre": "Aceite de Oliva",
"recetaId": 1
}Respuesta:
{
"message": "Ingrediente agregado exitosamente",
"ingrediente": {
"id": 22,
"nombre": "Aceite de Oliva",
"recetaId": 1,
"fechaAgregado": "2024-02-15T10:30:00Z"
}
}DELETE /ingredientes/:id/receta/:recetaIdParámetros:
id(number): ID del ingredienterecetaId(number): ID de la receta
Respuesta:
{
"message": "Ingrediente eliminado exitosamente"
}GET /ingredientes/buscar?nombre=polloDescripción: Busca todas las recetas que contengan un ingrediente específico. Esta es una funcionalidad clave que permite a los usuarios encontrar recetas basándose en ingredientes que tienen disponibles. La búsqueda es case-insensitive y utiliza expresiones regulares para coincidencias parciales.
Parámetros de consulta:
nombre(string, requerido): Nombre del ingrediente a buscar (búsqueda parcial, case-insensitive)
Headers requeridos: Ninguno
Respuesta exitosa (200):
{
"message": "Recetas encontradas con el ingrediente 'pollo'",
"count": 3,
"recetas": [
{
"id": 2,
"titulo": "Pollo al Curry",
"descripcion": "Pollo cocinado con especias y leche de coco",
"usuarioId": 2,
"fechaCreacion": "2024-01-21T18:30:00Z",
"ingredientesEncontrados": [
{
"id": 6,
"nombre": "Pollo",
"recetaId": 2,
"fechaAgregado": "2024-01-21T18:35:00Z"
}
]
},
{
"id": 3,
"titulo": "Ensalada César",
"descripcion": "Ensalada fresca con lechuga, pollo, queso parmesano y aderezo especial",
"usuarioId": 1,
"fechaCreacion": "2024-01-25T11:20:00Z",
"ingredientesEncontrados": [
{
"id": 12,
"nombre": "Pollo",
"recetaId": 3,
"fechaAgregado": "2024-01-25T11:25:00Z"
}
]
}
]
}Campos de respuesta:
message(string): Mensaje descriptivo con el ingrediente buscadocount(number): Número total de recetas encontradasrecetas(array): Lista de recetas que contienen el ingredienteingredientesEncontrados(array): Lista de ingredientes que coinciden con la búsqueda
Casos de uso:
- Búsqueda de recetas por ingredientes disponibles
- Sugerencias de recetas basadas en ingredientes
- Filtrado de recetas por ingredientes específicos
- Funcionalidad "¿Qué puedo cocinar con...?"
Posibles errores:
400- Parámetro 'nombre' no proporcionado500- Error interno del servidor
Ejemplos de búsqueda:
# Búsqueda exacta
curl "http://localhost:3000/ingredientes/buscar?nombre=pollo"
# Búsqueda parcial (encuentra "pollo", "pollo al", etc.)
curl "http://localhost:3000/ingredientes/buscar?nombre=pol"
# Búsqueda case-insensitive
curl "http://localhost:3000/ingredientes/buscar?nombre=POLLO"Características técnicas:
- Búsqueda case-insensitive: "pollo", "POLLO", "Pollo" dan los mismos resultados
- Búsqueda parcial: "pol" encuentra "pollo", "polenta", etc.
- Expresiones regulares: Utiliza MongoDB regex para búsquedas flexibles
- Optimización: Consulta eficiente que evita cargar todas las recetas
Casos de prueba recomendados:
nombre=pollo→ 3 recetasnombre=queso→ 2 recetasnombre=cebolla→ 2 recetasnombre=arroz→ 0 recetas (si no hay recetas con arroz)
-
Crear un usuario:
curl -X POST http://localhost:3000/usuarios \ -H "Content-Type: application/json" \ -d '{"id": 5, "nombre": "Chef Master", "email": "chef@email.com"}'
-
Crear una receta:
curl -X POST http://localhost:3000/recetas \ -H "Content-Type: application/json" \ -d '{"id": 6, "titulo": "Paella Valenciana", "descripcion": "Auténtica paella española", "usuarioId": 5}'
-
Agregar ingredientes:
curl -X POST http://localhost:3000/ingredientes \ -H "Content-Type: application/json" \ -d '{"id": 23, "nombre": "Arroz", "recetaId": 6}'
-
Buscar recetas con arroz:
curl "http://localhost:3000/ingredientes/buscar?nombre=arroz"
200- OK (operación exitosa)201- Created (recurso creado)400- Bad Request (datos inválidos)404- Not Found (recurso no encontrado)500- Internal Server Error (error del servidor)
La API devuelve mensajes de error claros en formato JSON:
{
"error": "Usuario no encontrado"
}- Los IDs deben ser únicos dentro de cada colección
- Al eliminar un usuario, se eliminan automáticamente todas sus recetas
- Al eliminar una receta, se eliminan automáticamente todos sus ingredientes
- La búsqueda de ingredientes es case-insensitive
- Todos los timestamps están en formato ISO 8601
-
Iniciar el servidor:
npm run dev
-
Verificar que el servidor esté funcionando:
- Abrir:
http://localhost:3000/health - Debe mostrar:
{"message": "API de Recetas Culinarias activa!!! 🍳"}
- Abrir:
-
Inicializar datos de prueba:
npm run seed
1.1 - Listar todos los usuarios
GET http://localhost:3000/usuariosResultado: 3 usuarios (María García, Juan Pérez, Ana López)
1.2 - Listar todas las recetas
GET http://localhost:3000/recetasResultado: 4 recetas iniciales
2.1 - Obtener usuario específico
GET http://localhost:3000/usuarios/1Resultado: Información de María García
2.2 - Crear nuevo usuario
POST http://localhost:3000/usuarios
Content-Type: application/json
{
"id": 4,
"nombre": "Chef Master",
"email": "chef.master@email.com"
}2.3 - Actualizar usuario
PATCH http://localhost:3000/usuarios/4
Content-Type: application/json
{
"nombre": "Chef Master Pro",
"email": "chef.pro@email.com"
}3.1 - Crear nueva receta
POST http://localhost:3000/recetas
Content-Type: application/json
{
"id": 5,
"titulo": "Risotto de Hongos",
"descripcion": "Cremoso risotto italiano con hongos porcini",
"usuarioId": 4
}3.2 - Obtener receta con ingredientes
GET http://localhost:3000/recetas/1Resultado: Pasta Carbonara con sus 5 ingredientes
3.3 - Listar recetas de un usuario específico
GET http://localhost:3000/recetas/usuario/1Resultado: 2 recetas de María García
4.1 - Agregar ingredientes a la nueva receta
POST http://localhost:3000/ingredientes
Content-Type: application/json
{
"id": 22,
"nombre": "Arroz Arborio",
"recetaId": 5
}4.2 - Ver ingredientes de una receta
GET http://localhost:3000/ingredientes/receta/1Resultado: 5 ingredientes de Pasta Carbonara
5.1 - Buscar recetas con "pollo"
GET http://localhost:3000/ingredientes/buscar?nombre=polloResultado: 3 recetas que contienen pollo
5.2 - Buscar recetas con "queso"
GET http://localhost:3000/ingredientes/buscar?nombre=quesoResultado: 2 recetas con queso parmesano
5.3 - Buscar recetas con "cebolla"
GET http://localhost:3000/ingredientes/buscar?nombre=cebollaResultado: 2 recetas con cebolla
6.1 - Recetas de María García (usuario 1)
GET http://localhost:3000/recetas/usuario/1Resultado: 2 recetas (Pasta Carbonara y Ensalada César)
6.2 - Recetas de Juan Pérez (usuario 2)
GET http://localhost:3000/recetas/usuario/2Resultado: 1 receta (Pollo al Curry)
6.3 - Recetas de Ana López (usuario 3)
GET http://localhost:3000/recetas/usuario/3Resultado: 1 receta (Tacos de Pollo)
7.1 - Eliminar ingrediente
DELETE http://localhost:3000/ingredientes/24/receta/57.2 - Eliminar receta
DELETE http://localhost:3000/recetas/57.3 - Eliminar usuario
DELETE http://localhost:3000/usuarios/4- "pollo" → 3 recetas encontradas
- "queso" → 2 recetas encontradas
- "cebolla" → 2 recetas encontradas
- "arroz" → 0 recetas (después de eliminar)
- Usuario 1 (María) → 2 recetas
- Usuario 2 (Juan) → 1 receta
- Usuario 3 (Ana) → 1 receta
base_url: http://localhost:3000
usuario_id: 1
receta_id: 1
Content-Type: application/json
- Usuarios (5 endpoints)
- Recetas (6 endpoints)
- Ingredientes (4 endpoints)
- Búsquedas (búsqueda por ingrediente)
- ✅ Gestión completa de usuarios (CRUD)
- ✅ Gestión completa de recetas (CRUD)
- ✅ Gestión completa de ingredientes (CRUD)
- ✅ Búsqueda por ingrediente (case-insensitive)
- ✅ Listado de recetas por usuario
- ✅ Eliminación en cascada (usuario → recetas → ingredientes)
- ✅ Validaciones y manejo de errores
Controllers → Services → Database
↓ ↓ ↓
Routes Business MongoDB
Logic
Todas las respuestas siguen un patrón consistente:
{
"message": "Descripción de la operación",
"count": 0, // Solo en listas
"data": {}, // Datos específicos
"error": "Mensaje" // Solo en errores
}- ✅ ID único en el sistema
- ✅ Campos obligatorios: id, nombre, email
- ✅ Validación de formato de email
- ✅ Prevención de duplicados
- ✅ ID único en el sistema
- ✅ Campos obligatorios: id, titulo, descripcion, usuarioId
- ✅ Verificación de existencia del usuario propietario
- ✅ Validación de longitud de campos
- ✅ ID único por receta
- ✅ Campos obligatorios: id, nombre, recetaId
- ✅ Verificación de existencia de la receta
- ✅ Prevención de duplicados por receta
- Índices recomendados en MongoDB:
// Índices para optimizar consultas db.usuarios.createIndex({ "id": 1 }) db.recetas.createIndex({ "id": 1 }) db.recetas.createIndex({ "usuarioId": 1 }) db.ingredientes.createIndex({ "recetaId": 1 }) db.ingredientes.createIndex({ "nombre": "text" })
- Respuestas de listas pueden ser cacheadas por 5 minutos
- Búsquedas frecuentes pueden usar cache Redis
- Headers de cache recomendados para endpoints GET
200- Operación exitosa201- Recurso creado exitosamente400- Datos de entrada inválidos404- Recurso no encontrado500- Error interno del servidor
{
"error": "Mensaje descriptivo del error",
"code": "ERROR_CODE",
"details": "Información adicional para debugging"
}- Sanitización de strings para prevenir inyecciones
- Validación de tipos de datos
- Límites de longitud en campos de texto
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
- Tiempo de respuesta por endpoint
- Número de requests por minuto
- Errores por tipo y endpoint
- Uso de memoria y CPU
{
"timestamp": "2024-02-15T10:30:00Z",
"level": "INFO",
"endpoint": "GET /recetas",
"responseTime": 150,
"statusCode": 200
}- Implementar rate limiting por IP
- Usar connection pooling para MongoDB
- Considerar sharding por usuarioId para recetas
- Implementar paginación en listas grandes
- Máximo 1000 usuarios por página
- Máximo 500 recetas por página
- Máximo 50 ingredientes por receta
- Timeout de 30 segundos por request
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/AmazingFeature) - Commit tus cambios (
git commit -m 'Add some AmazingFeature') - Push a la rama (
git push origin feature/AmazingFeature) - Abre un Pull Request
Este proyecto está bajo la Licencia ISC.