Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL Federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free
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.), supports defining custom scalars. For example, your schema might define a custom for Long, Date, BigDecimal, or GeoPoint.

Define class mapping

To interact with custom in your 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 scalar from your schema.

build.gradle[.kts]
apollo {
service("service") {
mapScalar("GeoPoint", "com.example.GeoPoint")
// Shortcuts exist for standard types - equivalent to mapScalar("Long", "kotlin.Long")
mapScalarToKotlinLong("Long")
}
}

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

Define class adapters

Each class you use to represent a custom scalar 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 scalar as a GraphQL .

Here's an adapter for a GeoPoint custom scalar:

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
reader.beginObject()
while(reader.hasNext()) {
when (reader.nextName()) {
"latitude" -> latitude = reader.nextDouble()
"longitude" -> longitude = reader.nextDouble()
}
}
reader.endObject()
// 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) {
writer.beginObject()
writer.name("latitude").value(value.latitude)
writer.name("longitude").value(value.longitude)
writer.endObject()
}
}

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 argument can be passed to mapScalar to specify the adapter to use:

build.gradle[.kts]
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://example.com/graphql")
.addCustomScalarAdapter(GeoPoint.type, geoPointAdapter)
.build()

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.

Built-in adapters

apollo-api provides the following built-in adapters:

AdapterDescription
com.apollographql.apollo.api.StringAdapterConverts from/to kotlin.String/java.lang.String
com.apollographql.apollo.api.IntAdapterConverts from/to kotlin.Int/java.lang.Int
com.apollographql.apollo.api.BooleanAdapterConverts from/to kotlin.Boolean/java.lang.Boolean
com.apollographql.apollo.api.DoubleAdapterConverts from/to kotlin.Double/java.lang.Double
com.apollographql.apollo.api.FloatAdapterConverts from/to kotlin.Float/java.lang.Float
com.apollographql.apollo.api.LongAdapterConverts from/to kotlin.Long/java.lang.Long
com.apollographql.apollo.api.AnyAdapterConverts from/to kotlin.Any/java.lang.Object

Extra adapters

Extra adapters for dates and big decimals are available at https://github.com/apollographql/apollo-kotlin-adapters

Previous
Error handling
Next
Fragments
Rate articleRateEdit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc., d/b/a Apollo GraphQL.

Privacy Policy

Company