Membangun middleware dan pipeline HTTP di RoadRunner: memakai middleware bawaan level Go, menulis middleware PSR-15 di sisi PHP untuk auth, CORS, dan logging, serta menyusun urutan eksekusi yang benar.

Di episode 5 kita sudah mengenal middleware bawaan RoadRunner seperti gzip dan static. Pada episode kali ini kita melebarkan pembahasan menjadi middleware & pipeline secara utuh: apa itu middleware, di level mana ia bisa hidup (Go atau PHP), bagaimana menyusun pipeline, dan praktik membangun middleware auth, CORS, serta logging.
Mengapa episode ini penting? Karena middleware adalah lapisan pertama yang menerima setiap request — tempat paling tepat untuk logika lintas-cutting seperti autentikasi, pembatasan, dan instrumentasi. Memahami di mana meletakkan middleware (Go vs PHP) menentukan performa dan fleksibilitas aplikasi kalian.
RoadRunner memberi dua tempat untuk middleware:
http.middleware. Sangat cepat, tidak menyentuh PHP, cocok untuk concern infrastruktur (kompresi, static, header, rate limit).Aturan praktis: semakin dini concern bisa ditangani tanpa PHP, semakin baik. Rate limit dan kompresi jangan dibayar dengan worker PHP jika bisa dilakukan Go.
RoadRunner worker memakai standar PSR-15 (Psr\Http\Server\MiddlewareInterface). Contoh middleware auth sederhana:
<?php
declare(strict_types=1);
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
final class AuthMiddleware implements MiddlewareInterface
{
public function __construct(private string $apiKey)
{
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($request->getHeaderLine('X-Api-Key') !== $this->apiKey) {
return new \GuzzleHttp\Psr7\Response(401, ['Content-Type' => 'application/json'], '{"error":"unauthorized"}');
}
return $handler->handle($request);
}
}Perhatikan kontrak PSR-15: setiap middleware menerima request dan handler, lalu memutuskan meneruskan ke middleware berikutnya ($handler->handle($request)) atau memotong dengan response langsung (di sini: 401).
Middleware disusun sebagai pipeline — objek yang membungkus satu sama lain. Di worker, susun urutannya:
use GuzzleHttp\Psr7\Response;
use Middlewares\Utils\Dispatcher;
$cors = new CorsMiddleware();
$auth = new AuthMiddleware($config->apiKey);
$logger = new LoggingMiddleware($logger);
$app = function (ServerRequestInterface $request): ResponseInterface {
return new Response(200, [], 'Hello World');
};
$pipeline = new Dispatcher([
$cors,
$logger,
$auth,
$app,
]);Bila memakai framework, pipeline ini biasanya sudah disediakan — di Laravel Octane kalian cukup memakai app/Http/Kernel.php $middleware seperti biasa; Octane yang mengeksekusinya di dalam worker. Pola manual di atas berguna untuk aplikasi tanpa framework (Spiral, atau bare worker).
Tip
Urutan pipeline penting: CORS dan logging biasanya ditaruh di luar auth supaya request yang ditolak tetap tercatat dan response tetap mendapat header CORS. Pikirkan setiap middleware sebagai onion — request masuk dari luar ke dalam, response keluar dari dalam ke luar.
Beberapa concern lebih baik di level Go. Contoh menambahkan header keamanan dan rate limit sederhana:
http:
address: 0.0.0.0:8080
middleware: ["headers", "gzip"]
headers:
response:
"X-Frame-Options": "DENY"
"X-Content-Type-Options": "nosniff"
"Referrer-Policy": "no-referrer"RoadRunner juga mendukung middleware kustom yang ditulis dalam Go dan dikompilasi ke binary (via Velox, episode 23). Ini pola lanjutan: kalian menulis logika high-performance di Go dan memanggilnya sebagai middleware, sambil tetap memakai PHP untuk middleware bisnis.
| Kesalahan | Dampak | Solusi |
|---|---|---|
| Logika berat di middleware Go | Config sulit di-debug | Pindahkan ke PHP bila butuh logika dinamis |
| Mutasi request di middleware | Response tidak sesuai ekspektasi | Buat request baru (immutable PSR-7) |
| Urutan salah (auth di luar CORS) | Request ditolak tanpa CORS header | Susun onion dengan benar |
| Middleware menyimpan state | Bocor antar request | State harus per-request |
Pada episode 8 ini, kalian telah membangun pipeline middleware.
Inti yang harus dibawa pulang:
$handler->handle() atau potong dengan response.Di episode 9 selanjutnya, kita belajar logging & observability — mengkonfigurasi plugin logs, memakai structured logs, mengintegrasikan PSR-3 dengan worker, dan mengekspor telemetry ke OpenTelemetry atau file log. Sampai jumpa di episode 9!