12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AndroidのGraphQLクライアントをapolloで実装

Last updated at Posted at 2018-08-11

はじめに

趣味でAndroidアプリを開発しているのですが、サーバーサイドとのやり取りをGraphQLを用いて行ってみようとしたときに、インターネット上の情報がやや古く、公式のページを見ても幾つか引っかかった箇所があったので、自分の備忘録的に記述します。

サーバーサイド側

普段はRailsのエンジニアをしているので、サーバーサイドはRailsを用いました。サーバー側の実装は興味ない方は読み飛ばしてください。

rubyでGraphQLを使うには、graphql-rubyというgemがあり、Gemfileに追加するだけで使える様になります。

Gemfile
gem 'graphql'

まずは、rails generateコマンドでgraphqlを使うのに必要な諸々のファイルを生成します。

rails g graphql:install

app/graphql ディレクトリが生成されていたり、config/routes.rb にgraphql関連のエンドポイントが追加されます。そして、それを処理する graphql_controller.rb も app/controllers配下に生成されます。

また、GraphQLの開発においては、GraphiQL(Qの前にiがあるので注意!)という実行環境アプリを用いると便利で、javascriptで開発されているのですが、それをRailsEngineとしてラップしたgraphiql-railsというgemもGemfileに追加されているかと思います。config/routes.rbに以下の様なルーティングが追加されていることからも/graphiqlでアクセスできそうです。

config/routes.rb
if Rails.env.development?
  mount GraphiQL::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
end

開発中はこれを用いれば良いのですが、本番環境や他のGraphQLのエンドポイントを叩きたいときに便利なのが、GraphiQLのアプリです。Electronでデスクトップアプリ化されているのが提供されているので、Macの方は

brew cask install graphiql
open /Applications/GraphiQL.app

でインストールできます。何かとあると便利なのでインストールしておくと良いと思います。

rails g graphql:object Movie id:ID! url:String! key:String! title:String! description:String! published_at:String 
rails g graphql:object Category id:ID! name:String! movies:[Movie]

generateしたあとは、ファイルを直接修正することもできます。
app/graphql/types/movie_type.rb
app/graphql/types/category_type.rb
などがオブジェクトを定義しているファイルです。

app/graphql/types/category_type.rb
module Types
  class CategoryType < Types::BaseObject
    field :id, ID, null: false
    field :name, String, null: false
    field :movies, [Types::MovieType], null: true do
      argument :num, Integer, required: false
    end

    def movies(num: 8)
      object.movies.limit(num)
    end
  end
end
app/graphql/types/movie_type.rb
module Types
  class MovieType < Types::BaseObject
    field :id, ID, null: false
    field :url, String, null: false
    field :key, String, null: false
    field :status, String, null: false
    field :title, String, null: false
    field :description, String, null: false
    field :published_at, String, null: false
  end
end
app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject
    field :category, CategoryType, null: true do
      argument :id, ID, required: true
    end

    def category(id:)
      Category.find(id)
    end
  end
end

だいたいこんな感じで定義してあげればとりあえず動くものができます。
開発環境であれば、http://localhost:3000/graphiql にアクセスして簡単なクエリを発行すればレスポンスが返ってきます。
image.png

image.png

簡単にサーバーサイド側の準備を行いました。今回はRailsで作りましたが、お好きなもので実装すれば良いかと思います。

Android側

Androidアプリ側でAPIの呼び出しをするとき、これまでのRESTfulなAPIであれば、Retrofit2などのライブラリを用いることが多いでしょう。GraphQLは情報を取得するときもPOSTですが、当初はRetrofit2でPOSTを実装すれば良いと思っていました。しかし、GraphQLでは、apolloというのを用いるのが多い様です。今回もapolloを用いてGraphQLのClient側の実装を行いました。
導入についてはApolloのチームがandroid向けのドキュメントを整備してくれています(こちら)。ただ、ちょっと情報がわかりにくく、少し苦労しましたので、自分の備忘録も込めて記述したいと思います。
基本的にはこちらのGet startedに沿って説明します。

必要なpluginをbuild.gradleに追加

まず必要なpluginをbuild.gradleに追加します。プロジェクトディレクトリ直下にあるbuild.gradleのdependenciesに以下のclasspathを追加します。
mavenを見るとapollo-gradle-plugingradle-pluginの二つがありますが、apollo-gradle-pluginの方を使用します。

build.gradle
  buildscript {
      dependencies {
          // 省略
+         classpath 'com.apollographql.apollo:apollo-gradle-plugin:0.5.0'
      }
  }

app配下にあるbuild.gradleの方に次の2行を追加します。apply plugin: 'com.apollographql.android'に関してはAndroid Pluginの下に書く必要があるので、apply pluginの一番下に追加すれば良いでしょう。

app/build.gradle
  apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'
+ apply plugin: 'com.apollographql.android'

  // 省略

  dependencies {
    // 省略
+   implementation 'com.apollographql.apollo:apollo-runtime:0.5.0'
  } 

バージョンはいずれも0.5.0にしました。過去の記事をみると0.4.1などを利用しているものもあるのですが、うまく動かない場合があります。基本、最新のものを利用すれば良いでしょう。
build.gradleへの追加が終わったら、Syncして反映してあげてください。

schema.jsonを置く

サーバーのGraphQLエンドポイントのスキーマを定義したファイルをAndroidプロジェクト内に置く必要があります。これを元に、apolloは、開発者が記述したgraphqlのクエリが妥当であるかなどのチェックをし、javaファイルに変換します。

schema.jsonを取得するには、apollo-codegenコマンドを利用します。1
apollo-codegenコマンドが未インストールの方は

yarn global add apollo-codegen

でインストールしてください。yarnが入っていない方はbrew install yarnでインストールできます。apollo-codegenが正しくインストールされているかバージョンを確認してみます。

$ apollo-codegen --version
0.20.2

次にschema.jsonをダウンロードします。

apollo-codegen download-schema https://hogehoge.com/graphql --output schema.json

schema.jsonは私の場合、
app/src/main/graphql/com/hogehoge/schema.json
に置きましたが、.graphqlと同じディレクトリに置く必要があります。2

.graphqlファイルの作成

今回は、以下の様な形で実装しました。

Category.graphql
query Category($id: ID!, $num: Int) {
  category(id: $id) {
    id
    name
    movies(num: $num) {
      id
      title
      publishedAt
    }
  }
}

このファイルを元にpluginがjavaクラスを生成してくれます。このときのクラス名はCategoryQueryとなります。
1行目にquery Category(・・・) {と書いてありますが、そこの名称+Queryという形になります。
かっこの中は引数です。今回の場合、CategoryのID($id)と紐づく動画のとってくる数($num)を変数にしています。
このクエリが間違っているとコンパイル時にエラーになるので、GraphiQLなどで正しいかどうかを確認しておくと良いかもしれません。GraphiQLでは、左下のQUERY VARIABLESで変数をセットできます。
image.png

ここで余談ですが、moviesのところはResponseField.forListというメソッドで実装されるのですが、v0.4.1では未実装で、この様な形はもしかしたら当時はうまくいかなかったかもしれません。v0.4.2からはある様です。

利用する側での実装

okHttpClientとapolloClientを用意するところは、(参考)の通りです。
apolloClient.queryの引数は先ほど作成した.graphqlファイルになります。まだビルドしていない場合は、CategoryQueryクラスが生成されていないのでエラーになるかもしれませんので、.graphqlファイルを追加したタイミングでビルドしてみると良いかもしれません。
.graphqlファイルで定義した引数のところは、メソッドになるようです。

CategoryQuery.builder()
             .id(2) // カテゴリーIDの奇数。$id
             .num(6) // 動画の数の引数。$num
             .build()
    val okHttpClient = OkHttpClient.Builder().build()
    val apolloClient = ApolloClient.builder()
        .serverUrl("https://hogehoge.com/graphql") // サーバのホストのは、ローカルでサーバーを立てている場合、10.0.2.2になるらしいので注意。rails sで起動している場合は、http://10.0.2.2:3000となる。10.0.2.2でうまくいかない場合は、10.0.3.2なども試してみる。
        .okHttpClient(okHttpClient)
        .build()

    // APIで取得したCategoryを格納するListを用意
    // Categoryは簡単なdata classを別で定義。 => data class Category(val name: String)
    val categories = mutableListOf<Category>()
    // APIで取得後、adapterに更新を通知するため。APIコールは別スレッド。
    val handler = Handler()

    apolloClient.query(
        CategoryQuery.builder().id("2").num(6).build()
    ).enqueue(
        // ApolloCall.Callbackを実装した無名クラスを作成。必要なメソッドをoverrideする。
        object : ApolloCall.Callback<CategoryQuery.Data>() {
          override fun onResponse(response: Response<CategoryQuery.Data>) {
            response.data()?.category()?.forEach {
              categories.add(Category(it.name())) // サンプルのため、moviesは無視。

              handler.post {
                // adapterはRecyclerView用のadapter
                adapter.categories = categories
                adapter.notifyDataSetChanged()
              }
            }
          }

          override fun onFailure(e: ApolloException) {
            Log.e("ApolloCall Failure", e.message, e)
          }
        }
    )

APIのところは、RxAndroidなどを用いればもう少しスッキリかけるかと思います。

参考

  1. apollo-cliでもschema:downloadができる様なのですが、Android Studioでコンパイルしたときに、GraphQL schema file should contain a valid GraphQL introspection query resultのエラーがでてうまくいきませんでした。

  2. schema.jsonのpathを明示することも可能な様です。(参考

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?