userbase/client
A lightweight PHP client for integrating Userbase into Laravel and other PHP applications. It simplifies authentication and user data workflows with a clean API, helping you add secure user management without boilerplate.
userbase/client is a thin, framework-agnostic PHP SDK (LinkORB) for talking to a UserBase service. With 5 stars and zero dependents, treat it as a small library you read before you trust — the source is the documentation.
Install and pin.
composer require userbase/client:^1.0
Pin a concrete constraint rather than dev-master; releases are sparse.
Read these files first, in this order:
src/ — usually one Client class plus a handful of DTO/model classes.composer.json — check the Guzzle/PSR-18 constraint; this is where Laravel conflicts show up.README.md / tests/ — the tests are the real usage examples when the README is thin.Confirm there is no Laravel service provider. LinkORB packages are typically plain PHP. Don't expect a facade or auto-discovery; you will wire it yourself.
Smoke test before designing anything. In php artisan tinker:
$client = new Userbase\Client\Client(config('services.userbase.url'), config('services.userbase.token'));
dump($client->getUser('some-id'));
Note the exact return type (DTO vs array vs null on 404) — this drives everything downstream.
First real use case: read-side lookups. Resolve a user by ID/email in a controller or login flow, render their profile. Keep the first integration read-only until you've seen how errors surface.
Bind it as a singleton in a service provider. Construct once, inject everywhere:
$this->app->singleton(Client::class, fn ($app) => new Client(
baseUrl: config('services.userbase.url'),
apiKey: config('services.userbase.token'),
));
Put credentials in config/services.php under a userbase key and in .env; never inline them.
Wrap it in an anti-corruption layer. Given the package's maturity, do not let Userbase\Client\* types leak into your domain. Define a small interface (UserDirectory) with your own DTOs and adapt the package inside one class. If the package is abandoned or its API shifts, you rewrite one file, not forty.
Prefer constructor injection over a facade. There's no facade anyway, and typed injection makes it trivially swappable in tests.
Custom auth provider, if UserBase is your identity store. Implement Illuminate\Contracts\Auth\UserProvider (retrieveById, retrieveByCredentials, validateCredentials) on top of the client, then register a guard driver. Keep session/remember-token concerns in your app — the SDK only knows about remote records.
Cache read paths. User lookups are usually hot and rarely changing. Wrap reads in Cache::remember("userbase:user:{$id}", 300, ...) and invalidate on writes. This also blunts upstream latency and outages.
Normalise errors at the boundary. Catch the package's exceptions (and GuzzleHttp\Exception\* if it isn't wrapped) in your adapter and rethrow as domain exceptions: UserNotFound, UserbaseUnavailable. Distinguish 404 from 5xx from timeout — callers behave differently for each.
Test with a fake, not the network. Bind a fake implementation of your interface in tests. If you must test the adapter itself, use Guzzle's MockHandler or Http::fake() depending on which transport it uses. Never let CI hit a live UserBase.
Multi-tenant / multi-environment. If base URL or token varies per tenant, register a scoped or named binding per tenant rather than mutating a shared singleton — the client likely holds its base URL immutably.
composer.lock, and read the diff before bumping.guzzlehttp/guzzle:^6 requirement will fight modern Laravel. If it conflicts, use the adapter pattern and call the API directly instead.timeout and connect_timeout through whatever options the constructor exposes; an unresponsive UserBase should not hang a request worker.storage/logs. Scrub Authorization and any custom key header.null, some throw, some return an empty DTO. Assert this behaviour in a test so a future version bump can't silently change it.DateTime parsing of null or empty strings is a classic crash site. Normalise in your adapter.Cache or a simple failure counter) so an upstream outage degrades gracefully instead of cascading.Client is final or its methods are private-heavy, compose rather than extend — a decorator implementing your own interface is almost always the cleaner seam.How can I help you explore Laravel packages today?