A Kotlin Multiplatform client for Deezerβs official REST API. Using Ktor Client. Supports Android (min SDK 24 / JVM 8+), JVM Java, Kotlin/JVM, Kotlin/Native platforms.
- Android minSdk Version 24
- JVM target Java 8+ / Kotlin JVM 1.8
Gradle
// common main
implementation("io.github.kingg22:deezer-client-kt:<latest-version>")
Maven
<dependency>
<groupId>io.github.kingg22</groupId>
<artifactId>deezer-client-kt-jvm</artifactId>
<version>current-version</version>
</dependency>
Install a Ktor client engine, see detail.
Example with CIO (Coroutines based):
Gradle
implementation("io.ktor:ktor-client-cio:$ktor_version")
Maven
<dependency>
<groupId>io.ktor</groupId>
<artifactId>ktor-client-cio-jvm</artifactId>
<version>${ktor_version}</version>
</dependency>
- β Kotlin
val client = DeezerApiClient()
val artist: Artist = client.artists.getById(27) // suspend fun
println("Artist: ${artist.name}") // Artist: Daft Punk
β οΈ Java (Blocking)
public class Test {
public static void main(String[] args) {
final var client = new DeezerApiJavaClient();
final Artist artist = client.artists.getById(27);
System.out.println("Artist: " + artist.getName()); // Artist: Daft Punk
}
}
- β Java (CompletableFuture)
import java.util.concurrent.CompletableFuture;
public class Test {
public static void main(String[] args) {
final var client = new DeezerApiJavaClient();
final CompletableFuture<Artist> future = client.artists.getByIdFuture(27);
future.whenComplete((artist, err) -> {
if (err != null) err.printStackTrace();
else System.out.println("Artist: " + artist.getName()); // Artist: Daft Punk
});
}
}
Kotlin
val client: DeezerApiClient // use the same client in the app
val result: PaginatedResponse<Track> = client.searches.search("eminem")
checkNotNull(result.next)
val nextPage: PaginatedResponse<Track> = checkNotNull(tested.fetchNext(expand=true))
// because the next is not null, fetchNext don't return null
// expand means the previous data (List<Track>) going to expand with the new response
// fetchNext is an extension function*
Java
import java.util.concurrent.CompletableFuture;
import io.github.kingg22.deezer.client.api.objects.PaginatedResponses;
public class Test {
public static void main(String[] args) {
final DeezerApiJavaClient client = "";// use the same client in the app
final PaginatedResponse<Track> response = client.searches.search("eminem");
final PaginatedResponse<Track> nextPage =
// Import the java helper
// Blocking
PaginatedResponses.fetchNext(response, Track.class, /* expand */ true);
final CompletableFuture<PaginatedResponse<Track>> nextPageFuture =
// async
PaginatedResponses.fetchNextFuture(response, Track.class, /* expand */ true);
// Null type is the same, but in java isn't a mandatory check, is recommended!
}
}
Kotlin
val client: DeezerApiClient // use the same client in the app
val episode: Episode = client.episodes.getById(526673645) // suspend fun
// after do things ...
val freshEpisode: Episode = episode.reload() // suspend fun
Java
import java.util.concurrent.CompletableFuture;
class Test {
public static void main(String[] args) {
final DeezerApiJavaClient client = "";// use the same client in the app
final Episode episode = client.episodes.getById(526673645);
final Episode freshEpisode = Resources.reload(tested); // blocking
final CompletableFuture<Episode> freshEpisodeFuture = Resources.reloadFuture(tested); // async
}
}
Kotlin
import io.github.kingg22.deezer.client.api.objects.SearchOrder
import io.github.kingg22.deezer.client.api.routes.SearchRoutes.Companion.buildAdvancedQuery
import io.github.kingg22.deezer.client.api.routes.SearchRoutes.Companion.setStrict
import kotlin.time.Duration.Companion.minutes
val client: DeezerApiClient // use the same client in the app
val query = buildAdvancedQuery { // DSL builder
artist = "eminem"
durationMin = 1.minutes
}
client.searches.searchPlaylist(query, strict = setString(true))
client.searches.searchTrack(
q = buildAdvancedQuery(q = "Not Afraid", artist = "eminem"), // function
order = SearchOrder.RANKING,
limit = 15,
index = 10,
)
Java
import static io.github.kingg22.deezer.client.api.routes.SearchRoutes.buildAdvancedQuery;
import static io.github.kingg22.deezer.client.api.routes.SearchRoutes.setStrict;
public class Test {
public static void main(String[] args) {
final DeezerApiJavaClient client = "";// use the same client in the app
client.searches.searchAlbum(
/* q =*/ buildAdvancedQuery(/* q = */ "King") // Only access to Java builder style
.artist("eminem")
.build(),
/* strict = */ setStrict(true),
/* order = */ SearchOrder.RATING_DESC,
/* index = */ 10,
/* limit = */ 15
);
}
}
But where is the http client of ktor?
Because the client needs to configure custom validators, throw exceptions; etc. I create an HttpClientBuilder
,
you can use it to configure some specs.
The default constructor of the client expects you to add an engine on your dependencies, and that's it.
Why not data class?
Read more about this here.
I take the decision to use class with Poko because immutability is guaranteed and equals are generated.
The idea of this api client is stateless, fetch what u need, and that's it.
Only PaginatedResponse
is data class to easy duplicate and fetch data.
- Kotlin can't access to the java version and viceversa
- Kotlin extensions vs. an external object with static methods (helpers / utility class)
- Java can't access some kotlin specific like DSL, reified types, suspend methods, function with kotlin duration
- Java wrappers of kotlin stuffs, so kotlin is a priority and dictates the route
- Java needs to use kotlin datetime LocalDate and LocalDateTime when get dates :/
- Similitude kotlin suspend and java blocking it reads the same >_<
- Support authenticated endpoints (user edit, user playlist edit, etc.)
- OAuth flow and token refresh support
- ktor-client-content-negotiation-json with kotlinx-serialization
- ktor-client-logging with Sf4lj Nop in runtime.
- kotlinx-datetime for java is not supported easy less, but can use long as seconds in some cases.
- Poko and KtorGen in compile time.
Please include one of the following types in your issue title or description:
- Serialization error β e.g. the JSON response missing required field, non-nullable property, etc.
- Missing field in response β field present in official API but absent in models (or extra nonβofficial data)
- Others
Also state if the issue refers to an official Deezer change or unofficial additional field.
We welcome contributions if:
- Code is written in Kotlin
- Uses the official Deezer API (no scraping or unofficial endpoints)
- Includes at least one test with a sample JSON response
Please fork, open PRs, and ensure tests pass.
Licensed under AGPL-3.0 β contributions must respect Deezer API Terms & Conditions, including attribution and rateβlimit policies.