Join us for GraphQL Summit, October 10-12 in San Diego. Use promo code ODYSSEY for $400 off your pass.
Launch GraphOS Studio
You're viewing documentation for an upcoming version of this software. Switch to the latest stable version.

Custom scalar types in Apollo Kotlin

In addition to its built-in scalar types (Int, String, etc.), GraphQL supports defining custom scalars. For example, your might define a custom for Long, Date, BigDecimal, or GeoPoint.

Define class mapping

To interact with custom s in your Apollo Kotlin app, you need to define a mapping in your build.gradle[.kts] file. This tells Apollo Kotlin which class to use to represent each custom from your .

apollo {
service("service") {
mapScalar("GeoPoint", "com.example.GeoPoint")
// Shortcuts exist for standard types - equivalent to mapScalar("Long", "kotlin.Long")

If needed, you can also do this with a built-in (such as ID) to override its default type.

Define class adapters

Each class you use to represent a custom also requires an adapter to convert it to and from the JSON format that's sent over the network.

Each adapter requires a fromJson function. A toJson function is also required if your app ever passes the custom as a GraphQL .

Here's an adapter for a GeoPoint custom :

class GeoPoint(val latitude: Double, val longitude: Double)
val geoPointAdapter = object : Adapter<GeoPoint> {
override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): GeoPoint {
var latitude: Double? = null
var longitude: Double? = null
while(reader.hasNext()) {
when (reader.nextName()) {
"latitude" -> latitude = reader.nextDouble()
"longitude" -> longitude = reader.nextDouble()
// fromJson can throw on unexpected data and the exception will be wrapped in a
// ApolloParseException
return GeoPoint(latitude!!, longitude!!)
// If you do not expect your scalar to be used as input, you can leave this method as TODO()
override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: GeoPoint) {

If you prefer working with Maps, Apollo Kotlin comes with AnyAdapter, which supports adapting String, Int, Double, Boolean, List, and Map. You can use it in an intermediate step:

val geoPointAdapter = object : Adapter<GeoPoint> {
override fun fromJson(reader: JsonReader, customScalarAdapters: CustomScalarAdapters): GeoPoint {
val map = AnyAdapter.fromJson(reader) as Map<String, Double>
return GeoPoint(map["latitude"] as Double, map["longitude"] as Double)
override fun toJson(writer: JsonWriter, customScalarAdapters: CustomScalarAdapters, value: GeoPoint) {
val map = mapOf(
"latitude" to value.latitude,
"longitude" to value.longitude
AnyAdapter.toJson(writer, map)

This solution is more concise but slightly less performant.

Register adapters

After you define your adapters, you need to register them. This can be done either in the build.gradle[.kts] file, or at runtime.

In build.gradle[.kts]

A third can be passed to mapScalar to specify the adapter to use:

apollo {
service("service") {
mapScalar("GeoPoint", "com.example.GeoPoint", "com.example.Adapters.geoPointAdapter")

The given expression is copied as-is in the generated code. Therefore, it's possible to pass any of the following:

  • An instantiation expression, like "com.example.GeoPointAdapter()"
  • A singleton reference, like "com.example.GeoPointAdapter"
  • A function call, like "com.example.Adapters.getGeoPointAdapter()"

Make sure you pass the full class name including the package, because imports aren't automatically generated.

At runtime

You can also register adapters on your ApolloClient instance by calling ApolloClient.Builder.addCustomScalarAdapter once for each adapter:

val apolloClient = ApolloClient.Builder().serverUrl("https://")
.addCustomScalarAdapter(GeoPoint.type, geoPointAdapter)

This method takes a type-safe generated class from Types, along with its corresponding adapter.

If you can't find Types, build your project to trigger codegen.

Apollo-provided adapters

The following built-in adapters can be used with common custom types:

com.apollographql.apollo3.api.FloatAdapterConverts from/to kotlin.Float/java.lang.Float
com.apollographql.apollo3.api.LongAdapterConverts from/to kotlin.Long/java.lang.Long

In addition, the com.apollographql.apollo3:apollo-adapters artifact provides these adapters:

com.apollographql.apollo3.adapter.KotlinxInstantAdapterFor kotlinx.datetime.Instant ISO8601 dates
com.apollographql.apollo3.adapter.JavaInstantAdapterFor java.time.Instant ISO8601 dates
com.apollographql.apollo3.adapter.KotlinxLocalDateAdapterFor kotlinx.datetime.LocalDate ISO8601 dates
com.apollographql.apollo3.adapter.JavaLocalDateAdapterFor java.time.LocalDate ISO8601 dates
com.apollographql.apollo3.adapter.KotlinxLocalDateTimeAdapterFor kotlinx.datetime.LocalDateTime ISO8601 dates
com.apollographql.apollo3.adapter.JavaLocalDateTimeAdapterFor java.time.LocalDateTime ISO8601 dates
com.apollographql.apollo3.adapter.KotlinxLocalTimeAdapterFor kotlinx.datetime.LocalTime ISO8601 dates
com.apollographql.apollo3.adapter.JavaLocalTimeAdapterFor java.time.LocalTime ISO8601 dates
com.apollographql.apollo3.adapter.JavaOffsetDateTimeAdapterFor java.time.OffsetDateTime ISO8601 dates
com.apollographql.apollo3.adapter.DateAdapterFor java.util.Date ISO8601 dates
com.apollographql.apollo3.adapter.BigDecimalAdapterFor a Multiplatform com.apollographql.apollo3.adapter.BigDecimal class holding big decimal values

Note: Because some adapters use kotlinx.datetime (which itself uses java.time), you need to enable core library desugaring on Android API levels < 26

For example, to use DateAdapter, configure your Gradle scripts like so:

dependencies {
apollo {
service("service") {
mapScalar("Date", "java.util.Date", "com.apollographql.apollo3.adapter.DateAdapter")
Error handling
Edit on GitHubEditForumsDiscord