【Flutter】【GraphQL】FlutterでGraphQL使ってみた話し①(フォルダ構成編)
はじめに
本記事では、FlutterでGraphQLを利用を検討している,もしくわGraphQLで実装しなければならないがどこから手をつければ良いかわからないと言った方向けの記事になります。
バージョン
- flutter: 3.19.5
- Dart:3.3.3
導入パッケージ
主に利用したパッケージです
graphql_flutter: ^5.1.2
graphql_codegen: ^0.14.0
本題
先にフォルダ構成を展開してから軽〜く解説してきますね
上から順に役割とデモコードを利用して解説をしていきます
パッケージ利用方法
おっと忘れていました
フォルダ構成の前にパッケージの利用方法も共有しておきますね
基本的に記載するコードはgraphqlとなります。記載したgraphqlをコマンドを活用してdartへと変換することができるのがgraphql_codegen
となるのです!
graphqlのコードを書いた際は下記のコードを叩いてください
※ --delete-conflicting-outputs
は生成されるファイルが既存のファイルと競合する場合に、それらの競合するファイルを削除するオプションになるため追加しなくてもOKです
詳しくはパッケージを参照してください
flutter pub run build_runner build --delete-conflicting-outputs
フォルダ構成
私はModels毎に作成しています
lib/
└── graphql/
├── fragments/
│ └── user_fragment/
│ ├── user_fragment.graphql
│ └── user_fragment.graphql.dart //graphql_codegenの自動生成
├── inputs/
├── mutations/
├── queries/
├── types/
├── utils/
└── schema.graphql
fragments
複数のqueryやmutaionで再利用できる一部のフィールドセットを定義するためのものです。
→利用することで定義する回数を大幅に減らすことができました!
※Typeを定義していないとエラーになるので注意
GraphQLの良い点はフロント側が指定した値を抜き出し利用できるという点です。
そのため、機能or画面などでFragmentを作成してあげるのも良いと思います!
fragment UserFragment on User {
id
name
email
region {
...RegionFragment
}
}
Fragment内でTypeを指定する場合こちらのようになります
型の指定はここではしません、型の定義はTypeで行います
inputs
queryやmutationに渡す引数として使用するオブジェクトの型を定義するものです。
input CreateUserInput {
id: String!
bio: String
name: String!
email: String!
iconUrl: String
}
types
schema内でオブジェクトの構造を定義するものです。
こちらで使用している「!」は必須の値という意味合いとなります。
※typeはdartのコードが生成されません
type User {
id: String!
bio: String
name: String!
email: String!
iconUrl: String
age: Int
createAt:DateTime
}
Mutation
サーバー側のデータを作成、更新、削除する操作を定義するものです。
RESTAPIで言うPOST/DELETE/PUT
に当たります。
ここもGraphqlの特徴の一つだと思っています
ここでようやくFragment
が活用されます!
本来はjson形式でレスポンスで返ってくる値を記載するのですが今回の場合はFragmentを活用しデータを取得しています。
input
もこちらで利用されていますね!
mutation createUser($input: CreateUserInput!) {
createUser(input: $input) {
...UserFragment
}
}
idだけなど限定的な値が欲しい場合下記のように記載することで実装可能です
createUser(input: $input) {
id
}
Queries
クライアントがサーバーからデータを取得するためのリクエストを定義するものです。
RESTAPIで言うGETに当たります
filter:取得データの種類を制限
limit:取得データ数の制限
orderBy:取得順のソート
page:ページネーションなどの追加取得機能のため
こちらの実装は取得する値やPJによって変わってくると思いますのでバックエンドエンジニアの方と連携しながら確認した方が効率的に進めることができると思います!
query listUsers//ここは自分がわかりやすい関数名でOK! (
$filter: TableUserFilterInput
$limit: Int
$orderBy: [OrderByUserInput]
$page: Int
) {
listUsers//こちら側はバックエンド側の実装に合わせる必要あり (
filter:$filter
limit:$limit
orderBy:$orderBy
page:$page
) {
items {
...UserFragment
}
}
}
Utils
共通処理やユーティリティ関数を定義するもので、複数のQueryやMutationで利用される共通ロジックを整理・再利用するために使われます
enum UploadTarget {
USER
ADMIN
}
schema.graphql
データの構造や操作を定義するスキーマを記述するファイルです。このファイルには、QueryやMutation、タイプ定義などが含まれ、APIの全体像を示すものです。
※こちらに関数名がバックエンド側と同じものが記載されていなかった場合はエラーとなりますので注意
scalar DateTime
type Query {
# User
getUser(id:String!): User
getMyUser:User
listUsers(
filter: TableBattleFilterInput!,
limit: Int!,
orderBy: [OrderByBattleInput]!,
page: Int!
): UserConnection
}
type Mutation {
# user
createUser(input: CreateUserInput!): User
updateUser(input: UpdateUserInput!, condition: TableUserConditionInput): User
deleteUser(input: DeleteUserInput!, condition: TableUserConditionInput): User
}
ここで注意すべき箇所はscalar
です
こちらの定義方法はpubspec.yaml
と同じ階層にbuild.yaml
を作成し定義していきます
こちらはgraphql_codegen
のパッケージにも記載されているため確認をお勧めします
targets:
$default:
builders:
graphql_codegen:
options:
scalars:
DateTime:
type: DateTime
scopes:
- lib/graphql/**
clients:
- graphql
- graphql_flutter
まとめ
ざっくりと解説してきましたがGraphqlの定義について理解できたでしょうか?
正直なところ、バックエンドの方も同じものを記載するのでコピーで良いです笑
個人的につまづいた箇所としては
typeとtypeが入れ子になっている状態の定義手法です。(Fragmentの箇所)
少しでも異なっていたらエラーでコードが生成されないので注意深くチェックしていきましょう!
Discussion