みなさまこんにちは〜!
メモリアインクのすだです。
本日は、
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 API | GraphQL |
---|---|---|
エンドポイント | リソースごとに異なるエンドポイント(例:/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を使って、サーバーが提供するスキーマ情報(どんなデータが取得できるか、どんなリクエストが可能かの仕様)を取得します。
- 指定したURL(例:
--schema
- 取得したスキーマ情報を指定した場所(例:
src/main/graphql/com/example/graphql/schema.graphqls
)に保存します。 - スキーマ情報は
.graphqls
という拡張子のファイル形式で保存されます。
- 取得したスキーマ情報を指定した場所(例:
GraphQLでは、プロジェクト内のsrc/main/graphql/
以下にスキーマファイルやクエリファイルを置くのが一般的です。必要に応じて自分でディレクトリとファイルを作成してください。
スキーマには以下のような情報が含まれます ▼
- どんなデータを取得できるか(例:ユーザー情報、投稿一覧など)
- リクエストに必要な引数(例:ユーザーIDなど)
- データの型情報(例:
String
、Int
、Boolean
など)
クエリの作成
スキーマを取得したら、クライアント側で実際にリクエストを送るためのクエリを作成します。
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クエリ(例: ユーザー情報を取得するクエリ)を表すクラスです。
- サーバーにGraphQLクエリ(ここでは
execute()
- クエリを実行して、レスポンスを取得します。
response
- サーバーから返ってきたデータを格納するオブジェクトです。この中にユーザー情報やエラー情報が含まれています。
Mutationを利用してデータを更新する
Mutationは、GraphQLにおける操作の1つで、データの作成・更新・削除をサーバーにリクエストする際に使用します。
REST APIでいうところのPOST
やPUT
、DELETE
リクエストに相当します。
たとえば、データベースに新しいユーザーを追加する操作を行いたい場合、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)
name
とemail
は、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エンジニアを目指すなら【ユニゾンキャリア】
自分の市場価値をさらに向上させてみませんか?
コメント