puth/laravel
A Laravel integration package for Puth, providing service provider registration, configuration publishing, and a convenient facade for using Puth features inside Laravel applications. Lightweight, easy to install via Composer, and designed for quick setup with minimal configuration.
puth/laravel is a thin Laravel client for the Puth API (puth.io). With one star and no dependents, treat it as a small, unproven convenience wrapper rather than a battle-tested abstraction — budget time to read the source before committing it to a production integration.
Minimal steps:
Install and pin.
composer require puth/laravel
Because the package is not widely used, pin the exact version in composer.json and commit composer.lock. There is no ecosystem pressure to keep it current, so accidental upgrades are your main supply-chain risk.
Read the source first. Clone the repo and look at, in order:
src/*ServiceProvider.php) — this tells you what gets registered, whether registration is deferred, and which bindings/singletons exist;config/puth.php or similar) — this is your contract for credentials and endpoints;Ten minutes here saves an hour of guessing later.
Publish config and set credentials.
php artisan vendor:publish --provider="Puth\Laravel\PuthServiceProvider"
Then add the key to .env (e.g. PUTH_API_KEY=..., PUTH_BASE_URL=...) and reference it from config/puth.php via env(). Never read env() outside config files — it returns null once config is cached.
First use case: a single authenticated call. Make one read-only request in php artisan tinker (a GET to a "me"/account or list endpoint) before writing any application code. That validates credentials, base URL, and the transport in one step. If it works in tinker but not in your app, the problem is config caching or container binding, not the API.
Wrap it immediately. Do not call the client directly from controllers or jobs. Put it behind a small interface in your own namespace (App\Contracts\PuthClient) so you can swap the package out later without touching call sites.
Repository/wrapper pattern (recommended). The package is a client, not a domain model. Bind your own service around it:
// App\Providers\AppServiceProvider::register()
$this->app->bind(PuthClient::class, fn ($app) => new PuthClientAdapter(
$app->make(\Puth\Laravel\Puth::class)
));
This is your insurance policy: a one-star package may be abandoned, and an interface lets you replace the transport with a plain Http::withToken(...) builder in an afternoon.
Dependency injection over facades. Facades are convenient in tinker but harder to test and hide the dependency. Prefer constructor injection of the client (or your adapter) in services, jobs, and listeners.
Queue jobs for anything write-shaped or slow. Wrap outbound calls in a job with $tries, $backoff, and $timeout set explicitly:
public int $tries = 3;
public array $backoff = [10, 60, 300];
public int $timeout = 30;
Use retry() on the job only for 5xx/429 responses — never blanket-retry 4xx, which will just burn quota.
Testing with Http::fake(). Laravel clients built on the HTTP client honor fakes if they go through Illuminate\Support\Facades\Http. Fake at the URL pattern level:
Http::fake(['api.puth.io/*' => Http::response(['data' => []], 200)]);
If the package uses Guzzle directly and ignores fakes, that's a strong signal to wrap it and fake your own adapter instead. Verify Http::assertSent() in at least one test so a silent URL change can't slip through.
Idempotency keys on writes. If the API supports them, generate a UUID per logical operation and persist it (e.g. on the model) so a queued retry doesn't duplicate a record. This matters more with a lightly-used client because you won't find community write-ups of its retry behaviour.
Config-driven environments. Keep base URL, timeout, and retry counts in config/puth.php so staging can point at a sandbox without code changes.
Observability. Log request ID / correlation ID from the response headers on every call, and emit a counter or timing metric around the adapter. With no dependents, you have no external signal if the API changes shape — your own logs are the only early warning.
env() call the package makes outside a config file returns null under php artisan config:cache. If auth suddenly fails in a cached deployment but works locally, this is almost always the cause. Audit the package's source for env( and, if present outside config, file an issue or override the binding.bootstrap/providers.php (Laravel 11+) or config/app.php (10 and below) if the package's auto-discovery misbehaves.RequestException/ConnectionException or returns a response object you must inspect. Assume nothing — wrap every call in a try/catch, map 401/403 to an alert (bad key), 429 to backoff (quota), and 5xx to a retryable job failure.PUTH_FAKE=true branch in your adapter that returns fixture data. This keeps feature tests fast and decouples your team from the real API's availability.illuminate/* or guzzlehttp/guzzle to an older range. Run composer why-not puth/laravel <laravel-version> before upgrading Laravel; with a small package, the constraint is rarely maintained.php artisan puth:ping) that performs one authenticated call. Wire it into your deploy smoke test or uptime monitor so an expired key is caught before a user notices.Http::withToken(config('puth.key'))->baseUrl(config('puth.base_url')) client is a contained change, not a refactor.How can I help you explore Laravel packages today?