A Spotify Web API wrapper written in Dart.
This project is in an early stage. Most notably, not all endpoints are available yet, and some response fields are not modeled. If you are missing something in particular, feel free to open a new issue on GitHub.
This project follows semantic versioning, so minor and patch version updates will not contain breaking changes.
The main entry point to the API is the SpotifyWebApi
class. The Dart API is organized similarly to the
Spotify Web API reference, so most endpoints are
collected in groups, e.g. tracks
or albums
.
import 'package:spotify_api/spotify_api.dart';
Future<void> example(AccessTokenRefresher refresher) async {
final api = SpotifyWebApi(
refresher: refresher,
);
// Search for tracks, albums, etc.
final searchResult = await api.search(
query: 'Bohemian Rhapsody',
types: [SearchType.track],
);
// Look up a specific track
final track = await api.tracks.getTrack('3z8h0TU7ReDPLIbEnYhWZb');
}
The AccessTokenRefresher
is expected as a given in this example. See Authentication for more
information.
You'll often encounter responses that contain a page of some API resource, perhaps most prominently when you search for something, or when you try to get the tracks in a playlist.
If you want to retrieve further pages, you can use a Paginator
, which has several convenient methods to go through the
pages:
void example(SpotifyWebApi api, Page<Track> tracks) async {
final paginator = api.paginator(tracks);
// Go over the items on the current page
for (final track in paginator.currentItems()) {
print(track.name);
}
// Go over the items on the next page
final nextPage = await paginator.nextPage();
for (final track in nextPage.currentItems()) {
print(track.name);
}
// Iterate over all tracks in all pages (including the current one)
await paginator.all().forEach((track) {
print(track.name);
});
}
Before you can do anything in the API, you have to think about authentication. Spotify offers four different OAuth flows.
Name | Supported |
---|---|
Authorization code | ✅ |
Authorization code with PKCE | ✅ |
Client credentials | ✅ |
Implicit grant | ⛔ |
Note that it's possible to implement an authentication flow yourself. The table above just shows the existing ones that are ready-to-use.
You'll need to register your app with Spotify before you can use this library. Spotify will give you a client ID and a client secret, the latter of which you may not need, depending on the OAuth flow you want to use.
The Authorization Code OAuth flows yield you a long-lived refresh token. That token should be persisted if you want to
be able to obtain new access tokens without user interaction. For that purpose, there's a simple
RefreshTokenStorage
interface that you'll need to implement.
There is a MemoryRefreshTokenStorage
implementation available, but that will not persist updated refresh
tokens beyond the object's lifetime, essentially discarding all refresh token updates.
This is the simplest full OAuth flow, as it only requires you client ID and your client secret and works without user interaction. It's not possible to access any user data using this flow.
Example:
final api = SpotifyWebApi(
refresher: ClientCredentialsRefresher(
clientId: 'myclientid',
clientSecret: 'supersecret',
),
);
The authorization code flow must be used if you want to access user data. The flow to initially authorize your app and obtain a refresh token is interactive. Once you have a refresh token, it can be used virtually forever (as long as the user does not revoke access).
You'll need to implement the relevant user interaction and production ready storage yourself, but
this package provides a generic framework to implement the OAuth flow with the
AuthorizationCodeUserAuthorization
class:
final flow = AuthorizationCodeUserAuthorization.withoutPkce(
clientId: 'myclientid',
clientSecret: 'supersecret',
stateManager: TtlRandomStateManager(ttl: Duration(minutes: 5)),
redirectUri: Uri.parse('https://example.com/spotifyauthcallback'),
);
// You'll need to direct the user to visit this URL somehow.
final authUrl = await auth.generateAuthorizationUrl(scopes: [Scopes.playlistReadPrivate]);
The stateManager
is responsible for creating and verifying the state for each authorization flow
to prevent CSRF attacks. The TtlRandomStateManager used in the example will create random states,
store them in memory, and clean them up after the given TTL. That may be enough for some users, but
most production setups will require a more durable solution, so the flow object doesn't need to be
stored while waiting for the user.
The redirect URL should match what you've specified in your Spotify app settings in the developer
portal. Note that this package will not provide a server for you. You'll have to figure out yourself
how to receive the callback. Once you have the callback data (see the
UserAuthorizationCallbackBody
model), you can continue with a flow object like created above:
// Let's assume we got the callback data and it's stored in this variable
final UserAuthorizationCallbackBody callback;
// The flow object is the same as above. If all is well, you now have a refresh token!
final refreshToken = flow.handleCallback(callback);
You should store the refresh token somewhere safe. With it, you can initialize a SpotifyWebApi
instance with it. It will refresh access tokens as needed.
final api = SpotifyWebApi(
refresher: AuthorizationCodeRefresher.withoutPkce(
clientId: 'myclientid',
clientSecret: 'supersecret',
refreshTokenStorage: MemoryRefreshTokenStorage(refreshToken),
),
);
Note that Spotify will occasionally respond with a new refresh token in addition to an access token.
The new refresh token will be stored using the refreshTokenStorage
that was passed to the
AuthorizationCodeRefresher
. For that reason, it's highly recommended to use a
RefreshTokenStorage
implementation that will persist the new refresh tokens somewhere, other than
the MemoryRefreshTokenStorage
used in the example above.
If you want to use the recommended authorization code flow with PKCE, you'll simply need to provide
a way to store the code verifier (a string) while the user is logging in. A simple implementation
is available as MemoryCodeVerifierStorage
, but it's recommended to provide a more durable storage
(perhaps using a relational database).
final flow = AuthorizationCodeUserAuthorization.withPkce(
clientId: 'myclientid',
stateManager: TtlRandomStateManager(ttl: Duration(minutes: 5)),
redirectUri: Uri.parse('https://example.com/spotifyauthcallback'),
codeVerifierStorage: MemoryCodeVerifierStorage(),
);
Note that no clientSecret is needed in this case. Use the AuthorizationCodeRefresher.withPkce
constructor to obtain access tokens with your refresh token.