The PHP SDK ships a Laravel queue driver so php artisan queue:work
just works. dispatch() pushes to AsyncBase; a worker pulls, runs your
handle(), and acks on success / nacks on throw.
composer require asyncbase/asyncbase
The service provider is auto-discovered via composer’s
extra.laravel.providers. No manual registration needed.
.env
QUEUE_CONNECTION=asyncbase
ASYNCBASE_KEY=sk_live_...
ASYNCBASE_BASE_URL=https://api.asyncbase.dev
config/queue.php
Add an asyncbase connection:
'connections' => [
// ... existing connections
'asyncbase' => [
'driver' => 'asyncbase',
'key' => env('ASYNCBASE_KEY'),
'base_url' => env('ASYNCBASE_BASE_URL'),
'queue' => env('ASYNCBASE_QUEUE', 'default'),
'group' => env('ASYNCBASE_GROUP', 'laravel-workers'),
'visibility_seconds' => 60,
],
],
That’s it. Laravel now treats AsyncBase like any other queue driver
(redis, sqs, database).
Dispatch + work
// app/Jobs/SendWelcomeEmail.php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SendWelcomeEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(public readonly string $to) {}
public function handle(): void
{
// your work here
}
}
Dispatch from anywhere in your app:
SendWelcomeEmail::dispatch('jane@example.com');
// Or with a delay:
SendWelcomeEmail::dispatch('jane@example.com')->delay(now()->addSeconds(30));
// Or target a specific queue name:
SendWelcomeEmail::dispatch('jane@example.com')->onQueue('emails');
Run the worker:
php artisan queue:work
# or target this connection explicitly:
php artisan queue:work asyncbase --queue=default --tries=3
Under supervisor / systemd:
[program:asyncbase-worker]
command=php /var/www/html/artisan queue:work --queue=default --tries=3
autostart=true
autorestart=true
user=www-data
stdout_logfile=/var/log/asyncbase-worker.log
Mapping — Laravel ↔ AsyncBase
| Laravel concept | AsyncBase |
|---|
Queue::push / dispatch | POST /v1/queues/{name}/messages |
$job->delete() (on success) | POST .../ack |
$job->release() (on throw) | POST .../nack — server applies exp-backoff |
$job->fail() (max tries) | final nack → server moves to DLQ |
Laravel $tries | AsyncBase retries (default 3) |
Laravel retry_after | AsyncBase visibility_seconds |
Laravel’s release($delay) passes a client-chosen delay. AsyncBase
ignores it — the server decides backoff based on the attempt number
(2^attempt × 1000 ms). To customize, set retries per-send via the
raw SDK instead of dispatch.
Raw SDK access
Need fine-grained control (custom delay, FIFO groups, dedup)? Inject the
raw Queue client:
// AppServiceProvider::register()
$this->app->singleton(AsyncBase\Queue::class, fn () =>
new AsyncBase\Queue(env('ASYNCBASE_KEY'))
);
Then:
use AsyncBase\Queue;
public function send(Request $req, Queue $q)
{
$r = $q->send('emails', [...], delay: '30s', fifoGroup: 'user-' . $req->user()->id);
return response()->json(['id' => $r->id]);
}
Mix and match — dispatch() for regular jobs, raw $q->send() for
advanced features.
Testing
Use Laravel’s fake queue:
Queue::fake();
SendWelcomeEmail::dispatch('jane@example.com');
Queue::assertPushed(SendWelcomeEmail::class);
The real driver is bypassed — no AsyncBase API calls in tests.
Horizon + stats
Horizon + queue:monitor rely on queue-size probes that AsyncBase
doesn’t expose yet (pendingSize, delayedSize, reservedSize). The
driver returns 0 for all, so Horizon won’t crash but also won’t show
live sizes. Dashboard at app.asyncbase.dev
is the source of truth for live queue depth.