Etiqueta: autenticación

  • Dejar Postman: usar CLI y scripts para pruebas de APIs

    Dejar Postman: usar CLI y scripts para pruebas de APIs

    Resumen: pasar de Postman a herramientas en terminal y scripts acelera pruebas, evita clics repetitivos y facilita la automatización. Este artículo muestra por qué y cómo hacerlo con ejemplos prácticos.

    Introducción

    Postman es útil para explorar APIs y aprender. Sin embargo, en proyectos con muchos endpoints o cuando se requiere automatización y versionado, depender de una GUI provoca trabajo manual repetitivo y menos control.

    Prerrequisitos

    Antes de reemplazar Postman por comandos y scripts, instala o confirma disponibilidad de las herramientas base y control de versiones.

    • Terminal / shell (bash).
    • curl o HTTPie para solicitudes directas.
    • Un lenguaje para scripts (en los ejemplos usamos JavaScript con fetch o axios).
    • Repositorio Git para versionar colecciones de pruebas.

    Desarrollo

    Procedimiento

    Convierte flujos manuales en comandos y scripts siguiendo pasos repetibles. La idea es minimizar clics, tener trazabilidad en Git y permitir automatización.

    1. Mapea los endpoints y los parámetros que usas en Postman.
    2. Escribe comandos curl o HTTPie para cada caso común (login, CRUD).
    3. Encapsula llamadas frecuentes en scripts (por ejemplo, en JavaScript) para validación y encadenamiento.
    4. Versiona esos scripts y ejecútalos desde CI o tareas programadas para pruebas automatizadas.

    Ejemplos

    Ejemplo básico con HTTPie (fácil de leer y cómodo en terminal).

    http POST https://api.example.com/login username=admin password=1234Lenguaje del código: Bash (bash)

    Ejemplo equivalente con curl (útil cuando necesitas control fino o integración en scripts bash).

    curl -X POST https://api.example.com/data \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{"name":"Kiran","age":25}'Lenguaje del código: Bash (bash)

    Ejemplo en JavaScript para usar dentro de un repositorio (versionable y combinable con pruebas unitarias o de integración).

    const headers = {
      "Authorization": "Bearer YOUR_TOKEN",
      "Content-Type": "application/json"
    };
    const body = { name: "Kiran", role: "developer" };
    
    fetch("https://api.example.com/users", {
      method: "POST",
      headers,
      body: JSON.stringify(body)
    })
      .then(res => res.json())
      .then(data => console.log(data))
      .catch(err => console.error(err));Lenguaje del código: JavaScript (javascript)

    Ventaja práctica: encadenar llamadas, registrar respuestas y usar los scripts en CI sin intervención manual.

    Checklist

    • Convertir endpoints críticos a comandos reproducibles.
    • Guardar scripts en el repositorio con ejemplos y README.
    • Agregar ejecución automática en CI para pruebas básicas.
    • Documentar cómo obtener y rotar tokens de autorización.

    Conclusión

    Postman sigue siendo valioso para aprendizaje y exploración rápida, pero mover pruebas a CLI y scripts reduce errores humanos, mejora la velocidad y habilita automatización y trazabilidad. Empieza por convertir los flujos más repetidos y versiona las pruebas.

  • Notificaciones PHP en tiempo real con Redis y WebSockets

    Notificaciones PHP en tiempo real con Redis y WebSockets

    Resumen: guía práctica para enviar notificaciones en tiempo real desde PHP usando Redis como bus de mensajes y WebSockets para entrega instantánea. Incluye ejemplo mínimo, buenas prácticas y checklist de despliegue.

    Introducción

    Las notificaciones en tiempo real son fundamentales para la experiencia de usuario moderna. En vez de pollings periódicos, una combinación de Redis (Pub/Sub o Streams) y un servidor WebSocket permite entregar mensajes al instante desde PHP.

    Prerrequisitos

    • PHP con soporte para ejecución de procesos permanentes (p. ej. Workerman, Swoole o Ratchet)
    • Redis (instancia accesible desde el servidor WebSocket)
    • Cliente Redis para PHP (Predis o phpredis) y composer
    • Clientes Web que soporten WebSockets y lógica de reconexión

    Desarrollo

    La arquitectura básica separa responsabilidades: la aplicación PHP publicará eventos en Redis; un proceso WebSocket suscrito a Redis retransmitirá esos mensajes a los clientes conectados.

    Procedimiento

    Pasos esenciales:

    1. Publicar un evento en Redis desde la app PHP cuando ocurre la acción (por ejemplo, comentario nuevo).
    2. El proceso WebSocket está suscrito al canal Redis y recibe el evento.
    3. El proceso WebSocket envía el payload a los clientes conectados (filtrando por usuario o canal si aplica).
    4. El cliente maneja el mensaje (mostrar notificación, solicitar detalles, marcar como leído, etc.).

    Ejemplo mínimo de servidor WebSocket en PHP usando Workerman y Predis (suscripción a Redis y broadcast a conexiones activas).

    <?php
    use Workerman\Worker;
    use Predis\Client as RedisClient;
    
    require 'vendor/autoload.php';
    
    $ws = new Worker("websocket://0.0.0.0:8080");
    $ws->count = 1;
    
    $redis = new RedisClient();
    
    $ws->onWorkerStart = function() use ($ws, $redis) {
        $pubsub = $redis->pubSubLoop();
        $pubsub->subscribe('notifications');
        foreach ($pubsub as $message) {
            if ($message->kind === 'message') {
                foreach ($ws->connections as $connection) {
                    $connection->send($message->payload);
                }
            }
        }
    };
    
    Worker::runAll();
    Lenguaje del código: PHP (php)

    Este proceso mantiene una conexión permanente a Redis y distribuye cada mensaje recibido a todas las conexiones WebSocket. En producción conviene no enviar todo el payload a todos los clientes: filtrar por canal/usuario.

    Ejemplo de publicación de un evento desde la aplicación PHP usando Predis.

    <?php
    use Predis\Client as RedisClient;
    
    $redis = new RedisClient();
    
    $event = json_encode([
        'type' => 'comment_created',
        'user_id' => 42,
        'data' => ['id' => 123, 'excerpt' => 'Nuevo comentario']
    ]);
    
    $redis->publish('notifications', $event);
    Lenguaje del código: PHP (php)

    En el ejemplo anterior publicamos en un canal genérico ‘notifications’. Para evitar sobre-broadcasting, use canales por usuario o por topic (p. ej. notifications:user:42).

    Ejemplos

    Cliente JavaScript: conexión WebSocket básica con reconexión y manejo de mensajes ligeros (solo resumen; adapte según su app).

    function createSocket(url) {
      let ws;
      let retry = 1000;
    
      function connect() {
        ws = new WebSocket(url);
    
        ws.onopen = () => {
          retry = 1000; // reset backoff
          console.log('WS connected');
        };
    
        ws.onmessage = (ev) => {
          const msg = JSON.parse(ev.data);
          // manejar evento: mostrar notificación o solicitar detalles
          console.log('evento', msg);
        };
    
        ws.onclose = () => {
          console.log('WS closed, reconnecting in', retry);
          setTimeout(connect, retry);
          retry = Math.min(30000, retry * 2);
        };
    
        ws.onerror = (err) => {
          console.error('WS error', err);
          ws.close();
        };
      }
    
      connect();
      return () => ws && ws.close();
    }
    
    // uso
    const stop = createSocket('wss://example.com:8080');
    Lenguaje del código: JavaScript (javascript)

    Consejo: mantenga los payloads ligeros. Enviar solo el ID del evento o un resumen reduce latencia y consumo de ancho de banda.

    Redis + WebSockets transforman un backend PHP tradicional en un canal de notificaciones cercano a tiempo real, sin necesidad de reescrituras completas.

    Checklist

    1. Usar canales por usuario o topic para evitar sobre-broadcast.
    2. Autenticar y autorizar suscripciones (gateway o tokens JWT).
    3. Implementar reconexión y reintentos en cliente.
    4. Considerar Redis Streams si necesita persistencia y replay de mensajes.
    5. Monitoreo del proceso WebSocket y alertas para reinicio automático.
    6. Limitar tamaño del payload y evitar enviar datos sensibles por el socket.

    Conclusión

    Combinar Redis y WebSockets permite a aplicaciones PHP ofrecer notificaciones instantáneas sin depender de polling. El patrón reduce latencia y escala bien si se aplica filtrado por canales, autenticación y manejo de fallos.

    Empiece con un prototipo mínimo (un proceso WebSocket suscrito a Redis) y evolucione a Streams, balanceo y autenticación según las necesidades de producción.

  • AsyncRequestContext en Laravel: reducir middleware con contexto

    AsyncRequestContext en Laravel: reducir middleware con contexto

    Este artículo explica cómo reemplazar middleware que solo propagan datos por petición con un contexto scoped (AsyncRequestContext) en Laravel. El objetivo: reducir la complejidad de rutas, mejorar la claridad de dependencias y facilitar pruebas unitarias.

    Incluye el diseño del contexto, el enlace en el service container y ejemplos de uso en acciones, controladores, rutas y pruebas.

    Introducción

    El patrón AsyncRequestContext propone un contenedor ligero por petición para almacenar estado que actualmente se inyecta en Illuminate\Http\Request mediante middleware.

    En lugar de ensuciar el Request con claves arbitrarias, se inicializa un contexto por ciclo de petición y se usa desde acciones y bindings del contenedor.

    Prerrequisitos

    Conocimientos básicos de Laravel: service container, providers, uso de acciones/servicios y rutas. No se requiere configuración adicional en el framework más allá de registrar el provider.

    Desarrollo

    La idea central es exponer un RequestContext scoped que se inicializa al enlazar en el container y se limpia en termination. Evita ‘request pollution’ y hace explícitas las dependencias.

    <?php
    namespace App\Context;
    
    class RequestContext
    {
        private static ?array $data = null;
    
        public static function initialize(): void
        {
            self::$data = [];
        }
    
        public static function set(string $key, mixed $value): void
        {
            self::$data[$key] = $value;
        }
    
        public static function get(string $key): mixed
        {
            return self::$data[$key] ?? null;
        }
    
        public static function flush(): void
        {
            self::$data = null;
        }
    }
    Lenguaje del código: PHP (php)

    En el service provider se enlaza RequestContext para inicializarlo por petición y se registra un hook de terminating para limpiar el estado.

    <?php
    namespace App\Providers;
    
    use Illuminate\Support\ServiceProvider;
    use App\Context\RequestContext;
    
    class ContextServiceProvider extends ServiceProvider
    {
        public function boot()
        {
            $this->app->bind(RequestContext::class, function () {
                RequestContext::initialize();
                return new RequestContext();
            });
    
            $this->app->terminating(function () {
                RequestContext::flush();
            });
        }
    }
    Lenguaje del código: PHP (php)

    Procedimiento

    En lugar de usar middleware que sólo añade datos al Request, crea acciones que reciben el Request y escriben en el RequestContext. Eso permite invocarlas donde y cuando hagan falta.

    <?php
    namespace App\Actions;
    
    use Illuminate\Http\Request;
    use App\Context\RequestContext;
    use App\Models\Tenant;
    
    class ResolveTenant
    {
        public function __construct(private RequestContext $context) {}
    
        public function execute(Request $request): void
        {
            $tenant = Tenant::fromDomain($request->host());
            $this->context->set('tenant', $tenant);
        }
    }
    Lenguaje del código: PHP (php)

    Consume el contexto explícitamente en controladores o servicios, lo que revela dependencias y facilita pruebas unitarias.

    <?php
    namespace App\Http\Controllers;
    
    use App\Context\RequestContext;
    
    class ReportController
    {
        public function index(RequestContext $context)
        {
            $tenant = $context->get('tenant');
            return $tenant->reports()->paginate();
        }
    }
    Lenguaje del código: PHP (php)

    Las rutas pueden simplificarse ejecutando acciones de resolución dentro de la closure y pasando el contexto al controlador.

    <?php
    use App\Actions\ResolveTenant;
    use App\Context\RequestContext;
    use App\Http\Controllers\ReportController;
    use Illuminate\Support\Facades\Route;
    
    Route::get('/reports', function (
        ResolveTenant $resolve,
        RequestContext $context
    ) {
        $resolve->execute(request());
        return (new ReportController)->index($context);
    })->middleware('auth:api');
    Lenguaje del código: PHP (php)

    Con este enfoque mantienes middleware para filtrado (autenticación, throttling) y usas acciones/contexto para propagar estado.

    Ejemplos

    Ejemplo de binding contextual que elige una implementación según un valor en RequestContext.

    <?php
    // En AppServiceProvider.php
    $this->app->bind(ReportExporter::class, function () {
        $format = RequestContext::get('export_format') ?? 'csv';
        return match ($format) {
            'excel' => new ExcelExporter,
            'pdf'   => new PDFExporter,
            default => new CSVExporter,
        };
    });
    Lenguaje del código: PHP (php)

    Prueba unitaria sin levantar el HTTP layer inicializando el contexto manualmente:

    <?php
    test('exports tenant reports in excel', function () {
        RequestContext::initialize();
        RequestContext::set('tenant', Tenant::factory()->create());
        RequestContext::set('export_format', 'excel');
    
        $exporter = app(ReportExporter::class);
        $this->assertInstanceOf(ExcelExporter::class, $exporter);
    });
    Lenguaje del código: PHP (php)

    Checklist

    1. Auditar middleware que solo escriben datos en el Request.
    2. Refactorizar esas responsabilidades en acciones (ResolveX classes).
    3. Sustituir $request->get(‘foo’) por $context->get(‘foo’) donde corresponda.
    4. Reducir arrays de middleware en rutas dejando filtros reales (auth, throttle).
    5. Asegurar que RequestContext::flush() se ejecuta en terminating.

    Conclusión

    AsyncRequestContext ofrece una alternativa práctica al uso excesivo de middleware para propagar estado por petición. Mejora la claridad, facilita pruebas y mantiene middleware para filtrado.

    Middleware es para filtrado, no para propagar datos. Deja que el contexto lleve tu estado cross‑cutting.