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.

mkdir blog-platform
cd blog-platform
npm init -y

Paso 2: Instalar Dependencias

Instala las dependencias necesarias para el proyecto.

npm install express mongoose bcryptjs jsonwebtoken body-parser
npm install --save-dev nodemon

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.

MONGO_URI=mongodb://localhost:27017/blog
JWT_SECRET=your_jwt_secret
PORT=5000

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.

  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 JWT_SECRET=your_jwt_secret
  4. 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

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