En este módulo, aprenderás a desarrollar una plataforma de blogs completa utilizando Node.js y Express. Este proyecto te permitirá aplicar muchos de los conceptos que has aprendido a lo largo del curso, como la gestión de bases de datos, autenticación, enrutamiento y más.
Objetivos del Módulo
- Crear una aplicación de blogs funcional.
- Implementar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) para las publicaciones del blog.
- Gestionar la autenticación y autorización de usuarios.
- Desplegar la aplicación en un entorno de producción.
Estructura del Módulo
Configuración del Proyecto
Paso 1: Inicializar el Proyecto
Primero, crea una nueva carpeta para tu proyecto y navega a ella desde la terminal. Luego, inicializa un nuevo proyecto de Node.js.
Paso 2: Instalar Dependencias
Instala las dependencias necesarias para el proyecto.
Paso 3: Configurar el Servidor
Crea un archivo server.js
y configura el servidor básico de Express.
const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); const app = express(); // Middleware app.use(bodyParser.json()); // Conectar a la base de datos mongoose.connect('mongodb://localhost:27017/blog', { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('MongoDB conectado')) .catch(err => console.log(err)); // Rutas app.get('/', (req, res) => { res.send('Bienvenido a la Plataforma de Blogs'); }); // Iniciar el servidor const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Servidor corriendo en el puerto ${PORT}`));
Modelado de Datos
Paso 1: Crear el Modelo de Usuario
Crea un archivo models/User.js
para definir el esquema del usuario.
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const UserSchema = new Schema({ username: { type: String, required: true, unique: true }, email: { type: String, required: true, unique: true }, password: { type: String, required: true } }); module.exports = mongoose.model('User', UserSchema);
Paso 2: Crear el Modelo de Publicación
Crea un archivo models/Post.js
para definir el esquema de las publicaciones del blog.
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const PostSchema = new Schema({ title: { type: String, required: true }, body: { type: String, required: true }, author: { type: Schema.Types.ObjectId, ref: 'User', required: true }, date: { type: Date, default: Date.now } }); module.exports = mongoose.model('Post', PostSchema);
Rutas y Controladores
Paso 1: Configurar Rutas de Autenticación
Crea un archivo routes/auth.js
para manejar el registro y el inicio de sesión de usuarios.
const express = require('express'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); const User = require('../models/User'); const router = express.Router(); // Registro de usuario router.post('/register', async (req, res) => { const { username, email, password } = req.body; try { let user = await User.findOne({ email }); if (user) { return res.status(400).json({ msg: 'El usuario ya existe' }); } user = new User({ username, email, password }); const salt = await bcrypt.genSalt(10); user.password = await bcrypt.hash(password, salt); await user.save(); const payload = { user: { id: user.id } }; jwt.sign(payload, 'secret', { expiresIn: 3600 }, (err, token) => { if (err) throw err; res.json({ token }); }); } catch (err) { console.error(err.message); res.status(500).send('Error en el servidor'); } }); // Inicio de sesión de usuario router.post('/login', async (req, res) => { const { email, password } = req.body; try { let user = await User.findOne({ email }); if (!user) { return res.status(400).json({ msg: 'Credenciales inválidas' }); } const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { return res.status(400).json({ msg: 'Credenciales inválidas' }); } const payload = { user: { id: user.id } }; jwt.sign(payload, 'secret', { expiresIn: 3600 }, (err, token) => { if (err) throw err; res.json({ token }); }); } catch (err) { console.error(err.message); res.status(500).send('Error en el servidor'); } }); module.exports = router;
Paso 2: Configurar Rutas de Publicaciones
Crea un archivo routes/posts.js
para manejar las operaciones CRUD de las publicaciones del blog.
const express = require('express'); const auth = require('../middleware/auth'); const Post = require('../models/Post'); const router = express.Router(); // Crear una nueva publicación router.post('/', auth, async (req, res) => { const { title, body } = req.body; try { const newPost = new Post({ title, body, author: req.user.id }); const post = await newPost.save(); res.json(post); } catch (err) { console.error(err.message); res.status(500).send('Error en el servidor'); } }); // Obtener todas las publicaciones router.get('/', async (req, res) => { try { const posts = await Post.find().populate('author', ['username']); res.json(posts); } catch (err) { console.error(err.message); res.status(500).send('Error en el servidor'); } }); // Obtener una publicación por ID router.get('/:id', async (req, res) => { try { const post = await Post.findById(req.params.id).populate('author', ['username']); if (!post) { return res.status(404).json({ msg: 'Publicación no encontrada' }); } res.json(post); } catch (err) { console.error(err.message); res.status(500).send('Error en el servidor'); } }); // Actualizar una publicación router.put('/:id', auth, async (req, res) => { const { title, body } = req.body; try { let post = await Post.findById(req.params.id); if (!post) { return res.status(404).json({ msg: 'Publicación no encontrada' }); } if (post.author.toString() !== req.user.id) { return res.status(401).json({ msg: 'No autorizado' }); } post.title = title; post.body = body; await post.save(); res.json(post); } catch (err) { console.error(err.message); res.status(500).send('Error en el servidor'); } }); // Eliminar una publicación router.delete('/:id', auth, async (req, res) => { try { let post = await Post.findById(req.params.id); if (!post) { return res.status(404).json({ msg: 'Publicación no encontrada' }); } if (post.author.toString() !== req.user.id) { return res.status(401).json({ msg: 'No autorizado' }); } await post.remove(); res.json({ msg: 'Publicación eliminada' }); } catch (err) { console.error(err.message); res.status(500).send('Error en el servidor'); } }); module.exports = router;
Paso 3: Configurar Middleware de Autenticación
Crea un archivo middleware/auth.js
para manejar la autenticación de usuarios.
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 hay token, autorización denegada' }); } try { const decoded = jwt.verify(token, 'secret'); req.user = decoded.user; next(); } catch (err) { res.status(401).json({ msg: 'Token no válido' }); } };
Paso 4: Integrar Rutas en el Servidor
Actualiza server.js
para incluir las rutas de autenticación y publicaciones.
const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); const app = express(); // Middleware app.use(bodyParser.json()); // Conectar a la base de datos mongoose.connect('mongodb://localhost:27017/blog', { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('MongoDB conectado')) .catch(err => console.log(err)); // Rutas app.use('/api/auth', require('./routes/auth')); app.use('/api/posts', require('./routes/posts')); app.get('/', (req, res) => { res.send('Bienvenido a la Plataforma de Blogs'); }); // Iniciar el servidor const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Servidor corriendo en el puerto ${PORT}`));
Autenticación y Autorización
Registro de Usuario
Para registrar un nuevo usuario, envía una solicitud POST a /api/auth/register
con el siguiente cuerpo:
{ "username": "usuario1", "email": "[email protected]", "password": "password123" }
Inicio de Sesión
Para iniciar sesión, envía una solicitud POST a /api/auth/login
con el siguiente cuerpo:
{ "email": "[email protected]", "password": "password123" }
El servidor responderá con un token JWT que debe ser incluido en el encabezado x-auth-token
para las solicitudes autenticadas.
Interfaz de Usuario
Paso 1: Configurar el Cliente
Puedes usar cualquier framework de frontend como React, Angular o Vue.js para construir la interfaz de usuario. Aquí, proporcionaremos un ejemplo básico usando HTML y JavaScript.
Paso 2: Crear Formularios de Registro e Inicio de Sesión
Crea un archivo index.html
con formularios para el registro e inicio de sesión.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Plataforma de Blogs</title> </head> <body> <h1>Plataforma de Blogs</h1> <div> <h2>Registro</h2> <form id="registerForm"> <input type="text" id="username" placeholder="Nombre de usuario" required> <input type="email" id="email" placeholder="Correo electrónico" required> <input type="password" id="password" placeholder="Contraseña" required> <button type="submit">Registrar</button> </form> </div> <div> <h2>Inicio de Sesión</h2> <form id="loginForm"> <input type="email" id="loginEmail" placeholder="Correo electrónico" required> <input type="password" id="loginPassword" placeholder="Contraseña" required> <button type="submit">Iniciar Sesión</button> </form> </div> <script> document.getElementById('registerForm').addEventListener('submit', async (e) => { e.preventDefault(); const username = document.getElementById('username').value; const email = document.getElementById('email').value; const password = document.getElementById('password').value; const res = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, email, password }) }); const data = await res.json(); console.log(data); }); document.getElementById('loginForm').addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('loginEmail').value; const password = document.getElementById('loginPassword').value; const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await res.json(); console.log(data); }); </script> </body> </html>
Despliegue
Paso 1: Configurar Variables de Entorno
Crea un archivo .env
para almacenar las variables de entorno.
Paso 2: Actualizar server.js
para Usar Variables de Entorno
Actualiza server.js
para usar las variables de entorno.
require('dotenv').config(); const express = require('express'); const mongoose = require('mongoose'); const bodyParser = require('body-parser'); const app = express(); // Middleware app.use(bodyParser.json()); // Conectar a la base de datos mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }) .then(() => console.log('MongoDB conectado')) .catch(err => console.log(err)); // Rutas app.use('/api/auth', require('./routes/auth')); app.use('/api/posts', require('./routes/posts')); app.get('/', (req, res) => { res.send('Bienvenido a la Plataforma de Blogs'); }); // Iniciar el servidor const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Servidor corriendo en el puerto ${PORT}`));
Paso 3: Desplegar en Heroku
Sigue los pasos para desplegar tu aplicación 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 JWT_SECRET=your_jwt_secret
- Despliega la aplicación:
git push heroku master
Conclusión
En este módulo, has aprendido a desarrollar una plataforma de blogs completa utilizando Node.js y Express. Has implementado operaciones CRUD, autenticación y autorización, y has desplegado la aplicación en un entorno de producción. Este proyecto te ha permitido aplicar muchos de los conceptos que has aprendido a lo largo del curso y te ha preparado para desarrollar aplicaciones web 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