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
