github apollographql/apollo-kotlin v3.0.0-alpha02

latest releases: v4.0.0-beta.6, v3.8.3, v4.0.0-beta.5...
2 years ago

This version reworks the CacheResolver API to hopefully clarify its usage as well as allow more advanced use cases if needed.

ObjectIdGenerator and CacheResolver APIs

This version splits the CacheResolver API in two distinct parts:

  • ObjectIdGenerator.cacheKeyForObject
    • takes Json data as input and returns a unique id for an object.
    • is used after a network request
    • is used during normalization when writing to the cache
  • CacheResolver.resolveField
    • takes a GraphQL field and operation variables as input and generates data for this field
    • this data can be a CacheKey for objects but it can also be any other data if needed. In that respect, it's closer to a resolver as might be found in apollo-server
    • is used before a network request
    • is used when reading the cache

Previously, both methods were in CacheResolver even if under the hood, the code path were very different. By separating them, it makes it explicit and also makes it possible to only implement one of them.

Note: In general, prefer using @typePolicy and @fieldPolicy that provide a declarative/easier way to manage normalization and resolution.

ObjectIdGenerator is usually the most common one. It gives objects a unique id:

type Product {
  uid: String!
  name: String!
  price: Float!
}
// An ObjectIdGenerator that uses the "uid" property if it exists
object UidObjectIdGenerator : ObjectIdGenerator {
  override fun cacheKeyForObject(obj: Map<String, Any?>, context: ObjectIdGeneratorContext): CacheKey? {
    val typename = obj["__typename"]?.toString()
    val uid = obj["uid"]?.toString()

    return if (typename != null && uid != null) {
      CacheKey.from(typename, listOf(uid))
    } else {
      null
    }
  }
}

CacheResolver allows resolving a specific field from a query:

query GetProduct($uid: String!) {
  product(uid: $uid) {
    uid
    name
    price
  }
}
object UidCacheResolver: CacheResolver {
  override fun resolveField(field: CompiledField, variables: Executable.Variables, parent: Map<String, Any?>, parentId: String): Any? {
    var type = field.type
    if (type is CompiledNotNullType) {
      type = type.ofType
    }
    if (type !is ObjectType) {
      // This only works for concrete types
      return MapCacheResolver.resolveField(field, variables, parent, parentId)
    }

    val uid = field.resolveArgument("uid", variables)?.toString()
    if (uid != null) {
       return CacheKey.from(type.name, listOf(uid))
    }

    // Always fallback to the default resolver
    return MapCacheResolver.resolveField(field, variables, parent, parentId)
  }
}

Bug fixes

  • Be robust to SDL schemas that already contain builtin definitions (#3241)

Don't miss a new apollo-kotlin release

NewReleases is sending notifications on new releases.