jordanpartridge/spotify-client
A Laravel-friendly client for the Spotify Web API, giving you a clean, expressive way to authenticate and interact with Spotify data such as tracks, artists, albums, and playlists from your PHP applications.
Install the package and (optionally) publish its config:
composer require jordanpartridge/spotify-client:^1.3.0
php artisan vendor:publish --tag=spotify-client-config
If you only need app-level access, this is the minimum viable setup in .env:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_AUTH_FLOW=client_credentials
The first place to look is the config file (config/spotify-client.php) — it now maps cleanly to environment variables, including SPOTIFY_CALLBACK_HOST, SPOTIFY_CALLBACK_PORT, and SPOTIFY_TOKEN_STORAGE_PATH. From there, the fastest first use case is resolving the client from the container and hitting a read-only endpoint:
use JordanPartridge\SpotifyClient\Contracts\SpotifyClientInterface;
$spotify = app(SpotifyClientInterface::class);
$tracks = $spotify->search()->tracks('The Beatles');
If that call returns data, your credentials and bearer token injection are working. If you need user-scoped data (playlists, saved tracks, playback), you're in Authorization Code territory — jump to the second pattern below.
Client Credentials for background/app data. Use this for search, catalog lookups, album/track metadata, and anything that doesn't need a user context. It's the least friction path and the one you should default to in jobs, commands, and scheduled tasks:
// app/Jobs/ImportArtistCatalog.php
public function handle(SpotifyClientInterface $spotify): void
{
$albums = $spotify->artists()->albums($this->artistId);
// ...
}
Because the client is bound in the container, injecting SpotifyClientInterface into jobs, controllers, and commands is the idiomatic pattern — don't app() it inside methods when you can inject it.
Authorization Code for user-scoped work. Resolve the manager and select the driver:
use JordanPartridge\SpotifyClient\Auth\SpotifyAuthManager;
$auth = app('spotify.auth')->driver('authorization_code');
The manager pattern means you can swap drivers in tests by binding a fake or by overriding the driver in the container. Treat app('spotify.auth') as the extension seam — if you need a custom auth strategy later, that's where it plugs in.
Token storage in real apps. The default file-based token store is fine for single-server setups and local dev, but point it at a persistent, writable path outside storage/framework/cache if you're deploying ephemeral containers:
SPOTIFY_TOKEN_STORAGE_PATH=/var/www/shared/spotify/tokens.json
For multi-server or queue-heavy apps, implement TokenStorageInterface against your cache or database and bind it in a service provider — this is the cleanest extension point in the package and avoids per-node token drift.
Refresh is automatic, so don't pre-check expiry. The package handles expiration and refresh internally. Write your code as if tokens are always valid; don't add your own "is this token expired" guard, because you'll end up duplicating logic that's already in the authenticator and can cause double-refresh races.
Callback server config for OAuth. If your local setup uses 127.0.0.1 rather than localhost (common with Docker, Conduit, and some browsers), set the host explicitly so the redirect URI registered in the Spotify dashboard matches exactly:
SPOTIFY_CALLBACK_HOST=127.0.0.1
SPOTIFY_CALLBACK_PORT=8080
php artisan config:clear after changing any SPOTIFY_* env var. Config caching in production will silently ignore new .env values.localhost vs 127.0.0.1 mismatch (Issue #21) causes redirect URI mismatches that look like auth bugs but are registration bugs. Set SPOTIFY_CALLBACK_HOST to exactly what's whitelisted in your Spotify app dashboard.SPOTIFY_TOKEN_STORAGE_PATH is set rather than relying on defaults.SpotifyAuthManager and TokenStorageInterface are worth adopting if you previously hand-rolled auth — otherwise you're carrying duplicated refresh logic.php artisan config:clear, (2) confirm client ID/secret are actually loaded via config('spotify-client...'), (3) verify callback host matches dashboard, (4) check token storage path is writable, (5) confirm you're using the right flow — client credentials cannot access user endpoints no matter how valid the token is.TokenStorageInterface is the intended customisation for persistence. Resist patching the file-based store directly; bind your own implementation instead so upgrades stay painless.How can I help you explore Laravel packages today?