Ir al contenido principal
Laravel, shipping fast.

Tres temas salen en toda conversación sobre el pipeline de una API. En cada uno, la cantidad correcta de código que hay que escribir es casi ninguna.

CORS. El middleware HandleCors de Laravel ya está en la pila global, y su configuración por defecto es abierta: cualquier origen puede llamar a cualquier cosa bajo api/*. Para una API que da la cara a navegadores, la restringes a tus propios front ends. Para una API a la que solo llaman otros servidores, como esta, CORS no debería aplicarse en absoluto, porque es un mecanismo de navegador. Publica la configuración con php artisan config:publish cors y deja paths como un array vacío. El valor abierto por defecto hace poco daño a una API que se autentica con un token bearer y sin cookies, pero «hace poco daño» no es un ajuste que hayas elegido tú. (Documentación de Laravel: Routing › Cross-Origin Resource Sharing (CORS).)

Compresión. Gzip le corresponde al servidor web o a la CDN que está delante de tu aplicación. Nginx comprime una respuesta JSON más rápido de lo que puede PHP, sabe qué tipos de contenido saltarse y fija la cabecera Vary correctamente.

Seguridad del transporte. Sirve la API solo por HTTPS, y rechaza el HTTP plano directamente en el balanceador de carga en lugar de redirigirlo. Para cuando se envía una redirección, el token bearer ya cruzó la red en texto claro. Strict-Transport-Security y X-Content-Type-Options: nosniff son una línea cada una en la configuración del servidor web.

La petición completa

POST /api/licenses
  servidor  límite por dirección           429
  global    proxies, CORS, tamaño máximo   Laravel
  api       SetLocale
            autenticación                  401
            throttle por consumidor        429
            comprobación de habilidad      403
  request   StoreLicenseRequest            422
  acción    LicenseController@store        201
  después   se registra la petición        terminate()

Lee la columna de la derecha de arriba abajo. Una petición se rechaza lo antes y lo más barato posible: una sin autenticar cuesta una búsqueda de token, una con throttle cuesta una lectura de caché, y ninguna se acerca al proveedor. Para eso sirve el pipeline: para disponer ese orden.

Probar el pipeline

Los middleware se prueban a través de los endpoints que protegen:

// tests/Feature/ThrottleTest.php
it('throttles a consumer past its limit', function () {
    Sanctum::actingAs(
        Consumer::factory()->create(),
        ['licenses:read'],
    );
    $this->mock(LicenseContract::class)
        ->shouldReceive('all')->andReturn(collect());

    foreach (range(1, 60) as $attempt) {
        $this->getJson('/api/licenses')->assertOk();
    }

    $this->getJson('/api/licenses')
        ->assertTooManyRequests()
        ->assertHeader('Retry-After');
});

El contrato se reemplaza para que el test nunca toque al proveedor. Lo que se pone a prueba es la petición número sesenta y uno.

Y uno para la afirmación que este capítulo hizo sobre los rechazos:

// tests/Feature/RequestLogTest.php
it('records a refused request', function () {
    $this->getJson('/api/licenses')->assertUnauthorized();

    $this->assertDatabaseHas('api_requests', [
        'status' => 401,
        'consumer_id' => null,
    ]);
});

Resumen del capítulo 6

  • Los middleware se configuran en bootstrap/app.php. Usa api() para los tuyos, no la pila global.
  • Lee la lista de middleware que Laravel ya ejecuta antes de escribir uno.
  • Define el limitador api una vez, con el consumidor como clave. Laravel envía las cabeceras del límite y el 429.
  • La autenticación se ejecuta antes que ese limitador, así que acota las peticiones por dirección delante de la aplicación, y responde 429 ahí también.
  • Dile a Laravel en qué proxies confiar, por dirección, o todos los llamantes anónimos compartirán un contador.
  • Fija el idioma a partir de Accept-Language con getPreferredLanguage().
  • Registra cada petición en terminate(), incluidas las rechazadas. Nunca el token. Los cuerpos, solo cuando lo hayas decidido.
  • CORS, la compresión y la seguridad del transporte son configuración. Cierra CORS si ningún navegador te llama, y rechaza el HTTP plano.

No se pudo cargar el audio. Inténtalo de nuevo en un momento.