Take Control Of Your APIs With GraphQL

Take Control Of Your APIs With GraphQL

Introducing GraphQL APIs and the benefits of using them, and how to interface with these APIs on Android.

Bc87ea9c7a0f85b8761b716a677c6694?s=128

Adam McNeilly

April 21, 2020
Tweet

Transcript

  1. Take Control Of Your APIs With GraphQL Adam McNeilly -

    @AdamMc331 @AdamMc331 #AndroidMakers 1
  2. What Is GraphQL? @AdamMc331 #AndroidMakers 2

  3. What Is Why Use GraphQL? @AdamMc331 #AndroidMakers 3

  4. Let's Examine An Existing API @AdamMc331 #AndroidMakers 4

  5. PokeAPI1 // https://pokeapi.co/api/v2/pokemon/ { "count": 964, "next": "https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20", "previous": null,

    "results": [ { "name": "bulbasaur", "url": "https://pokeapi.co/api/v2/pokemon/1/" }, { "name": "ivysaur", "url": "https://pokeapi.co/api/v2/pokemon/2/" }, { "name": "venusaur", "url": "https://pokeapi.co/api/v2/pokemon/3/" } ] } 1 https://pokeapi.co/ @AdamMc331 #AndroidMakers 5
  6. PokeAPI1 // https://pokeapi.co/api/v2/pokemon/squirtle // Originally 11,000 lines { "abilities": [],

    "base_experience": 63, "forms": [], "game_indices": [], "height": 5, "held_items": [], "id": 7, "is_default": true, "location_area_encounters": "https://pokeapi.co/api/v2/pokemon/7/encounters", "moves": [], "name": "squirtle", "order": 10, "species": {}, "sprites": {}, "stats": [], "types": [], "weight": 90 } 1 https://pokeapi.co/ @AdamMc331 #AndroidMakers 6
  7. What Are The Drawbacks Of REST APIs? @AdamMc331 #AndroidMakers 7

  8. What Are The Drawbacks Of REST APIs? • Responses may

    not have enough data @AdamMc331 #AndroidMakers 7
  9. What Are The Drawbacks Of REST APIs? • Responses may

    not have enough data • Responses may have too much data @AdamMc331 #AndroidMakers 7
  10. What Are The Drawbacks Of REST APIs? • Responses may

    not have enough data • Responses may have too much data • Responses are not controlled by the client @AdamMc331 #AndroidMakers 7
  11. What Are The Drawbacks Of REST APIs? • Responses may

    not have enough data • Responses may have too much data • Responses are not controlled by the client • Any changes to responses have to go through another developer @AdamMc331 #AndroidMakers 7
  12. Let's Examine A GraphQL API 2 2 https://github.com/lucasbento/graphql-pokemon @AdamMc331 #AndroidMakers

    8
  13. GraphQL Query Syntax { pokemon(name: "bulbasaur") { // Put All

    Fields Here } } @AdamMc331 #AndroidMakers 9
  14. Only Get What You Ask For // Query { pokemon(name:

    "bulbasaur") { name } } // Response { "data": { "pokemon": { "name": "Bulbasaur" } } } @AdamMc331 #AndroidMakers 10
  15. Another Example // Query { pokemon(name: "bulbasaur") { name number

    } } // Response { "data": { "pokemon": { "name": "Bulbasaur", "number": "001" } } } @AdamMc331 #AndroidMakers 11
  16. What Are The Benefits Of GraphQL APIs? @AdamMc331 #AndroidMakers 12

  17. What Are The Benefits Of GraphQL APIs? • Responses have

    just enough data @AdamMc331 #AndroidMakers 12
  18. What Are The Benefits Of GraphQL APIs? • Responses have

    just enough data • Responses are controlled by the client @AdamMc331 #AndroidMakers 12
  19. What Are The Benefits Of GraphQL APIs? • Responses have

    just enough data • Responses are controlled by the client • Any changes to responses can be done by the client @AdamMc331 #AndroidMakers 12
  20. Understanding GraphQL @AdamMc331 #AndroidMakers 13

  21. GraphiQL Interface @AdamMc331 #AndroidMakers 14

  22. Documentation Explorer @AdamMc331 #AndroidMakers 15

  23. Documentation Explorer • ! Indicates non-nullable @AdamMc331 #AndroidMakers 15

  24. Documentation Explorer • ! Indicates non-nullable • Brackets are array

    syntax @AdamMc331 #AndroidMakers 15
  25. Nested Objects @AdamMc331 #AndroidMakers 16

  26. GraphQL On Android @AdamMc331 #AndroidMakers 17

  27. Apollo GraphQL3 3 https://github.com/apollographql/apollo-android @AdamMc331 #AndroidMakers 18

  28. Adding Dependencies @AdamMc331 #AndroidMakers 19

  29. Create A GraphQL Directory @AdamMc331 #AndroidMakers 20

  30. Defining Your Schema @AdamMc331 #AndroidMakers 21

  31. Introspection Query 4 4 https://graphqlmastery.com/blog/graphql-introspection-and-introspection-queries @AdamMc331 #AndroidMakers 22

  32. Introspection Query 4 • A query that asks for meta

    data about a GraphQL Schema 4 https://graphqlmastery.com/blog/graphql-introspection-and-introspection-queries @AdamMc331 #AndroidMakers 22
  33. Introspection Query 4 • A query that asks for meta

    data about a GraphQL Schema • Will return all of the possible queries and mutations 4 https://graphqlmastery.com/blog/graphql-introspection-and-introspection-queries @AdamMc331 #AndroidMakers 22
  34. Introspection Query 4 • A query that asks for meta

    data about a GraphQL Schema • Will return all of the possible queries and mutations • Will return all of the models in a graph 4 https://graphqlmastery.com/blog/graphql-introspection-and-introspection-queries @AdamMc331 #AndroidMakers 22
  35. Introspection Query 4 • A query that asks for meta

    data about a GraphQL Schema • Will return all of the possible queries and mutations • Will return all of the models in a graph • Foot note has a deep dive into these queries 4 https://graphqlmastery.com/blog/graphql-introspection-and-introspection-queries @AdamMc331 #AndroidMakers 22
  36. Run Introspection Query @AdamMc331 #AndroidMakers 23

  37. Copy Result @AdamMc331 #AndroidMakers 24

  38. Paste Into schema.json @AdamMc331 #AndroidMakers 25

  39. Writing Your Queries @AdamMc331 #AndroidMakers 26

  40. Create A [something].graphql File // pokemonqueries.graphql // same directory as

    our schema.json file query PokemonQuery($first: Int!) { pokemonList: pokemons(first: $first) { name types image } } @AdamMc331 #AndroidMakers 27
  41. Build Our Project @AdamMc331 #AndroidMakers 28

  42. Generated Java Classes For Our Queries @AdamMc331 #AndroidMakers 29

  43. Write A Query From Our Code val query = PokemonQuery.builder()

    .first(DEFAULT_LIMIT) .build() val callback = object : ApolloCall.Callback<PokemonQuery.Data>() { override fun onFailure(e: ApolloException) { Log.e("ApolloService", e.message, e) } override fun onResponse(response: Response<PokemonQuery.Data>) { val pokemonList = response.data() ?.pokemonList() ?.map { apolloPokemon -> val name = apolloPokemon.name() val typeNames = apolloPokemon.types() val image = apolloPokemon.image() } } } apolloClient.query(query).enqueue(callback) @AdamMc331 #AndroidMakers 30
  44. Write A Query From Our Code val query = PokemonQuery.builder()

    .first(DEFAULT_LIMIT) .build() val callback = object : ApolloCall.Callback<PokemonQuery.Data>() { override fun onFailure(e: ApolloException) { Log.e("ApolloService", e.message, e) } override fun onResponse(response: Response<PokemonQuery.Data>) { val pokemonList = response.data() ?.pokemonList() ?.map { apolloPokemon -> val name = apolloPokemon.name() val typeNames = apolloPokemon.types() val image = apolloPokemon.image() } } } apolloClient.query(query).enqueue(callback) @AdamMc331 #AndroidMakers 30
  45. Write A Query From Our Code val query = PokemonQuery.builder()

    .first(DEFAULT_LIMIT) .build() val callback = object : ApolloCall.Callback<PokemonQuery.Data>() { override fun onFailure(e: ApolloException) { Log.e("ApolloService", e.message, e) } override fun onResponse(response: Response<PokemonQuery.Data>) { val pokemonList = response.data() ?.pokemonList() ?.map { apolloPokemon -> val name = apolloPokemon.name() val typeNames = apolloPokemon.types() val image = apolloPokemon.image() } } } apolloClient.query(query).enqueue(callback) @AdamMc331 #AndroidMakers 30
  46. Write A Query From Our Code val query = PokemonQuery.builder()

    .first(DEFAULT_LIMIT) .build() val callback = object : ApolloCall.Callback<PokemonQuery.Data>() { override fun onFailure(e: ApolloException) { Log.e("ApolloService", e.message, e) } override fun onResponse(response: Response<PokemonQuery.Data>) { val pokemonList = response.data() ?.pokemonList() ?.map { apolloPokemon -> val name = apolloPokemon.name() val typeNames = apolloPokemon.types() val image = apolloPokemon.image() } } } apolloClient.query(query).enqueue(callback) @AdamMc331 #AndroidMakers 30
  47. Write A Query From Our Code val query = PokemonQuery.builder()

    .first(DEFAULT_LIMIT) .build() val callback = object : ApolloCall.Callback<PokemonQuery.Data>() { override fun onFailure(e: ApolloException) { Log.e("ApolloService", e.message, e) } override fun onResponse(response: Response<PokemonQuery.Data>) { val pokemonList = response.data() ?.pokemonList() ?.map { apolloPokemon -> val name = apolloPokemon.name() val typeNames = apolloPokemon.types() val image = apolloPokemon.image() } } } apolloClient.query(query).enqueue(callback) @AdamMc331 #AndroidMakers 30
  48. Understanding The Generated Code @AdamMc331 #AndroidMakers 31

  49. Understanding The Generated Code @AdamMc331 #AndroidMakers 32

  50. Understanding The Generated Code @AdamMc331 #AndroidMakers 33

  51. Understanding The Generated Code @AdamMc331 #AndroidMakers 34

  52. Creating An Apollo Client val okHttpClient = OkHttpClient.Builder() .build() val

    apolloClient = ApolloClient.builder() .serverUrl("https://graphql-pokemon.now.sh/") .okHttpClient(okHttpClient) .build() @AdamMc331 #AndroidMakers 35
  53. You Wrote Your First GraphQL Query @AdamMc331 #AndroidMakers 36

  54. Going Further With Apollo @AdamMc331 #AndroidMakers 37

  55. Use Kotlin Models // build.gradle or build.gradle.kts apollo { generateKotlinModels.set(true)

    // or false for Java models } @AdamMc331 #AndroidMakers 38
  56. Query Code Is More Modern // repository.kt // No builder

    pattern val query = PokemonQuery(first = DEFAULT_LIMIT) // Use fields not methods val name = apolloPokemon.name val typeNames = apolloPokemon.types val image = apolloPokemon.image @AdamMc331 #AndroidMakers 39
  57. GraphQL Fragments @AdamMc331 #AndroidMakers 40

  58. Queries With Duplicated Code query PokemonQuery($first: Int!) { pokemonList: pokemons(first:

    $first) { name types image } } query PokemonDetailQuery($pokemonName: String) { pokemon: pokemon(name: $pokemonName) { name types image } } @AdamMc331 #AndroidMakers 41
  59. Share Information With A Fragment fragment ApolloPokemon on Pokemon {

    name types image } query PokemonQuery($first: Int!) { pokemonList: pokemons(first: $first) { ...ApolloPokemon } } query PokemonDetailQuery($pokemonName: String) { pokemon: pokemon(name: $pokemonName) { ...ApolloPokemon } } @AdamMc331 #AndroidMakers 42
  60. Migrating To GraphQL @AdamMc331 #AndroidMakers 43

  61. A Common Problem /** * This class has a direct

    dependency on a REST API. * It returns a data class we defined. * Not the data class generated by Apollo. * This makes swapping them difficult. */ class PokemonListViewModel( private val restApi: PokemonAPI ) : ViewModel() { @AdamMc331 #AndroidMakers 44
  62. Leverage Repository Interfaces interface PokemonRepository { fun getPokemonList(): PokemonListResponse? fun

    getPokemonDetail(pokemonName: String): Pokemon? } class PokemonListViewModel( private val repository: PokemonRepository ) : ViewModel() { @AdamMc331 #AndroidMakers 45
  63. Create Multiple Implementations class RetrofitService(): PokemonRepository { } class ApolloService():

    PokemonRepository { } @AdamMc331 #AndroidMakers 46
  64. Allows You To A/B Test Your New API private fun

    getRepository(): PokemonRepository { if (isInGraphQLTestGroup) { return ApolloService() } else { return RetrofitService() } } @AdamMc331 #AndroidMakers 47
  65. Why A/B Test A New API? @AdamMc331 #AndroidMakers 48

  66. Why A/B Test A New API? • You can monitor

    performance of a page using different APIs @AdamMc331 #AndroidMakers 48
  67. Why A/B Test A New API? • You can monitor

    performance of a page using different APIs • This is a big change, you should ship with confidence @AdamMc331 #AndroidMakers 48
  68. Why A/B Test A New API? • You can monitor

    performance of a page using different APIs • This is a big change, you should ship with confidence • You won't need to do this for every page, but is helpful for your first page to use GraphQL @AdamMc331 #AndroidMakers 48
  69. Dealing With Generated Code @AdamMc331 #AndroidMakers 49

  70. Our Repository Doesn't Return Generated Code interface PokemonRepository { fun

    getPokemonList(): PokemonListResponse? fun getPokemonDetail(pokemonName: String): Pokemon? } @AdamMc331 #AndroidMakers 50
  71. Leverage Kotlin Extension Functions // pokemonqueries.graphql fragment ApolloPokemon on Pokemon

    { name types image } // ApolloService.kt private fun ApolloPokemon.toPokemon(): Pokemon { return Pokemon( name = this.name.orEmpty(), firstType = this.types?.getOrNull(0), secondType = this.types?.getOrNull(1), frontSpriteUrl = this.image ) } @AdamMc331 #AndroidMakers 51
  72. Makes The Parsing Of Requests Cleaner val callback = object

    : ApolloCall.Callback<PokemonQuery.Data>() { override fun onFailure(e: ApolloException) { Log.e("ApolloService", e.message, e) } override fun onResponse(response: Response<PokemonQuery.Data>) { val pokemonList = response.data() ?.pokemonList ?.filterNotNull() ?.map { apolloPokemon -> val pokemon = apolloPokemon.fragments.apolloPokemon.toPokemon() } } } @AdamMc331 #AndroidMakers 52
  73. Consuming Apollo API Requests @AdamMc331 #AndroidMakers 53

  74. Callbacks val callback = object : ApolloCall.Callback<PokemonQuery.Data>() { override fun

    onFailure(e: ApolloException) { // ... } override fun onResponse(response: Response<PokemonQuery.Data>) { // ... } } @AdamMc331 #AndroidMakers 54
  75. RxJava25 val query = PokemonQuery(first = DEFAULT_LIMIT) apolloClient.rxQuery(query) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())

    .subscribe { // ... } 5 https://www.apollographql.com/docs/android/advanced/rxjava2/ @AdamMc331 #AndroidMakers 55
  76. Coroutines6 override suspend fun getPokemon(): PokemonResponse { val query =

    PokemonQuery(first = DEFAULT_LIMIT) val response = apolloClient.query(query).toDeferred().await() } 6 https://www.apollographql.com/docs/android/advanced/coroutines/ @AdamMc331 #AndroidMakers 56
  77. One Last Suggestion @AdamMc331 #AndroidMakers 57

  78. JS GraphQL Plugin7 7 https://jimkyndemeyer.github.io/js-graphql-intellij-plugin/ @AdamMc331 #AndroidMakers 58

  79. Thank You! @AdamMc331 #AndroidMakers 59