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.
6.2 Desplegar en Heroku
- Inicia sesión en Heroku:
heroku login
- Crea una nueva aplicación:
heroku create
- Configura las variables de entorno en Heroku:
heroku config:set MONGO_URI=<your_mongo_uri> heroku config:set JWT_SECRET=<your_jwt_secret>
- 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
- Creando un Servidor HTTP Simple
- Manejo de Solicitudes y Respuestas
- Sirviendo Archivos Estáticos
- Enrutamiento
Módulo 5: NPM y Gestión de Paquetes
- Introducción a NPM
- Instalación y Uso de Paquetes
- Creación y Publicación de Paquetes
- Versionado Semántico
Módulo 6: Framework Express.js
- Introducción a Express.js
- Configuración de una Aplicación Express
- Middleware
- Enrutamiento en Express
- Manejo de Errores
Módulo 7: Bases de Datos y ORMs
- Introducción a las Bases de Datos
- Usando MongoDB con Mongoose
- Usando Bases de Datos SQL con Sequelize
- Operaciones CRUD
Módulo 8: Autenticación y Autorización
- Introducción a la Autenticación
- Usando Passport.js
- Autenticación JWT
- Control de Acceso Basado en Roles
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias con Mocha y Chai
- Pruebas de Integración
- Depuración de Aplicaciones Node.js
Módulo 10: Temas Avanzados
- Módulo Cluster
- Hilos de Trabajo
- Optimización del Rendimiento
- Construcción de APIs RESTful
- GraphQL con Node.js
Módulo 11: Despliegue y DevOps
- Variables de Entorno
- Usando PM2 para la Gestión de Procesos
- Desplegando en Heroku
- Integración y Despliegue Continuos