Crear un Servidor Node.js con Express y Autenticación JWT

En esta ocasión vamos a ver un ejemplo simple de código que muestra cómo configurar un servidor Node.js utilizando Express, autenticando la conexión con JWT.

¿Qué es Express?

Express es un framework de Node.js que proporciona una serie de características para el desarrollo de API, simplificando bastante el trabajo. Lo cual nos posibilitará, como veremos en este ejemplo, montar un CRUD completo.

El proyecto

Básicamente vamos a crear un sistema CRUD que nos permite conectar con una BD externa en MySQL, previo logueo del usuario. Si el login es correcto, se genera un token JWT que se usa para autenticar las diferentes rutas dle CRUD: GET, POST, PUT y DELETE.

Veremos el funcionamiento de las diferentes rutas usando Postman, para acceder a la API

Instalación y configuración

El primer requisito es tener instalado Node en el equipo. Una vez instalado, iniciamos un proyecto en la carpeta donde vamos a configurar nuestra aplicación

npm init -y

Una vez inicializado, instalaremos algunas librerias necesarias para el funcionamiento del proyecto.

Primero Express, para las rutas de la API

npm install express

Después una librería que nos permitirá conectar con MySQL

npm install mysql

Y finalmente la libreria que nos permitirá usar tokens JWT

npm install jsonwebtoken

Ahora ya tenemos configurado todo el ambiente necesario para empezar a trabajar.

Lo primero que haremos será configurar un fichero que nos permitirá conectar a la BD que tendremos creada. En el caso que nos ocupa, es una simple base de datos, con dos tablas, una con un usuario para el login y otra con datos para mostrar, nada más. El fichero exportará el modulo de conexión para poder usarlo más adelante.

// config_DB.js

const mysql = require('mysql');

const connection = mysql.createConnection({
    host: 'localhost',
    user: '_USUARIO_BD_',
    password: '#_PASSWORD_DB_',
    database: 'datos_bd_node'
});

connection.connect((err) => {
    if (err) {
        console.error('Error al conectar a la base de datos:', err);
        return;
    }
    console.log('Conexión a la base de datos establecida');
});

module.exports = connection;

Ahora vamos a montar el fichero que nos permitirá autentificar el usuario y autorizar las rutas, si el token del login es correcto. También se exportan todos los parámetros, para poder usarlos más adelante en el servidor node.js.

// config_JWT.js
const jwt = require('jsonwebtoken');
const secretKey = 'CLAVE_JWT'; // Clave secreta para firmar los tokens JWT


// Middleware de autenticación JWT
function authenticateToken(req, res, next) {
    const authHeader = req.headers['authorization'];
    const token = authHeader && authHeader.split(' ')[1];

    if (token == null) return res.sendStatus(401); // No autorizado

    jwt.verify(token, secretKey, (err, user) => {
        if (err) return res.sendStatus(403); // Prohibido
        req.user = user;
        next(); // Continuar con la ejecución de la solicitud
    });
    
}
module.exports = {
    authenticateToken: authenticateToken,
    jwt: jwt,
    secretKey: secretKey
};

Ahora en nuestro fichero server.js (que controlará el servidor) importaremos express y los ficheros que hemos creado anteriormente

// Importa el módulo express y lo asigna a la constante express
const express = require('express');
// Inicializa una instancia de la aplicación express
const app = express();
// Define el puerto en el que se ejecutará el servidor, tomando el valor del entorno si está definido, de lo contrario utiliza el puerto 3000
const PORT = process.env.PORT || 3000;
// Importa la configuración de la conexión a la base de datos
const connection = require('./config_DB');
// Importa funciones y constantes relacionadas con la autenticación JWT
const { authenticateToken, jwt, secretKey } = require('./config_JWT');

// Middleware para parsear el cuerpo de las solicitudes POST como objetos JSON
app.use(express.json());

Ahora lo que resta es ir montando los diferentes metodos para las llamadas del CRUD.

Vemos el GET

// Ruta para leer y mostrar los datos de la tabla usuarios
// El middleware authenticateToken verifica si el token JWT en la solicitud es válido antes de permitir el acceso a esta ruta
app.get('/usuarios', authenticateToken, (req, res) => {
    // Consulta todos los usuarios en la base de datos
    connection.query('SELECT * FROM datos_usuarios', (error, results) => {
        if (error) {
            // En caso de error en la consulta, envía una respuesta de error al cliente
            console.error('Error al realizar la consulta:', error);
            return res.status(500).send('Error interno del servidor');
        }
        // Envia los resultados de la consulta como respuesta al cliente en formato JSON
        res.json(results);
    });
});

El GET:id

// Ruta para ver los datos de un usuario por su ID
// Esta ruta también utiliza el middleware authenticateToken para verificar la autenticación del usuario
app.get('/usuarios/:id_usuario', authenticateToken, (req, res) => {
    // Obtiene el ID del usuario de los parámetros de la solicitud
    const usuarioId = req.params.id_usuario;
    // Consulta los datos del usuario con el ID proporcionado en la tabla usuarios
    connection.query('SELECT * FROM datos_usuarios WHERE id_usuario = ?', usuarioId, (error, results) => {
        if (error) {
            console.error('Error al obtener los datos del usuario:', error);
            return res.status(500).send('Error interno del servidor');
        }
        if (results.length === 0) {
            // Si no se encuentran resultados, envía una respuesta de usuario no encontrado al cliente
            return res.status(404).send('Usuario no encontrado');
        }
        // Envía los datos del usuario encontrado como respuesta al cliente en formato JSON
        res.json(results[0]);
    });
});

El POST

// Ruta para añadir un nuevo usuario a la tabla usuarios
// También utiliza el middleware authenticateToken para verificar la autenticación del usuario
app.post('/usuarios', authenticateToken, (req, res) => {
    // Obtiene los datos del nuevo usuario del cuerpo de la solicitud
    const nuevoUsuario = req.body;
    // Inserta el nuevo usuario en la base de datos
    connection.query('INSERT INTO datos_usuarios SET ?', nuevoUsuario, (error, results) => {
        if (error) {
            console.error('Error al insertar un nuevo usuario:', error);
            return res.status(500).send('Error interno del servidor');
        }
        // Envía una respuesta de éxito al cliente
        res.status(201).send('Usuario añadido correctamente');
    });
});

El PUT

// Ruta para actualizar un usuario existente por su ID
// Utiliza el middleware authenticateToken para verificar la autenticación del usuario
app.put('/usuarios/:id_usuario', authenticateToken, (req, res) => {
    // Obtiene el ID del usuario a actualizar de los parámetros de la solicitud
    const usuarioId = req.params.id_usuario;
    // Obtiene los datos actualizados del cuerpo de la solicitud
    const datosActualizados = req.body;
    // Actualiza los datos del usuario en la base de datos
    connection.query('UPDATE datos_usuarios SET ? WHERE id_usuario = ?', [datosActualizados, usuarioId], (error, results) => {
        if (error) {
            console.error('Error al actualizar el usuario:', error);
            return res.status(500).send('Error interno del servidor');
        }
        // Envía una respuesta de éxito al cliente
        res.send('Usuario actualizado correctamente');
    });
});

Y finalmente el metodo DELETE

// Ruta para borrar un usuario existente por su ID
// Utiliza el middleware authenticateToken para verificar la autenticación del usuario
app.delete('/usuarios/:id_usuario', authenticateToken, (req, res) => {
    // Obtiene el ID del usuario a eliminar de los parámetros de la solicitud
    const usuarioId = req.params.id_usuario;
    // Elimina el usuario de la base de datos
    connection.query('DELETE FROM datos_usuarios WHERE id_usuario = ?', usuarioId, (error, results) => {
        if (error) {
            console.error('Error al eliminar el usuario:', error);
            return res.status(500).send('Error interno del servidor');
        }
        // Envía una respuesta de éxito al cliente
        res.send('Usuario eliminado correctamente');
    });
});

También incluimos en el código, el metodo para hacer el login y que nos tiene que devolver un token JWT si todo ha ido bien. El token, nos permitirá hacer las llamadas autenticadas al reto de funciones.

// Ruta para autenticar un usuario y generar un token JWT
app.post('/login', (req, res) => {
    // Obtiene el nombre de usuario y la contraseña del cuerpo de la solicitud
    const { username, password } = req.body;
    // Verifica si el usuario existe en la base de datos y si la contraseña coincide
    connection.query('SELECT * FROM user WHERE username = ? AND password = ?', [username, password], (error, results) => {
        if (error) {
            console.error('Error al buscar usuario:', error);
            return res.status(500).send('Error interno del servidor');
        }
        if (results.length === 0) {
            // Si no se encuentra el usuario o la contraseña es incorrecta, envía una respuesta de credenciales incorrectas
            return res.status(401).json({ message: 'Credenciales incorrectas' });
        }
        // Si la autenticación es exitosa, genera un token JWT y lo envía al cliente como respuesta
        const token = jwt.sign({ username: username }, secretKey);
        res.json({ token: token });
    });
});

Como siempre, os incluyo el enlace a mi repositorio de GitHub donde podréis encontrar este código.

También podréis encontrar más información sobre express y jsonwebtoken en las páginas de los respectivos proyectos.

Espero que os haya resultado útil este post.

Por Jose Manuel Sanz Prieto

Desarrollador web. En este blog hablo de fotografía, programación con Django, Python, PHP y privacidad.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *