API para gestión de gastos personales con Express e informes mensuales agregados

API REST en Node.js con Express para gestionar gastos personales, con informes mensuales agregados usando MongoDB, Mongoose y moment.js.

Breve Introducción

API para gestión de gastos personales con Express e informes mensuales agregados (node)
DESCRIPCIÓN:
Una aplicación Node.js que expone una API REST para gestionar gastos personales. Permite agregar, modificar, eliminar y visualizar gastos, y obtener informes mensuales agregados. Utiliza Express para el servidor, Mongoose para la gestión de MongoDB y moment.js para la manipulación de fechas.

NOTAS ADICIONALES:
Para ejecutar el proyecto es necesario tener MongoDB en ejecución y configurar la variable de entorno MONGODB_URI con la cadena de conexión. El código está estructurado para ser fácilmente extensible y robusto, con manejo de errores y validación básica.

EXPLICACIONES:
- El modelo Mongoose define un esquema para el gasto con campos: descripción, importe, categoría y fecha.
- Los endpoints REST permiten crear, leer, actualizar y eliminar gastos, con respuestas en JSON.
- El endpoint /api/report/mensile agrega los gastos por mes y categoría usando la agregación de MongoDB.
- Moment.js se usa para normalizar las fechas y facilitar el agrupamiento mensual.
- El middleware body-parser permite leer el cuerpo de las solicitudes POST y PUT en formato JSON.
- La gestión de errores está centralizada para devolver mensajes claros y códigos HTTP apropiados.

Código de Ejemplo: node

javascript
// Importar dependencias
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const moment = require('moment');

// Crear la aplicación Express
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware para análisis JSON
app.use(bodyParser.json());

// Conexión a MongoDB mediante Mongoose
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/spese_db';
mongoose.connect(mongoUri, {
  useNewUrlParser: true,
  useUnifiedTopology: true
}).then(() => {
  console.log('Conectado a MongoDB');
}).catch(err => {
  console.error('Error de conexión a MongoDB:', err);
  process.exit(1);
});

// Definición del esquema Mongoose para un gasto
const spesaSchema = new mongoose.Schema({
  descrizione: { type: String, required: true, trim: true },
  importo: { type: Number, required: true, min: 0 },
  categoria: { type: String, required: true, trim: true },
  data: { type: Date, required: true }
}, { timestamps: true });

const Spesa = mongoose.model('Spesa', spesaSchema);

// RUTA: Crear nuevo gasto
app.post('/api/spese', async (req, res) => {
  try {
    const { descrizione, importo, categoria, data } = req.body;

    // Validación básica
    if (!descrizione || !importo || !categoria || !data) {
      return res.status(400).json({ error: 'Todos los campos son obligatorios' });
    }

    const dataParsed = moment(data, moment.ISO_8601, true);
    if (!dataParsed.isValid()) {
      return res.status(400).json({ error: 'Fecha no válida, usar formato ISO 8601' });
    }

    const nuovaSpesa = new Spesa({
      descrizione: descrizione.trim(),
      importo: parseFloat(importo),
      categoria: categoria.trim(),
      data: dataParsed.toDate()
    });

    const saved = await nuovaSpesa.save();
    res.status(201).json(saved);
  } catch (err) {
    console.error('Error POST /api/spese:', err);
    res.status(500).json({ error: 'Error interno del servidor' });
  }
});

// RUTA: Obtener todos los gastos
app.get('/api/spese', async (req, res) => {
  try {
    const spese = await Spesa.find().sort({ data: -1 });
    res.json(spese);
  } catch (err) {
    console.error('Error GET /api/spese:', err);
    res.status(500).json({ error: 'Error interno del servidor' });
  }
});

// RUTA: Obtener gasto por ID
app.get('/api/spese/:id', async (req, res) => {
  try {
    const spesa = await Spesa.findById(req.params.id);
    if (!spesa) {
      return res.status(404).json({ error: 'Gasto no encontrado' });
    }
    res.json(spesa);
  } catch (err) {
    console.error('Error GET /api/spese/:id:', err);
    res.status(500).json({ error: 'Error interno del servidor' });
  }
});

// RUTA: Actualizar gasto por ID
app.put('/api/spese/:id', async (req, res) => {
  try {
    const { descrizione, importo, categoria, data } = req.body;

    const updateData = {};
    if (descrizione) updateData.descrizione = descrizione.trim();
    if (importo !== undefined) updateData.importo = parseFloat(importo);
    if (categoria) updateData.categoria = categoria.trim();
    if (data) {
      const dataParsed = moment(data, moment.ISO_8601, true);
      if (!dataParsed.isValid()) {
        return res.status(400).json({ error: 'Fecha no válida, usar formato ISO 8601' });
      }
      updateData.data = dataParsed.toDate();
    }

    const spesaAggiornata = await Spesa.findByIdAndUpdate(req.params.id, updateData, { new: true, runValidators: true });
    if (!spesaAggiornata) {
      return res.status(404).json({ error: 'Gasto no encontrado' });
    }
    res.json(spesaAggiornata);
  } catch (err) {
    console.error('Error PUT /api/spese/:id:', err);
    res.status(500).json({ error: 'Error interno del servidor' });
  }
});

// RUTA: Eliminar gasto por ID
app.delete('/api/spese/:id', async (req, res) => {
  try {
    const spesaEliminata = await Spesa.findByIdAndDelete(req.params.id);
    if (!spesaEliminata) {
      return res.status(404).json({ error: 'Gasto no encontrado' });
    }
    res.json({ message: 'Gasto eliminado con éxito' });
  } catch (err) {
    console.error('Error DELETE /api/spese/:id:', err);
    res.status(500).json({ error: 'Error interno del servidor' });
  }
});

// RUTA: Informe mensual agregado por categoría
app.get('/api/report/mensile', async (req, res) => {
  try {
    // Agregación MongoDB para sumar importes agrupados por año, mes y categoría
    const report = await Spesa.aggregate([
      {
        $group: {
          _id: {
            anno: { $year: '$data' },
            mese: { $month: '$data' },
            categoria: '$categoria'
          },
          totale: { $sum: '$importo' }
        }
      },
      {
        $sort: {
          '_id.anno': -1,
          '_id.mese': -1,
          '_id.categoria': 1
        }
      }
    ]);

    // Reformatear datos para respuesta más legible
    const risultato = {};
    report.forEach(item => {
      const anno = item._id.anno;
      const mese = item._id.mese;
      const categoria = item._id.categoria;
      if (!risultato[anno]) risultato[anno] = {};
      if (!risultato[anno][mese]) risultato[anno][mese] = {};
      risultato[anno][mese][categoria] = item.totale;
    });

    res.json(risultato);
  } catch (err) {
    console.error('Error GET /api/report/mensile:', err);
    res.status(500).json({ error: 'Error interno del servidor' });
  }
});

// Iniciar servidor
app.listen(PORT, () => {
  console.log(`Servidor iniciado en el puerto ${PORT}`);
});

// Fin del código
×
×
×
¿Quieres regenerar la respuesta?
×
Exportar chat
Elige el formato de exportación
⏳ Generazione PDF in corso…
Allegati
×
⚠️ Estás a punto de cerrar el chat y pasar al generador de imágenes. Si no has iniciado sesión, perderás nuestro chat. ¿Confirmas?
👁 Estás viendo un chat compartido en modo temporal. No se guardará.
💬
×
Prompts guardados
×
Nota privada
×
Etiqueta
×
Buscar en todos los chats
×
Tus insights
Analizando…
×
Preferencias de IA
Nombre del asistente 30
Longitud de las respuestas
Modelo de IA
Respuestas estructuradas
Incluir ejemplos
Usar emojis
Guíame, no resuelvas Pistas y preguntas en vez de la solución hecha
Más ajustes →
×
Compartir este chat
Quien abra este enlace podrá ver el chat o añadirlo a su perfil como chat propio.
⚠️ Atención: también se compartirán los archivos adjuntos del chat. Quien lo añada recibirá una copia en su propia carpeta.
Chat compartido
Alguien ha compartido un chat contigo. ¿Solo quieres verlo o añadirlo a tus chats?
⚠️ Atención: también se compartirán los archivos adjuntos del chat. Quien lo añada recibirá una copia en su propia carpeta.
×

📌 Mensajes guardados

Cargando...

×

Historial de Chat

programas-ejemplo · HISTORIAL DE CHAT

Cargando...

Preferencias de IA

×
  • 🟢 BásicoRespuestas rápidas y esenciales para estudiar
  • 🔵 MedioMayor calidad para estudio y programación
  • 🟣 AvanzadoRazonamiento complejo y análisis detallado
Explicar Pasos

¿Qué se necesita?

Entorno de ejecución Node.js

Framework Express.js para el servidor HTTP

MongoDB como base de datos NoSQL para almacenar gastos

Mongoose ODM para modelar datos e interactuar con MongoDB

moment.js para gestión y formateo de fechas

Configuración del entorno: variable MONGODB_URI para la conexión a la base de datos

Dependencias en package.json: express, mongoose, moment, body-parser

Estructura API REST: endpoint /api/spese para CRUD, /api/report/mensile para informe agregado

Middleware Express para análisis JSON y manejo de errores

Ningún hardware Arduino involucrado en este proyecto

Temas Similares

Hello World HTTP con Node.js sin framework
Servidor HTTP básico en Node.js sin frameworks que responde ’Hello World’. Ideal para aprender conceptos fundamentales de servidores HTTP en Node.js.
Script Node.js para leer y escribir un archivo de texto
Script Node.js para leer y escribir archivos de texto usando APIs asíncronas, ideal para principiantes en manejo de archivos y Node.js.
Mini servidor JSON con el módulo http de Node.js
Ejemplo completo de mini servidor HTTP en Node.js que responde con JSON en el puerto 3, ideal para principiantes sin dependencias externas.
Contador de visitas en memoria con Node.js
Servidor HTTP simple con Node.js que cuenta visitas en memoria sin bases de datos, ideal para entender manejo de estado en aplicaciones Node.
Servidor estático mínimo para servir archivos HTML y CSS
Servidor HTTP simple en Node.js para servir archivos estáticos HTML y CSS desde la carpeta ’public’, ideal para proyectos pequeños y pruebas locales.
Convertidor de texto a mayúsculas desde archivo a consola con Node.js
Script Node.js que lee un archivo de texto, convierte su contenido a mayúsculas y lo muestra en consola. Ideal para principiantes en manipulación de archivos.
Temporizador con setTimeout y setInterval en Node.js
Ejemplo básico de temporizadores en Node.js usando setTimeout y setInterval para ejecutar funciones con retraso o a intervalos regulares.
Registrador simple en archivo con marca de tiempo en Node.js
Módulo simple en Node.js para registrar mensajes con marca de tiempo ISO en archivo, ideal para seguimiento ordenado de eventos y errores.