Etiqueta: express

  • PHP vs Node.js: cómo elegir el backend en 2025

    PHP vs Node.js: cómo elegir el backend en 2025

    Resumen: Comparativa técnica entre PHP y Node.js para elegir backend según requisitos de rendimiento, ecosistema y experiencia del equipo.

    Este artículo sintetiza ventajas, limitaciones y ejemplos mínimos para ayudar a arquitectos y desarrolladores a decidir qué tecnología usar.

    Introducción

    PHP y Node.js son tecnologías maduras para backend con enfoques distintos: PHP tradicionalmente síncrono y orientado a request-response; Node.js basado en I/O asíncrono y event-driven.

    Prerrequisitos

    Antes de elegir, evalúa: cargas concurrentes previstas, necesidad de tiempo real, experiencia del equipo y requisitos de despliegue.

    1. Definir el patrón de tráfico (p. ej. muchas conexiones concurrentes vs pocas por usuario).
    2. Identificar dependencias y ecosistema (CMS, bibliotecas, integraciones).
    3. Revisar capacidades de hosting y costos operativos.

    Desarrollo

    Comparar en cuatro ejes: rendimiento, ecosistema, curva de aprendizaje y despliegue.

    Procedimiento

    1) Analiza el caso de uso. 2) Mide concurrencia y latencia objetivo. 3) Selecciona la pila que minimice riesgos técnicos y de operación.

    Resumen de diferencias clave:

    • PHP: buen soporte para sitios de contenido, despliegue sencillo en hosting compartido, amplio ecosistema (WordPress, Composer, Laravel).
    • Node.js: mejor para I/O intensivo y tiempo real; ecosistema moderno con npm y frameworks como Express o Fastify.

    Ejemplos

    Ejemplos mínimos que ilustran el modelo de ejecución en cada plataforma.

    <?php
    // Ejemplo PHP: manejador básico en entorno tradicional (request-response)
    header('Content-Type: application/json');
    $response = ['time' => date('c'), 'message' => 'Hola desde PHP'];
    echo json_encode($response);
    Lenguaje del código: PHP (php)

    En PHP el ciclo típico abre, procesa y cierra la petición; es sencillo y efectivo para sitios de contenido.

    const http = require('http');
    
    const server = http.createServer((req, res) => {
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ time: new Date().toISOString(), message: 'Hola desde Node.js' }));
    });
    
    server.listen(3000, () => {
      console.log('Server listening on port 3000');
    });
    Lenguaje del código: JavaScript (javascript)

    Node.js mantiene un loop de eventos que permite gestionar muchas conexiones concurrentes con eficiencia en I/O.

    Checklist

    Lista rápida para decidir tecnología según el proyecto.

    1. ¿El proyecto requiere tiempo real o muchas conexiones concurrentes? → Considerar Node.js.
    2. ¿Priman CMS o contenidos estáticos y despliegue rápido? → PHP/WordPress puede ser suficiente.
    3. ¿El equipo domina JavaScript o PHP/Laravel? → Elegir según experiencia para reducir riesgos.
    4. ¿Necesitas microservicios o arquitectura basada en eventos? → Node.js suele facilitarlo.

    Conclusión

    No existe una respuesta universal: PHP sigue siendo válido para sitios de contenido y despliegues rápidos; Node.js destaca en aplicaciones en tiempo real y arquitecturas orientadas a I/O.

    La recomendación práctica: prioriza requisitos técnicos, experiencia del equipo y costos operativos al seleccionar entre PHP y Node.js.

  • Registrar cada llamada API sin ralentizar el servidor

    Registrar cada llamada API sin ralentizar el servidor

    Resumen: Cómo registrar cada llamada a tu API sin degradar el rendimiento. Enfoque práctico: logging asíncrono, buffering, offload a workers, enmascarado de datos y rotación.

    Introducción

    Registrar cada petición API es crítico para depuración, auditoría y análisis, pero hacerlo mal puede convertir el logging en el principal cuello de botella.

    La regla de oro: nunca hagas que el manejador principal espere a que los logs se escriban. El registro debe ser asíncrono y, preferiblemente, salir del camino de ejecución principal.

    Prerrequisitos

    Los ejemplos usan Node.js y bibliotecas comunes; adapta las ideas a otros runtimes si es necesario.

    • Dependencia de un logger de alto rendimiento (p. ej. Pino).
    • Mecanismo para buffering o una cola (memoria, Redis, Kafka).
    • Worker o servicio externo para persistencia y envío a sistemas centralizados.

    Desarrollo

    Procedimiento

    Paso 1 — Decide qué registrar: evita sobre-logging. Campos típicos: timestamp, método, ruta, status, latencia, IP, userId, requestId y payload con datos sensibles enmascarados.

    {
      "time": "2025-08-11T14:23:45.123Z",
      "method": "POST",
      "url": "/api/orders",
      "status": 201,
      "responseTimeMs": 123,
      "userId": "usr_1a2b3c",
      "ip": "203.0.113.42",
      "userAgent": "Mozilla/5.0 (Macintosh...)",
      "requestId": "req_abcd1234"
    }
    Lenguaje del código: JSON / JSON con comentarios (json)

    Paso 2 — Usa un logger no bloqueante. Olvida console.log en producción; emplea un logger diseñado para escritura asíncrona y serialización rápida.

    const pino = require('pino');
    const logger = pino({
      level: 'info',
      transport: {
        target: 'pino-pretty'
      }
    });
    
    app.use((req, res, next) => {
      const start = Date.now();
      
      res.on('finish', () => {
        logger.info({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          responseTime: Date.now() - start
        });
      });
      
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 3 — Bufferiza en memoria y envía en lotes. Convierte miles de escrituras pequeñas en pocas escrituras grandes.

    let logBuffer = [];
    const BUFFER_SIZE = 50;
    const FLUSH_INTERVAL = 5000; // ms
    
    function flushLogs() {
      if (logBuffer.length > 0) {
        logger.info({ batch: logBuffer });
        logBuffer = [];
      }
    }
    
    setInterval(flushLogs, FLUSH_INTERVAL);
    
    app.use((req, res, next) => {
      const start = Date.now();
    
      res.on('finish', () => {
        logBuffer.push({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          time: Date.now(),
          latency: Date.now() - start
        });
    
        if (logBuffer.length >= BUFFER_SIZE) {
          flushLogs();
        }
      });
    
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 4 — Desacopla mediante cola y workers: para tráfico intenso, envía logs a una cola y procesa desde procesos independientes.

    const Redis = require('ioredis');
    const pub = new Redis();
    
    app.use((req, res, next) => {
      const start = Date.now();
    
      res.on('finish', () => {
        pub.publish('api_logs', JSON.stringify({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          latency: Date.now() - start
        }));
      });
    
      next();
    });
    Lenguaje del código: JavaScript (javascript)
    const sub = new Redis();
    sub.subscribe('api_logs');
    sub.on('message', (channel, message) => {
      const logData = JSON.parse(message);
      // Persistir logData de forma asíncrona (BD, ELK, S3...)
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 5 — Envío remoto asíncrono: si mandas logs a sistemas como ELK o CloudWatch, hazlo en lotes o por workers para evitar latencia en el request path.

    const winston = require('winston');
    require('winston-cloudwatch');
    
    winston.add(new winston.transports.CloudWatch({
      logGroupName: 'my-api-logs',
      logStreamName: 'production',
      awsRegion: 'us-east-1',
      jsonMessage: true
    }));
    
    app.use((req, res, next) => {
      const start = Date.now();
      res.on('finish', () => {
        winston.info({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          latency: Date.now() - start
        });
      });
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 6 — Enmascara datos sensibles antes de loguear: nunca escribas contraseñas, números de tarjeta o tokens sin protección.

    function maskSensitive(obj) {
      const clone = { ...obj };
      if (clone.password) clone.password = '******';
      if (clone.cardNumber) clone.cardNumber = '**** **** **** ' + clone.cardNumber.slice(-4);
      return clone;
    }
    
    app.use(express.json());
    app.use((req, res, next) => {
      req.body = maskSensitive(req.body);
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 7 — Mide y ajusta: compara latencias y uso de CPU/memoria antes y después del cambio. Usa herramientas de carga y APM para validar impacto.

    autocannon -c 100 -d 30 http://localhost:3000
    Lenguaje del código: Bash (bash)

    Paso 8 — Rotación y archivo: configura rotación por tamaño o día y mueve logs antiguos a almacenamiento económico.

    pino server.js | tee >(pino-pretty) | rotatelogs ./logs/api-%Y-%m-%d.log 86400
    Lenguaje del código: Bash (bash)

    Paso 9 — Distribuye a gran escala: evita escribir localmente, usa shippers (Filebeat/Fluent Bit), Kafka para ingesta masiva y sampling para reducir volumen de success logs.

    Paso 10 — Arquitectura de producción (resumen): gateway → collectors/colas → workers → almacenamiento indexable → dashboards y alertas.

    Ejemplos

    Los ejemplos anteriores cubren patrones comunes: logger rápido (Pino), buffering en proceso, publicación a Redis y consumo por workers, y envío asíncrono a servicios remotos.

    Checklist

    1. No bloquear el request path: logging asíncrono.
    2. Usar un logger de alto rendimiento (p. ej. Pino).
    3. Bufferizar y enviar en lotes.
    4. Offload a workers o colas para tráfico intenso.
    5. Enmascarar datos sensibles antes de persistir.
    6. Rotación y archivado de logs.
    7. Medir impacto y ajustar frecuencias/ tamaños de lote.

    Conclusión

    Registrar cada llamada API es viable si se diseña para no bloquear el flujo principal: emplea loggers rápidos, buffering, colas y workers; enmascara datos; y monitoriza el costo del logging.

    Aplica estas prácticas progresivamente y verifica con pruebas de carga y APM antes de desplegar en producción.