Volver a explorar Snippets

Implementación Segura y Eficiente de API REST para Usuarios con Node.js, Express y PostgreSQL

JAVASCRIPT 16 de junio de 2026 7 lecturas
Este snippet demuestra cómo construir una API RESTful robusta y segura en Node.js con Express y PostgreSQL para la recuperación eficiente de datos de usuario, aplicando las mejores prácticas de seguridad y gestión de conexiones.

En mi experiencia como desarrollador, he identificado que la construcción de APIs RESTful robustas requiere una atención meticulosa a la seguridad, la eficiencia y la gestión de recursos. El snippet original, aunque busca la rapidez, introduce vulnerabilidades críticas y problemas de rendimiento que son inaceptables en un entorno de producción.

Mi objetivo con esta refactorización es demostrar cómo implementar una ruta de Express para recuperar usuarios de una base de datos PostgreSQL de manera segura y eficiente. Abordaremos los siguientes puntos clave:

  • Prevención de SQL Injection: Utilizaré consultas parametrizadas para asegurar que los datos de entrada del usuario sean tratados como valores y no como parte de la estructura de la consulta SQL.
  • Gestión Óptima de Conexiones: Implementaré un Pool de conexiones de pg. Esto permite reutilizar las conexiones a la base de datos, eliminando la sobrecarga de establecer una nueva conexión por cada solicitud y previniendo fugas de recursos.
  • Validación Robusta de Entrada: Añadiré validación para el ID del usuario, asegurando que solo se procesen entradas válidas y numéricas.
  • Manejo Seguro de Errores: Implementaré un bloque try...catch para capturar errores, loguearlos internamente y enviar respuestas genéricas al cliente, evitando la exposición de detalles sensibles del servidor.
  • Filtrado de Datos Sensibles: La consulta SQL seleccionará explícitamente solo los campos necesarios, evitando la exposición accidental de información confidencial como contraseñas hash.
  • Uso de async/await: Modernizaré el código asíncrono para una mayor legibilidad y mantenibilidad.

Esta aproximación no solo mejora drásticamente la seguridad y el rendimiento, sino que también establece un estándar de calidad para el desarrollo de APIs en Node.js con PostgreSQL.

const express = require('express');
const { Pool } = require('pg'); // Usar Pool para la gestión de conexiones
const app = express();
app.use(express.json());

// Configuración del pool de conexiones a PostgreSQL
const pool = new Pool({
    connectionString: 'postgres://user:pass@localhost:5432/db',
    max: 10, // Máximo de clientes en el pool
    idleTimeoutMillis: 30000, // Tiempo de inactividad antes de que un cliente sea cerrado
    connectionTimeoutMillis: 2000 // Tiempo máximo para adquirir una conexión
});

// Manejo de errores del pool (opcional pero recomendado)
pool.on('error', (err, client) => {
    console.error('Error inesperado en el pool de conexiones', err);
    // Un error en el pool no debería detener la aplicación, pero debe ser monitoreado.
});

// Ruta segura y eficiente para obtener un usuario por ID
app.get('/api/v1/users/:id', async (req, res) => {
    const { id } = req.params;

    // 1. Validación de entrada: Asegurarse de que el ID es un número entero válido
    if (!id || !/^\d+$/.test(id)) {
        return res.status(400).json({ message: 'ID de usuario inválido. Debe ser un número entero.' });
    }

    let client; // Declarar client fuera del try para asegurar que se pueda liberar en finally
    try {
        // 2. Obtener una conexión del pool
        client = await pool.connect();

        // 3. Consulta parametrizada para prevenir SQL Injection
        const query = 'SELECT id, username, email FROM usuarios WHERE id = $1'; // Seleccionar solo campos necesarios
        const result = await client.query(query, [id]);

        if (result.rows.length === 0) {
            return res.status(404).json({ message: 'Usuario no encontrado.' });
        }

        // 4. Enviar solo los datos relevantes y filtrados
        const user = result.rows[0];
        // Si hubiera campos sensibles como 'password_hash', se filtrarían aquí si no se hizo en la consulta SQL
        // delete user.password_hash; // Ejemplo si se seleccionó accidentalmente

        res.status(200).json(user);

    } catch (error) {
        // 5. Manejo de errores: Loggear el error internamente y enviar un mensaje genérico al cliente
        console.error('Error al obtener usuario por ID:', error.message, error.stack);
        res.status(500).json({ message: 'Ocurrió un error interno del servidor al procesar su solicitud.' });
    } finally {
        // 6. Liberar la conexión al pool
        if (client) {
            client.release();
        }
    }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Servidor escuchando en el puerto ${PORT}`);
});

// Manejo de cierre de la aplicación para terminar el pool de conexiones
process.on('SIGINT', async () => {
    console.log('Cerrando pool de conexiones de PostgreSQL...');
    await pool.end();
    console.log('Pool de conexiones cerrado. Saliendo de la aplicación.');
    process.exit(0);
});
¿Qué te pareció?
🔥 Brillante 1
💡 Me sirvió 0
🚀 A otro nivel 0