En este módulo, construiremos una herramienta de gestión de tareas utilizando Node.js y Express. Este proyecto integrará muchos de los conceptos que hemos aprendido a lo largo del curso, incluyendo la creación de servidores HTTP, manejo de rutas, autenticación, operaciones CRUD, y despliegue.

Objetivos del Módulo

  • Crear una API RESTful para la gestión de tareas.
  • Implementar autenticación y autorización.
  • Realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) en las tareas.
  • Desplegar la aplicación en un entorno de producción.

Requisitos Previos

  • Conocimientos básicos de Node.js y Express.
  • Familiaridad con MongoDB y Mongoose.
  • Comprensión de JWT para autenticación.
  • Experiencia con NPM y gestión de paquetes.

Estructura del Proyecto

La estructura del proyecto será la siguiente:

task-manager/
├── config/
│   └── db.js
├── controllers/
│   ├── authController.js
│   └── taskController.js
├── middlewares/
│   └── authMiddleware.js
├── models/
│   ├── Task.js
│   └── User.js
├── routes/
│   ├── authRoutes.js
│   └── taskRoutes.js
├── .env
├── app.js
└── package.json

Paso 1: Configuración del Proyecto

1.1 Inicializar el Proyecto

Primero, inicializaremos un nuevo proyecto de Node.js y configuraremos las dependencias necesarias.

mkdir task-manager
cd task-manager
npm init -y
npm install express mongoose bcryptjs jsonwebtoken dotenv

1.2 Configuración de la Base de Datos

Crearemos un archivo config/db.js para configurar la conexión a MongoDB.

// config/db.js
const mongoose = require('mongoose');
const dotenv = require('dotenv');

dotenv.config();

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.MONGO_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('MongoDB connected');
  } catch (error) {
    console.error('MongoDB connection error:', error);
    process.exit(1);
  }
};

module.exports = connectDB;

1.3 Configuración del Servidor

Configuraremos el servidor Express en app.js.

// app.js
const express = require('express');
const connectDB = require('./config/db');
const dotenv = require('dotenv');

dotenv.config();

const app = express();

// Conectar a la base de datos
connectDB();

// Middleware para parsear JSON
app.use(express.json());

// Rutas
app.use('/api/auth', require('./routes/authRoutes'));
app.use('/api/tasks', require('./routes/taskRoutes'));

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Paso 2: Modelos de Datos

2.1 Modelo de Usuario

Crearemos un modelo de usuario en models/User.js.

// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true,
  },
  email: {
    type: String,
    required: true,
    unique: true,
  },
  password: {
    type: String,
    required: true,
  },
});

UserSchema.pre('save', async function (next) {
  if (!this.isModified('password')) {
    return next();
  }
  const salt = await bcrypt.genSalt(10);
  this.password = await bcrypt.hash(this.password, salt);
  next();
});

module.exports = mongoose.model('User', UserSchema);

2.2 Modelo de Tarea

Crearemos un modelo de tarea en models/Task.js.

// models/Task.js
const mongoose = require('mongoose');

const TaskSchema = new mongoose.Schema({
  user: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true,
  },
  title: {
    type: String,
    required: true,
  },
  description: {
    type: String,
  },
  status: {
    type: String,
    enum: ['pending', 'in-progress', 'completed'],
    default: 'pending',
  },
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

module.exports = mongoose.model('Task', TaskSchema);

Paso 3: Controladores

3.1 Controlador de Autenticación

Crearemos un controlador de autenticación en controllers/authController.js.

// controllers/authController.js
const User = require('../models/User');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

exports.register = async (req, res) => {
  const { name, email, password } = req.body;

  try {
    let user = await User.findOne({ email });
    if (user) {
      return res.status(400).json({ msg: 'User already exists' });
    }

    user = new User({
      name,
      email,
      password,
    });

    await user.save();

    const payload = {
      user: {
        id: user.id,
      },
    };

    jwt.sign(
      payload,
      process.env.JWT_SECRET,
      { expiresIn: '1h' },
      (err, token) => {
        if (err) throw err;
        res.json({ token });
      }
    );
  } catch (error) {
    console.error(error.message);
    res.status(500).send('Server error');
  }
};

exports.login = async (req, res) => {
  const { email, password } = req.body;

  try {
    let user = await User.findOne({ email });
    if (!user) {
      return res.status(400).json({ msg: 'Invalid credentials' });
    }

    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
      return res.status(400).json({ msg: 'Invalid credentials' });
    }

    const payload = {
      user: {
        id: user.id,
      },
    };

    jwt.sign(
      payload,
      process.env.JWT_SECRET,
      { expiresIn: '1h' },
      (err, token) => {
        if (err) throw err;
        res.json({ token });
      }
    );
  } catch (error) {
    console.error(error.message);
    res.status(500).send('Server error');
  }
};

3.2 Controlador de Tareas

Crearemos un controlador de tareas en controllers/taskController.js.

// controllers/taskController.js
const Task = require('../models/Task');

exports.createTask = async (req, res) => {
  const { title, description } = req.body;

  try {
    const newTask = new Task({
      user: req.user.id,
      title,
      description,
    });

    const task = await newTask.save();
    res.json(task);
  } catch (error) {
    console.error(error.message);
    res.status(500).send('Server error');
  }
};

exports.getTasks = async (req, res) => {
  try {
    const tasks = await Task.find({ user: req.user.id });
    res.json(tasks);
  } catch (error) {
    console.error(error.message);
    res.status(500).send('Server error');
  }
};

exports.updateTask = async (req, res) => {
  const { title, description, status } = req.body;

  try {
    let task = await Task.findById(req.params.id);

    if (!task) {
      return res.status(404).json({ msg: 'Task not found' });
    }

    if (task.user.toString() !== req.user.id) {
      return res.status(401).json({ msg: 'Not authorized' });
    }

    task = await Task.findByIdAndUpdate(
      req.params.id,
      { $set: { title, description, status } },
      { new: true }
    );

    res.json(task);
  } catch (error) {
    console.error(error.message);
    res.status(500).send('Server error');
  }
};

exports.deleteTask = async (req, res) => {
  try {
    let task = await Task.findById(req.params.id);

    if (!task) {
      return res.status(404).json({ msg: 'Task not found' });
    }

    if (task.user.toString() !== req.user.id) {
      return res.status(401).json({ msg: 'Not authorized' });
    }

    await Task.findByIdAndRemove(req.params.id);

    res.json({ msg: 'Task removed' });
  } catch (error) {
    console.error(error.message);
    res.status(500).send('Server error');
  }
};

Paso 4: Rutas

4.1 Rutas de Autenticación

Crearemos rutas de autenticación en routes/authRoutes.js.

// routes/authRoutes.js
const express = require('express');
const { register, login } = require('../controllers/authController');

const router = express.Router();

router.post('/register', register);
router.post('/login', login);

module.exports = router;

4.2 Rutas de Tareas

Crearemos rutas de tareas en routes/taskRoutes.js.

// routes/taskRoutes.js
const express = require('express');
const {
  createTask,
  getTasks,
  updateTask,
  deleteTask,
} = require('../controllers/taskController');
const auth = require('../middlewares/authMiddleware');

const router = express.Router();

router.post('/', auth, createTask);
router.get('/', auth, getTasks);
router.put('/:id', auth, updateTask);
router.delete('/:id', auth, deleteTask);

module.exports = router;

Paso 5: Middleware de Autenticación

Crearemos un middleware de autenticación en middlewares/authMiddleware.js.

// middlewares/authMiddleware.js
const jwt = require('jsonwebtoken');

module.exports = function (req, res, next) {
  const token = req.header('x-auth-token');

  if (!token) {
    return res.status(401).json({ msg: 'No token, authorization denied' });
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded.user;
    next();
  } catch (error) {
    res.status(401).json({ msg: 'Token is not valid' });
  }
};

Paso 6: Despliegue

Finalmente, desplegaremos nuestra aplicación en Heroku.

6.1 Preparar para el Despliegue

Asegúrate de que tu archivo package.json tenga un script de inicio.

"scripts": {
  "start": "node app.js"
}

6.2 Desplegar en Heroku

  1. Inicia sesión en Heroku: heroku login
  2. Crea una nueva aplicación: heroku create
  3. Configura las variables de entorno en Heroku:
    heroku config:set MONGO_URI=<your_mongo_uri>
    heroku config:set JWT_SECRET=<your_jwt_secret>
    
  4. Despliega la aplicación:
    git add .
    git commit -m "Initial commit"
    git push heroku master
    

Conclusión

En este módulo, hemos construido una herramienta de gestión de tareas completa utilizando Node.js y Express. Hemos cubierto la creación de una API RESTful, la implementación de autenticación y autorización, y el despliegue de la aplicación en un entorno de producción. Este proyecto integra muchos de los conceptos aprendidos a lo largo del curso y proporciona una base sólida para construir aplicaciones más complejas en el futuro.

Curso de Node.js

Módulo 1: Introducción a Node.js

Módulo 2: Conceptos Básicos

Módulo 3: Sistema de Archivos y E/S

Módulo 4: HTTP y Servidores Web

Módulo 5: NPM y Gestión de Paquetes

Módulo 6: Framework Express.js

Módulo 7: Bases de Datos y ORMs

Módulo 8: Autenticación y Autorización

Módulo 9: Pruebas y Depuración

Módulo 10: Temas Avanzados

Módulo 11: Despliegue y DevOps

Módulo 12: Proyectos del Mundo Real

© Copyright 2024. Todos los derechos reservados