richardfullmer/rabbitmq-management-api
A PHP client for the RabbitMQ Management HTTP API, letting you inspect and manage brokers, queues, exchanges, connections, and channels from Laravel or any PHP app. Clean, framework-agnostic wrapper with an expressive interface for monitoring and administration.
This is a thin, dependency-light PHP client over the RabbitMQ Management HTTP API — not AMQP. It talks to port 15672, not 5672, and never opens a broker connection. Everything it does (list queues, purge, close connections, create vhosts, publish test messages) is an HTTP call against the same API the web console uses.
Minimum viable setup:
rabbitmq-plugins enable rabbitmq_management, and confirm http://rabbitmq-host:15672/api/overview returns JSON.composer require richardfullmer/rabbitmq-management-api — check the PHP constraint in its composer.json first; this package's last release was 2020, so on PHP 8.3+/Laravel 11 you may need to confirm it installs cleanly.src/ManagementApi.php first, then src/Api/. There is one class per REST resource (Queues, Exchanges, Bindings, Connections, Channels, Consumers, Nodes, Users, Vhosts, Permissions, Policies, Overview). Those two directories are the whole package; there is no service provider, facade, or config.AppServiceProvider:// config/rabbitmq.php
'management' => [
'url' => env('RABBITMQ_MANAGEMENT_URL', 'http://rabbitmq:15672'),
'user' => env('RABBITMQ_MANAGEMENT_USER', 'guest'),
'pass' => env('RABBITMQ_MANAGEMENT_PASS', 'guest'),
],
// AppServiceProvider::register()
$this->app->singleton(\RabbitMq\ManagementApi\ManagementApi::class, fn () => new \RabbitMq\ManagementApi\ManagementApi(
config('rabbitmq.management.url'),
config('rabbitmq.management.user'),
config('rabbitmq.management.pass'),
));
(Verify the exact namespace against the package's composer.json autoload block — it is not RichardFullmer\....)
First use case: an ops health snapshot. This is the 80% use case for a Laravel dev — you already have a queue worker fleet and want visibility you don't get from queue:work logs:
$api = app(ManagementApi::class);
$overview = $api->overview()->get();
$queues = $api->queues()->all();
foreach ($queues as $q) {
if ($q['messages_ready'] > 1000 || $q['consumers'] === 0) {
Log::warning('rabbit.queue.alert', [
'vhost' => $q['vhost'],
'queue' => $q['name'],
'ready' => $q['messages_ready'],
'consumers' => $q['consumers'],
]);
}
}
Wrap it, don't sprinkle it. The raw client returns decoded arrays and throws Guzzle exceptions. Bind it once, then expose a small domain service (RabbitAdmin) with methods like queueDepth(string $queue): int, purgeQueue(string $queue): void, closeStaleConnections(): int. Your controllers and commands should never touch $api->queues()->get(...) directly — that keeps the (stale) third-party surface area in one file you can swap out later.
Dashboard / admin panel pattern. Cache aggressively. The management API rebuilds stats per request and is expensive on a busy cluster:
$snapshot = Cache::remember('rabbit.snapshot', now()->addSeconds(15), function () use ($api) {
return collect($api->queues()->all())
->map(fn ($q) => Arr::only($q, ['vhost','name','messages','messages_ready','messages_unacknowledged','consumers','state']))
->values()
->all();
});
Poll from a scheduled php artisan rabbit:snapshot command writing to cache; let the UI read the cache. Never call /api/queues on page load in production.
Provisioning pattern (multi-tenant or per-environment). This is where the package genuinely saves you from hand-clicking the console. A typical onboarding command creates vhost → user → permissions → policy → queues → bindings in order:
$api->vhosts()->create($vhost);
$api->users()->create($user, $password, ['monitoring']);
$api->permissions()->create($vhost, $user, [
'configure' => '.*', 'write' => '.*', 'read' => '.*',
]);
$api->policies()->create($vhost, 'dlx-default', '.*', [
'dead-letter-exchange' => 'dlx',
], 0);
$api->queues()->create('orders', $vhost, ['x-queue-type' => 'quorum']);
$api->bindings()->create($vhost, 'orders', 'orders.created', [], 'orders');
Note the split: creating a queue does not create its bindings — you must call bindings()->create() explicitly. Same for exchanges.
Routing smoke tests. Because publishing goes over HTTP, you can push a message into an exchange from a PHPUnit test or a rabbit:smoke command without ext-amqp or a live worker:
$api->exchanges()->publish($vhost, 'amq.topic', 'orders.created', [
'producer' => 'smoke-test', 'payload' => ['order_id' => 123],
]);
Then assert the message lands by polling the bound queue's messages_ready. Useful as a pre-deploy gate in CI against a docker-compose RabbitMQ.
Housekeeping command. A nightly rabbit:prune that purges dead-letter queues older than N days, deletes queues that have been at zero consumers and non-zero ready for 24h, and closes connections whose client_properties.product no longer exists. This is the highest-value operational script you can build on this package.
Testing. Inject a GuzzleHttp\Handler\MockHandler (the client accepts a Guzzle instance) or bind a fake RabbitAdmin in the container. Don't hit a real broker from feature tests unless it's a dedicated docker service.
RABBITMQ_MANAGEMENT_URL is not your queue.connections.rabbitmq.host in the same format. Put the management URL and the AMQP credentials in one config file so they can't drift; a common bug is the app consuming from one broker while the admin panel reports on another./ must be URL-encoded as %2F when you hand-build any HTTP call. The library usually handles this, but if you drop to raw Guzzle for a missing endpoint, this will bite you with 404s that look like auth errors.messages and consumers lag the broker by a few seconds. Never assert on them immediately after publishing in a test without a retry/poll loop — flaky CI is the usual symptom./api/queues is heavy. On a cluster with thousands of queues the payload is multiple MB. Prefer queues()->get($vhost, $name), or pass pagination/columns query params if the wrapper exposes them. Cache the full list; don't request it per row.monitoring tag to read overview/nodes/queues, management for most mutations, and administrator to create users/vhosts/permissions. Creating a "read-only dashboard user" with no tags and wondering why you get 401s is the classic first-day mistake.guest only works from localhost. In Docker that means the container is not localhost — create a real user or you'll chase connection refused/401s. Also, guest is deleted by default in newer RabbitMQ images unless you pass RABBITMQ_DEFAULT_USER.GuzzleHttp\Exception\ClientException. A missing queue is a 404, a bad permission is 403; letting these bubble to a Livewire/Inertia action produces a 500. Map to domain exceptions. RequestException covers timeouts and 5xx — retry those, never retry 4xx.GuzzleHttp\Client(['timeout' => 5, 'connect_timeout' => 2]) into the constructor.vladimir-yuldashev/laravel-queue-rabbitmq.?columns= optimization, no async, no automatic pagination helper, no typed DTOs, no Laravel cache/retry integration. Keep a small RabbitAdmin wrapper — it's a 100-line class that makes the package survivable and lets you migrate to a hand-rolled Http:: client with five typed methods if the dependency goes unmaintained, which is a real risk given the last release date and zero dependents.How can I help you explore Laravel packages today?