📖

Strawberry Shake (.NET) で GraphQL #5 GraphQL union の利用

2024/12/10に公開

Strawberry Shake (.NET) で GraphQL #5 GraphQL union の利用

前回は Shopify の GraphQL の型と .NET の型のマッピングについて解説しました。今回は GraphQL の union 型ではまったことをまとめます。

うまくいかなかったケース

GraphQL はとても高機能で、インターフェイスも宣言して利用できます。レスポンスの際は次のようにインターフェイスの実装の型を指定することになります。

次の例は Shopify のカートの行の商品の Variant (色とかサイズなど) の Title を取得するクエリになります。

query GetCart($id: ID!) {
    cart(id: $id) {
        lines {
            edges {
                node {
                    merchandise {
                        ...on ProductVariant {
                            title
                        }
                    }
                }
            }
        }
    }
}

C# のコードからアクセスすると、.NET の対応する型に Title プロパティが見つかりません。

しかし、デバッガーを利用すると Title プロパティは存在し、値も入っていました。なんでやねん! と思いました。

GraphQL の merchandise プロパティの型は Merchandise となっていますが、次のように宣言されています。

"The merchandise to be purchased at checkout."
union Merchandise = ProductVariant

Merchandise 型は ProductVariant をとるとなっています。union は次の例のように、型の候補を書くもののようです。

"The list of possible resources a `MenuItem` can reference.\n"
union MenuItemResource = Article | Blog | Collection | Metaobject | Page | Product | ShopPolicy

今回の例では Merchandise 型は ProductVariant 型しかありませんが、...on ProductVariant を指定して、ProductVariant 型だったらこうすると記述しています。

Merchandise にはプロパティはなく、.NET 上の対応する型にも同様にプロパティがないということです。

インターフェイスを利用する

GraphQL の union は Strawberry Shake ではインターフェイスで実現しています。

GraphQL の型 Strawberry Shake が生成する型
Merchandise IGetCart_Cart_Lines_Edges_Node_Merchandise
ProductVariant IGetCart_Cart_Lines_Edges_Node_Merchandise_ProductVariant

IGetCart_Cart_Lines_Edges_Node_Merchandise_ProductVariant 型は IGetCart_Cart_Lines_Edges_Node_Merchandise 型を継承していて、Title プロパティを含んでいます。

ですので、次にように IGetCart_Cart_Lines_Edges_Node_Merchandise_ProductVariant 型にキャストすればいいようです。

foreach (var line in result.Data.Cart.Lines.Edges)
{
    var productVariant = (IGetCart_Cart_Lines_Edges_Node_Merchandise_ProductVariant)line.Node.Merchandise;
    Console.WriteLine(productVariant.Title);
}

試していませんが、GraphQL インターフェイスも union と同様に .NET インターフェイスの継承関係として実装されているのではないかと思います。

ちょっとカッコよく

キャストはしないといけないんですけど、キャスト自体は隠したいですよね! というわけで。

Strawberry Shake が生成する型は部分型 (partial class) です。ですので生成された型に対してメソッドを追加することは簡単です。

次のようにします。

// 名前空間は生成された型に合わせる。
namespace SampleApp.GraphQL;

public partial interface IGetCart_Cart_Lines_Edges_Node_Merchandise
{
    public IGetCart_Cart_Lines_Edges_Node_Merchandise_ProductVariant? OnProductVariant()
    {
        return this as IGetCart_Cart_Lines_Edges_Node_Merchandise_ProductVariant;
    }
}

これで、次のように GraphQL の ...on **** に似た形式でコードを書くことができます。

foreach (var line in result.Data.Cart.Lines.Edges)
{
    Console.WriteLine(line.Node.Merchandise.OnProductVariant().Title);
}

最後に

GraphQL が初めてで苦労し、Strawberry Shake のような自動生成のタイプは使い始めはいいけどうまくいかない時には調査が大変です。それで苦労しましたが、分かってしまえばたいした話ではないです。一度うまくいけば、「慣れてない GraphQL じゃなくて RESTful API を何で用意してくれないんだ?」から「GraphQL って呼び出し側で微調整がやりやすくて便利だよね」に変わります。

日本語の情報が全くなく、公式サイトもドキュメントが短めでかつ GraphQL 初心者ということで理解できず... 苦労した結果をまとめました。

Discussion