🚀

Strawberry Shake (.NET) で GraphQL #1 こと始め

2024/12/10に公開

Strawberry Shake (.NET) で GraphQL #1 こと始め

GraphQL 超初心者です。

GraphQL は RESTful API と比べると、呼び出し側の自由度が高いです。アプリケーションのとあるところでは Book に対して ISBN と書籍名と著者名だけの一覧が欲しいけど別のところは著者名はいらないけど販売数は欲しいといったことに対応できます。RESTful API ではちょっとやりにくいところです。

そんな GraphQL ですが、レスポンスの形式 (著者名も欲しいなど) をクライアントが指定できる都合上、クライアント側が必要に応じてレスポンスに加えるプロパティを増やしていくことになります。こ.NET は静的型付け言語なので、対応するレスポンスの型 (BookAuthor など) にプロパティを追加しなければならず、ちょっと面倒です。

そこで発見したのがこのあたりを自動生成で解決してくれる Strawberry Shake です。JetBrains や .NET のブログで紹介されていました。

Strawberry Shake の概要

Strawberry Shake は定義ファイルから、リクエストとレスポンス用の型、呼び出し用のクライアントコードを Source Generator を利用して自動生成します。

例えば GraphQL ではクエリを次のように書きます。

query GetSessions {
  sessions(order: { title: ASC }) {
    nodes {
      title
    }
  }
}

この定義ファイルから必要なコードが自動生成され、それを利用して API を呼び出すことができます。

var client = services.GetRequiredService<IGraphQLClient>();

var result = await client.GetSessions.ExecuteAsync();

Strawberry Shake を始める

公式サイトの Get started with Strawberry Shake in a Console application に始め方が書いてあります。

何はなくとも .NET コンソールプロジェクトを作成します。新しく SampleApp ディレクトリを作成してその中で作業します。

$ dotnet new console

GraphQL スキーマを同期するための StrabwerryShake.Tools をプロジェクトにインストールします。

$ dotnet new tool-manifest
$ dotnet tool install StrawberryShake.Tools

ここまでは一般的には Visual Studio ソリューションに対してのセットアップです。面倒なので今回はソリューションは使っていません。これ以降が .NET のプロジェクトに対するセットアップです。

このプロジェクトに StrawberryShake.Server nuget パッケージを組み込みます。なんで Server なんだろう? っていう疑問がありますが、気にしないでいいようです。

$ dotnet add package StrawberryShake.Server

そして GraphQL のスキーマを同期します。ここでは Strawberry Shake の開発元が用意している GraphQL API のエンドポイントを利用します。

$ dotnet graphql init https://workshop.chillicream.com/graphql/

いくつかのファイルが作成されていますが、.graphqlrc.json を次のように修正します。strawberryShake セクションの name を修正し、namespacedependencyInjection を足します。

.graphql.json
// ...

  "extensions": {
    "strawberryShake": { // StrawberryShake 専用の
      "name": "GraphQLClient",
      "url": "https://workshop.chillicream.com/graphql/",
      "namespace": "SampleApp.GraphQL",
      "dependencyInjection": true,
      "records": {

// ...

ここまでが .NET プロジェクトで Strawberry Shake を利用するためのセットアップです。GraphQL 関係のファイルが作成されていますが、のちほど解説します。

次に GetSessions.graphql ファイルを次の内容で作成します。(Strawberry Shake 公式サイトからのコピペです)

GetSessions.graphql
query GetSessions {
  sessions(order: { title: ASC }) {
    nodes {
      title
    }
  }
}

一度ビルドして、Strawberry Shake に必要なソースコードを自動生成させます。うまくいけば Generate... というメッセージが表示されます。

$ dotnet build

// ...

  Generate C# Clients started.
  Generate SampleApp started.
  Generate SampleApp completed in 1164 ms
  Generate C# Clients completed in 1345 ms

// ...

ビルドに成功しました。

// ...

そして呼び出すコードを書きます。

Program.cs
using Microsoft.Extensions.DependencyInjection;
using SampleApp.GraphQL;
using StrawberryShake;

// StrawberryShake の DI 用設定。
var serviceCollection = new ServiceCollection();
serviceCollection
    .AddGraphQLClient()
    .ConfigureHttpClient(client => client
        .BaseAddress = new Uri("https://workshop.chillicream.com/graphql")
    );
var services = serviceCollection.BuildServiceProvider();

// DI から自動生成された GraphQL クライアントを取得。
var client = services.GetRequiredService<IGraphQLClient>();

var result = await client.GetSessions.ExecuteAsync();
result.EnsureNoErrors(); // エラーがないことを保証。

foreach (var session in result.Data.Sessions.Nodes)
{
    Console.WriteLine(session.Title);
}

実行してみます。(Session ってカンファレンスのセッションのことのようです)

$ dotnet run

// ...

"Hey Mycroft": Getting Started with the OSS Home Assistant
(WPF + WinForms) * .NET Core = Modern Desktop
.NET Rocks Live on Software Feature Selection with Christine Yen
3D printed Bionic Hand a little IOT and a Xamarin Mobile App
A Developer's Guide to Advertising
A Piece of Git
A Practical Guide to Dashboarding.
A lap around Azure Devops
A practical guide to deep learning
APIs Exposed!

うまくいきました!

なお、自動生成は毎回行うわけではなく、以前の結果を利用します。修正後の dontet build で必要に応じて再生成されますが、再生成されないこともありようです。うまくいかない場合は obj ディレクトリを削除して再度 dotnet build をしてみてください。

ファイルの解説

Strawberry Shake のセットアップで勝手に作成されるファイルは次の通りです。

  • .graphqlrc.json
  • schema.graphql
  • schema.extension.graphql

.graphql.json

GraphQL configuration に記載がありますので、このファイルは GraphQL 一般の設定ファイルのようです。

Strawberr yShake 用の設定は extensions -> strawberryShake にあります。

項目 説明
name 自動生成されるクライアントの型名。これの先頭に I を付けたインターフェイスと名前の実装クラスが作られます。
namespace 自動生成される型の名前空間。
url 接続先の API エンドポイント。
accessModifier 自動生成される型の公開範囲。おそらく "public""internal"
dependencyInjection .NET 標準 DI を使うかどうか。truefalse

詳しくは公式サイトの Configuration をご覧ください。

なお、私の環境ではこのファイルを修正した後は obj ディレクトリを削除して再度 dontet build をする必要がありました。

schema.graphql

こちらも GraphQL 一般のファイルのようです。今回セッションの一覧を取得しましたが、このファイルの中にどうやって呼び出すかや戻り値は何かが書かれています。

一部抜粋ですが、セッションの一覧だけでなく、個別のセッション情報を取得する sessionById の定義があります。

schema.graphql
type Query {
  // ...

  attendeeById("The attendee identifier." id: ID!): Attendee!
  attendeesById(ids: [ID!]!): [Attendee!]!
  sessions("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: SessionFilterInput order: [SessionSortInput!]): SessionsConnection
  sessionById(id: ID!): Session!

  // ...

このファイルは直接修正することはないと思われます。

schema.extensions.graphql

こちらは Strawberry Shake 用の追加のスキーマ設定ファイルのようです。本格的に Strawberry Shake を使う場合は追加でスキーマに設定をすることになりますが、schema.graphql ではなくこのファイルを修正することになります。

少しだけ GraphQL のお話

自分も触り始めたばかりなで解説は穴だらけなのですが、調べた範囲で一番助かったのが WEB+DB PRESS Vol.125 の GraphQL 特集でした。そのうちちゃんとした書籍を読んでみたいと思いますが、雑誌の記事なので短めでとても助かりました。

今回次のように graphql クエリを書きました。

GetSessions.graphql
query GetSessions {
  sessions(order: { title: ASC }) {
    nodes {
      title
    }
  }
}

一行目の query GetSessions で取得のクエリであること、名前は GetSessions であることが宣言されています。この名前は自由に決定することができ、この名前で Strawberry Shake はコードを生成します。

それに対して二行目以降は API の提供者が決めたスキーマ (schema.graphql) にある通りに書く必要があります。schema.graphql には sessions があります。

type Query {
  sessions("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: SessionFilterInput order: [SessionSortInput!]): SessionsConnection

first という引数を渡せることや、order で順序を制御できること、戻り値の型が SessionsConnection であることなどが分かります。

提供元が用意する API 仕様書やこの schema.graphql を見ながら .graphql クエリを書くことになります。

なお、オペレーションには query のほかに更新を行う mutation や、変更を購読する subscription などもあるようです。

最後に

GraphQL を始めて触るので、なかなかうまくいきませんでした。ただ、一度動いてなんとなく分かるようになると、利点が分かるようになってきました。

自分はどちらかというとバックエンド屋さんなのですが、API を呼び出す側からすると GraphQL の微調整のやりやすさはとてもありがたいんじゃないかと思います。まだ慣れないですけど。

次回は JetBrains Rider 編です。

Discussion