【正社員】還元率83%【フリーランス】マージン一律5万円で案件をご紹介させていただきます。 詳細はこちら

【Android】<Kotlin>GraphQLをAndroidで活用するためのApolloライブラリ入門!

【Android】<Kotlin>GraphQLをAndroidで活用するためのApolloライブラリ入門!
すだ

みなさまこんにちは〜!
メモリアインクのすだです。

本日は、
KotlinのGraphQLをAndroidで活用するために、
Apolloライブラリを導入して実装する方法を 徹底解説していきます!

この記事を読んでわかること…
・GraphQLとは?REST APIとの違い
・Apolloライブラリとは?
・Apolloライブラリの導入方法
・Apolloを使ってGraphQL通信を実現する方法

目次

環境

  • Kotlin (ver 1.9.0)
  • Android Studio (Giraffe | 2022.3.1 Patch 3)

GraphQLとは?REST APIとの違い

データのやり取りを行うAPIの設計で、最もよく知られているのがREST APIです。
しかし、APIの設計にはさまざまな方法があり、その中でも注目されているのがGraphQLです。

GraphQLは、Facebookが開発したAPIのクエリ言語および実行環境(リクエストを受け取り処理する仕組み)です。この仕組みの特徴は、クライアント側で「必要なデータだけ」を指定してリクエストできる点にあり、
その結果、不要なデータのやり取りが発生せず、通信の効率を大幅に向上させることができます。

REST APIがサーバー側で定義された固定のレスポンスを返すのに対し、
GraphQLはクライアントが必要なデータを自由に指定して取得できる点が大きな違いです。

REST APIとGraphQLは、他にも細かく 以下のような違いがあります。▼

項目REST APIGraphQL
エンドポイントリソースごとに異なるエンドポイント(例:/users, /posts)単一エンドポイント(例:/graphql)
データ取得の柔軟性サーバーが定義したレスポンス構造で固定され、余計なデータが含まれることも多い必要なデータをクエリで指定し、必要最小限のデータを取得できる
リクエスト数複数リソースを取得するには複数回リクエストが必要1回のリクエストで複数リソースを取得可能
型安全性型情報はドキュメントなどを参照しないと分からないスキーマに基づいて型が定義されるため、クライアント側で型チェックが可能
進化性フィールドを追加すると、既存クライアントが想定外のデータを受け取る可能性がある新しいフィールドを追加しても既存クライアントに影響を与えない
キャッシュキャッシュはHTTPプロトコル(例:ETag)に依存クライアントライブラリでキャッシュを簡単に実装可能

あとで詳しくご紹介しますが、イメージとしては以下のような実装の違いがあります。

例)
REST APIの場合▼: ユーザー情報と投稿を取得するには2回のリクエストが必要

GET /users/1
GET /users/1/posts

GraphQLの場合▼: 1回のリクエストで両方のデータを取得可能

query {
  user(id: "1") {
    name
    posts {
      title
    }
  }
}

Apolloライブラリとは?

Apolloは、GraphQLを使用するための人気のあるクライアントライブラリです。
Android用にはApollo Kotlinが提供されており、Kotlin Multiplatformにも対応しています。

以下の特徴があります。▼

  • GraphQLスキーマから型安全なKotlinコードを自動生成
  • キャッシュ機能でオフライン対応
  • Mutationやサブスクリプションのサポート
  • 簡単な設定で導入可能

早速Apolloライブラリを導入してみましょう!

プロジェクトへのApolloライブラリの導入

1. Gradle設定

アプリレベルのbuild.gradleにApolloライブラリを追加します。

dependencies {
    implementation("com.apollographql.apollo3:apollo-runtime:4.0.0")
}

そして、プロジェクトレベルのbuild.gradleに以下を追加してください。

apollo {
    service("service") {
        packageName.set("com.example.graphql") // 自分のパッケージ名に変更
    }
}

2. Apolloプラグインの適用

次に、pluginsブロックに以下を追加します。

plugins {
    id("com.apollographql.apollo3").version("4.0.0")
}

以上でプロジェクトへのApolloライブラリの導入が完了しました。

GraphQLスキーマとクエリの作成

GraphQLスキーマの取得

GraphQLでは、APIの仕様(どんなデータが取得できるのか、どんなリクエストが可能なのか)をスキーマという形式で管理します。
スキーマは、GraphQLサーバーからダウンロードしてプロジェクト内に保存する必要があります。

以下のコマンドを実行すると、サーバーからスキーマを取得して、指定したファイルに保存します。

./gradlew downloadApolloSchema \
    --endpoint='https://your-api-endpoint.com/graphql' \
    --schema='src/main/graphql/com/example/graphql/schema.graphqls'
  • --endpoint
    • 指定したURL(例: https://your-api-endpoint.com/graphql)のGraphQLサーバーにアクセスします。
    • このURLを使って、サーバーが提供するスキーマ情報(どんなデータが取得できるか、どんなリクエストが可能かの仕様)を取得します。
  • --schema
    • 取得したスキーマ情報を指定した場所(例: src/main/graphql/com/example/graphql/schema.graphqls)に保存します。
    • スキーマ情報は.graphqlsという拡張子のファイル形式で保存されます。

GraphQLでは、プロジェクト内のsrc/main/graphql/以下にスキーマファイルやクエリファイルを置くのが一般的です。必要に応じて自分でディレクトリとファイルを作成してください。

スキーマには以下のような情報が含まれます ▼

  • どんなデータを取得できるか(例:ユーザー情報、投稿一覧など)
  • リクエストに必要な引数(例:ユーザーIDなど)
  • データの型情報(例:StringIntBooleanなど)

クエリの作成

スキーマを取得したら、クライアント側で実際にリクエストを送るためのクエリを作成します。

src/main/graphql/com/example/graphqlフォルダに以下のようなクエリファイルを作成します。
(クエリは.graphqlという拡張子のファイルに記述します。)

例:src/main/graphql/com/example/graphql/GetUsers.graphql

query GetUsers {
    users {
        id
        name
        email
    }
}

上記は、ユーザー情報を取得するためのクエリの例です。

  • query GetUsers
    • クエリの名前です。このクエリでは「GetUsers」という名前を付けています。
  • users
    • サーバー側で定義された「ユーザー情報」を取得するフィールドです。
  • id, name, email
    • 必要なデータを指定しています。この場合、ユーザーのID、名前、メールアドレスを取得します。

なぜクエリを作るのか?

クエリは「どのデータを取得するか」を具体的に指示するものです。
GraphQLでは、このクエリをサーバーに送信することで、必要なデータだけを効率的に取得できます。

Apolloを使ったGraphQLクエリの実行

準備が整ったところで、早速クエリの実行(= APIを叩く)をしてみましょう。

以下は、作成したGetUsersクエリを実行するサンプルコードです。

fun fetchUsers() {
    val apolloClient = ApolloClient.Builder()
        .serverUrl("https://your-api-endpoint.com/graphql")
        .build()

    runBlocking {
        val response: ApolloResponse<GetUsersQuery.Data> = apolloClient.query(GetUsersQuery()).execute()
        val users = response.data?.users

        users?.forEach {
            println("ID: ${it.id}, Name: ${it.name}, Email: ${it.email}")
        }
    }
}

3、4行目:

  • ApolloClient.Builder()
    • Apolloクライアントを構築します。
  • serverUrl
    • GraphQLサーバーのエンドポイントURLを指定しています。ここにリクエストを送信します。

8、9行目:

  • apolloClient.query()
    • サーバーにGraphQLクエリ(ここではGetUsersQuery)を送信します。
    • GetUsersQueryは、プロジェクトに用意されたGraphQLクエリ(例: ユーザー情報を取得するクエリ)を表すクラスです。
  • execute()
    • クエリを実行して、レスポンスを取得します。
  • response
    • サーバーから返ってきたデータを格納するオブジェクトです。この中にユーザー情報やエラー情報が含まれています。

Mutationを利用してデータを更新する

Mutationは、GraphQLにおける操作の1つで、データの作成・更新・削除をサーバーにリクエストする際に使用します。
REST APIでいうところのPOSTPUTDELETEリクエストに相当します。

たとえば、データベースに新しいユーザーを追加する操作を行いたい場合、Mutationを使用します。

以下は、ユーザーを追加するMutationクエリの例です。

mutation AddUser($name: String!, $email: String!) {
    addUser(name: $name, email: $email) {
        id
        name
    }
}

1行目:


  • mutation(AddUserMutation(name, email))
    • GraphQL Mutationを実行します。
  • 引数の受け渡し
    • $nameと$emailは実行時に渡されます。
fun addUser(name: String, email: String) {
    val apolloClient = ApolloClient.Builder()
        .serverUrl("https://your-api-endpoint.com/graphql")
        .build()

    runBlocking {
        val response = apolloClient.mutation(AddUserMutation(name, email)).execute()
        println("New User ID: ${response.data?.addUser?.id}")
    }
}

7行目:

  • AddUserMutation(name, email)
    • nameemailは、Mutationに渡される引数です。これらがサーバーに送信され、新しいユーザーが作成されます。
  • apolloClient.mutation()
    • Apolloクライアントを使って、GraphQLのMutationをサーバーに送信するメソッドです。
    • この時点で、サーバーにリクエストが準備されます。
  • .execute():
    • サーバーにリクエストを送信し、その結果をレスポンスとして受け取ります。
    • このレスポンスには、サーバーが返してきたデータ(たとえば新しく追加されたユーザーのIDや名前)や、エラー情報が含まれます。

エラーハンドリングとデバッグ方法

GraphQL通信時のエラーは、以下のように処理すると良いでしょう。▼

val response = apolloClient.query(GetUsersQuery()).execute()
if (response.hasErrors()) {
    response.errors?.forEach {
        println("Error: ${it.message}")
    }
} else {
    println("Query successful!")
}

また、Apolloはデバッグログを有効にすることで、リクエストやレスポンスを確認できます。▼

val apolloClient = ApolloClient.Builder()
    .serverUrl("https://your-api-endpoint.com/graphql")
    .httpLogLevel(HttpLogLevel.BODY)
    .build()

Apolloのキャッシュ機能の活用

Apolloにはキャッシュ機能が内蔵されており、ネットワークの負荷を軽減することができます。

val apolloClient = ApolloClient.Builder()
    .serverUrl("https://your-api-endpoint.com/graphql")
    .normalizedCache(MemoryCacheFactory(maxSizeBytes = 10 * 1024 * 1024)) // 10MBのキャッシュ
    .build()

キャッシュされたデータを使用することで、同じクエリを繰り返し実行してもネットワーク負荷がかかりません。

3行目:

  • normalizedCache
    • Apolloのキャッシュ機能を有効化します。
  • キャッシュの種類:
    • MemoryCacheFactory
      • メモリ内にキャッシュを保存します。一時的なキャッシュで、アプリが終了すると消えます。
      • 例: MemoryCacheFactory(maxSizeBytes = 10 * 1024 * 1024)は10MBのメモリキャッシュを設定。
    • SqlNormalizedCacheFactory
      • データベース(SQLite)にキャッシュを保存し、永続的に保持します。

まとめ

おつかれさまでした。いかがでしたでしょうか!

GraphQLを活用して、効率的で柔軟なデータ取得をアプリに取り入れてみてください!

すだ

技術者としてのキャリアパスを次のレベルへと進めたい皆様、
未経験からIT・Webエンジニアを目指すなら【ユニゾンキャリア】
を通じて、
自分の市場価値をさらに向上させてみませんか?

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

弊社テックブログをご愛読いただきありがとうございます。
当テックブログを運用している株式会社メモリアインクは、
【正社員】還元率83%
【フリーランス】マージン一律5万円で案件のご紹介
と、エンジニアの皆様に分かりやすい形で稼げる仕組みを構築し提供させていただいております。

コメント

コメントする

目次