👋

Graphqlまとめ

2024/02/11に公開

概要

個人開発でGraphqlを採用したので、これを機にまとめてみました。

型のサポート

Graphqlにはスカラー型オブジェクト型ルート型が存在します。(他にもinterfaceやEnumもサポートされています)

スカラー型

プリミティブな型だと、スカラー型と呼ばれる下記が利用できます。

  • String(文字列型)
  • Int(整数型)
  • Float(浮動小数点型)
  • Boolean(論理型)
  • ID(ID型)

オブジェクト型

Query, Mutationの基盤となる型です。
必須フィールドには!(エクスクラメーションマーク)をつけて表現することができます。


type User {
	id: ID!
	name: String
	email: String
	age: Int
}

また、オブジェクト型のフィールドを指定することも可能です。


type User {
	id: ID!
	name: String
	email: String
	age: Int
	post: Post // type Post
}

ルート型

Query, Mutation, Subscriptionが存在します。
それぞれの役割は下記になります。

  • Query: データ取得
  • Mutation: データ登録・更新・削除
  • Subscription: データの監視

Graphqlスキーマ

APIの仕様に当たるのがGraphqlスキーマです。
先ほどのスカラー型オブジェクト型ルート型を組み合わせて構築していきます。

先ほどのUserを全件取得するQueryを考えてみます。


type Query {
	users: [User]
}

また、Graphqlサーバを立ち上げると定義したschemaを確認することができます。

実際にQueryを実行しながら仕様を確認することもできます。

Graphqlを選択した理由

クエリ言語としての利便性

先ほど挙げた例以外にもGraphQLEnum型, Union型Interface型の定義が可能です。
Interfaceのみ軽く触れるとOOPのようにポリモーフィズムが可能になります。

groupIdを持つAdminUserとして返却したい場合を考えます。

Interface User {
	id: ID!
	name: String!
}

type Admin implements User {
	id: ID!
	name: String!
	groupId: ID!
}

type Query {
  users: [User!]
}

クライアントで生成するクエリではFragmentを利用します。


query getUsers {
  users {
    name
    ... on Admin {
      groupId
    }
  }
}

そのほかにも、GraphQL Code Generateを利用することでクエリから型を自動生成してくれます。

https://the-guild.dev/graphql/codegen/plugins/typescript/typescript

参考までに、筆者が利用しているcodegen.ymlです。
(ReactでApollo Serverを利用している場合、pluginsを指定することでhooksも自動生成してくれます。)

schema: ../server/src/graphql/schema.graphql # Graphql Schema
documents: ./src/graphql/query.graphql # Query
generates:
  src/graphql/types.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo

スキーマドリブン

GraphQLではスキーマがAPI仕様となるので、スキーマさえ定義すればフロントとバックエンドを並行開発することができます。

Restだと、クライアントがAPIに強く依存します。(エンドポイント、jsonレスポンスなど)

一方GraphQLだと、互いにGraphQLに依存することになります。所謂、「依存性の逆転(Dependency Inversion)」と言えると思います。

もちろん、GraphQLを介したからといってクライアント、サーバーサイドが互いに完全に無関心で開発できるわけではありません※1が「ある程度」疎結合な開発が可能になります。

※1 あるいは完璧なスキーマ定義ができれば可能かもしれません。

クエリによる柔軟なデータ取得・更新

Graphqlクエリを用いて、サーバーからデータを取得/更新できます。
そのためクライアントはRestのようにエンドポイント/HTTPメソッドを意識する必要はありません。

その代わりにクライアントはQueryとMutationを用いてCRUDを実現します。

Fluxとの相性

Fluxではアプリケーションの状態をStoreで持ちます。
Storeは(UI層的な意味合いで)グローバルに呼び出される可能性があるため、トップページにアクセスした際に初期化することが多いかと思います。

ページ遷移の多いモバイルアプリケーションは際たる例です。
初回起動が伸びることと引き換えに、その後の通信を最小限に抑えページ遷移をスムーズに行う必要があります。

これらを実現するためにはページごと(エンドポイントごと)に必要な情報をfetchしてくるRestとは相性があまり良いと言えません。(うまくやればできないこともないかもしれませんが、初回起動時にレスポンスを変えたくなった場合などにクライアントとサーバーの両方の改修が必要になります。)

GraphQLだとこれらの問題が一気に解決します。

サーバーはスキーマのみ意識すればよく、クライアントもまたスキーマとクエリのみを意識すれば良いのです。

Discussion