C#でGraphQLを実装する方法: 初心者向けガイド

GraphQLは、柔軟で効率的なAPIを構築するためのツールです。本記事では、C#でGraphQLを利用してAPIを構築する方法を、初心者向けにステップバイステップで解説します。必要な開発環境の準備から、スキーマの定義、クエリの作成、テスト方法まで、実際のコード例を交えて詳しく説明します。これにより、C#でのGraphQLの基本をしっかりと理解し、実践に役立てることができるでしょう。

目次

GraphQLとは

GraphQLは、Facebookが開発したAPIクエリ言語およびランタイムです。REST APIの代替として、クライアントが必要なデータだけを取得できるように設計されています。これにより、複数のエンドポイントにアクセスする必要がなく、オーバーフェッチやアンダーフェッチの問題を解消します。また、強力な型システムを備えているため、APIの一貫性と予測可能性が向上します。GraphQLを利用することで、API開発がよりシンプルで効率的になります。

開発環境の準備

C#でGraphQLを実装するためには、いくつかの開発ツールとライブラリが必要です。以下は、開発環境をセットアップするための手順です。

Visual Studioのインストール

まず、Microsoftの統合開発環境(IDE)であるVisual Studioをインストールします。最新バージョンのVisual Studio Community Editionは無料で使用できます。

.NET Core SDKのインストール

次に、.NET Core SDKをインストールします。.NET Coreはクロスプラットフォームで動作するフレームワークであり、C#での開発に必要です。

プロジェクトの作成

Visual Studioを開き、新しいASP.NET Core Webアプリケーションプロジェクトを作成します。テンプレートとして「ASP.NET Core Web API」を選択し、プロジェクト名を設定します。

これで、C#でGraphQLを実装するための基本的な開発環境が整います。次に、必要なNuGetパッケージをインストールして、具体的な実装を進めていきます。

必要なパッケージのインストール

C#でGraphQLを利用するためには、いくつかのNuGetパッケージをインストールする必要があります。以下の手順に従って、必要なパッケージをインストールします。

GraphQL.NETのインストール

GraphQLの基本的な機能を提供するために、GraphQL.NETパッケージをインストールします。Visual Studioの「NuGet パッケージマネージャー」を開き、以下のコマンドを使用してインストールします。

Install-Package GraphQL

GraphQL.Server.Transports.AspNetCoreのインストール

ASP.NET CoreでGraphQLサーバーを実装するために必要なパッケージです。以下のコマンドを使用してインストールします。

Install-Package GraphQL.Server.Transports.AspNetCore

GraphQL.Server.Ui.Playgroundのインストール

GraphQL Playgroundを使用して、GraphQL APIを簡単にテストできるようにするためのパッケージです。以下のコマンドを使用してインストールします。

Install-Package GraphQL.Server.Ui.Playground

これらのパッケージをインストールすることで、C#でGraphQLを実装するための基本的なツールが揃います。次は、プロジェクトの初期設定と基本的なGraphQLの設定方法について説明します。

初期設定

C#でGraphQLを実装するためのプロジェクトの初期設定を行います。ここでは、基本的なGraphQLの設定を行う手順を解説します。

Startup.csの編集

プロジェクトのStartup.csファイルを開き、必要なサービスとミドルウェアを設定します。

サービスの登録

まず、ConfigureServicesメソッド内にGraphQLのサービスを登録します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
    services.AddSingleton<ISchema, MySchema>();
    services.AddGraphQL(options =>
    {
        options.EnableMetrics = false;
    }).AddSystemTextJson();
}

ここで、IDocumentExecuterISchemaは、それぞれGraphQLのクエリの実行とスキーマを定義するためのサービスです。

ミドルウェアの設定

次に、Configureメソッド内にGraphQLのミドルウェアを設定します。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });

    app.UseGraphQL<ISchema>("/graphql");
    app.UseGraphQLPlayground(options: new GraphQLPlaygroundOptions());
}

これにより、/graphqlエンドポイントでGraphQLを使用できるようになり、/ui/playgroundエンドポイントでGraphQL Playgroundを利用してAPIをテストできるようになります。

次に、GraphQLスキーマの定義方法について説明します。

スキーマの定義

GraphQLスキーマは、APIがどのようなデータを提供するかを定義する中心的な要素です。ここでは、スキーマの作成方法とその重要性について説明します。

スキーマとは

GraphQLスキーマは、クエリやミューテーションがどのようなデータ型を扱うかを定義します。これにより、クライアントはどのようなクエリが可能かを事前に知ることができます。

クエリタイプの定義

まず、基本的なクエリタイプを定義します。クエリタイプは、クエリで取得できるフィールドを定義するクラスです。

public class Query : ObjectGraphType
{
    public Query()
    {
        Field<StringGraphType>(
            "hello",
            resolve: context => "Hello, GraphQL!"
        );

        Field<UserType>(
            "user",
            arguments: new QueryArguments(new QueryArgument<IdGraphType> { Name = "id" }),
            resolve: context => userRepository.GetUserById(context.GetArgument<int>("id"))
        );
    }
}

この例では、helloフィールドは単純な文字列を返し、userフィールドはユーザー情報を返します。

データタイプの定義

次に、ユーザーデータの型を定義します。UserTypeは、ユーザーオブジェクトのフィールドを定義します。

public class UserType : ObjectGraphType<User>
{
    public UserType()
    {
        Field(x => x.Id).Description("The ID of the user.");
        Field(x => x.Name).Description("The name of the user.");
        Field(x => x.Email).Description("The email of the user.");
    }
}

スキーマの統合

最後に、定義したクエリタイプとデータタイプをスキーマに統合します。

public class MySchema : Schema
{
    public MySchema(IServiceProvider provider) : base(provider)
    {
        Query = provider.GetRequiredService<Query>();
    }
}

これで、基本的なGraphQLスキーマの設定が完了です。次に、具体的なクエリの作成方法について説明します。

クエリの作成

GraphQLクエリは、クライアントがサーバーからデータを取得するためのリクエストです。ここでは、具体的なクエリの書き方と実装例を示します。

クエリの構造

GraphQLクエリは、取得したいデータのフィールドを指定するシンプルな構造です。以下は、ユーザー情報を取得する基本的なクエリの例です。

{
  user(id: 1) {
    id
    name
    email
  }
}

このクエリでは、idが1のユーザーのidnameemailフィールドを取得します。

クエリの実装

クライアントからのクエリに対してサーバーがどのように応答するかを定義します。前述のクエリタイプにユーザー情報を返すフィールドを追加します。

public class Query : ObjectGraphType
{
    public Query(IUserRepository userRepository)
    {
        Field<StringGraphType>(
            "hello",
            resolve: context => "Hello, GraphQL!"
        );

        Field<UserType>(
            "user",
            arguments: new QueryArguments(new QueryArgument<IdGraphType> { Name = "id" }),
            resolve: context => userRepository.GetUserById(context.GetArgument<int>("id"))
        );
    }
}

ここでは、IUserRepositoryを利用して、指定されたIDのユーザー情報を取得しています。

クエリのテスト

GraphQL Playgroundを使用してクエリをテストします。/ui/playgroundエンドポイントにアクセスし、以下のクエリを実行します。

{
  user(id: 1) {
    id
    name
    email
  }
}

このクエリを実行すると、指定したIDのユーザー情報が返されることを確認できます。これにより、クエリが正しく動作していることを確認できます。

これで、基本的なGraphQLクエリの作成と実装方法が理解できました。次は、データの更新や削除を行うためのミューテーションの作成方法について説明します。

ミューテーションの作成

GraphQLミューテーションは、データの作成、更新、削除を行うためのリクエストです。ここでは、ミューテーションの書き方と具体的な実装例を紹介します。

ミューテーションの構造

GraphQLミューテーションは、変更したいデータのフィールドとその値を指定する構造です。以下は、新しいユーザーを作成するミューテーションの例です。

mutation {
  createUser(input: { name: "John Doe", email: "john.doe@example.com" }) {
    id
    name
    email
  }
}

このミューテーションでは、新しいユーザーを作成し、そのidnameemailフィールドを返します。

ミューテーションの実装

サーバー側でミューテーションを実装します。まず、ミューテーションタイプを定義します。

public class Mutation : ObjectGraphType
{
    public Mutation(IUserRepository userRepository)
    {
        Field<UserType>(
            "createUser",
            arguments: new QueryArguments(
                new QueryArgument<NonNullGraphType<UserInputType>> { Name = "input" }
            ),
            resolve: context =>
            {
                var userInput = context.GetArgument<User>("input");
                return userRepository.CreateUser(userInput);
            }
        );
    }
}

次に、UserInputTypeを定義して、ユーザー作成のための入力データ型を指定します。

public class UserInputType : InputObjectGraphType
{
    public UserInputType()
    {
        Name = "UserInput";
        Field<NonNullGraphType<StringGraphType>>("name");
        Field<NonNullGraphType<StringGraphType>>("email");
    }
}

スキーマへのミューテーション追加

定義したミューテーションをスキーマに追加します。

public class MySchema : Schema
{
    public MySchema(IServiceProvider provider) : base(provider)
    {
        Query = provider.GetRequiredService<Query>();
        Mutation = provider.GetRequiredService<Mutation>();
    }
}

ミューテーションのテスト

GraphQL Playgroundを使用してミューテーションをテストします。/ui/playgroundエンドポイントにアクセスし、以下のミューテーションを実行します。

mutation {
  createUser(input: { name: "John Doe", email: "john.doe@example.com" }) {
    id
    name
    email
  }
}

このミューテーションを実行すると、新しいユーザーが作成され、その情報が返されることを確認できます。

これで、基本的なGraphQLミューテーションの作成と実装方法が理解できました。次は、クエリとミューテーションに対するリゾルバーの作成方法について説明します。

リゾルバーの作成

GraphQLリゾルバーは、クエリやミューテーションに対する実際のデータ取得や操作を行うロジックを提供します。ここでは、リゾルバーの作成方法と実装例を説明します。

リゾルバーとは

リゾルバーは、GraphQLの各フィールドに対してどのようにデータを取得するかを定義する関数です。クエリやミューテーションのリクエストが来たときに実行され、指定されたデータを返します。

クエリリゾルバーの作成

クエリリゾルバーは、クエリで要求されたデータを取得するためのロジックを実装します。以下は、ユーザー情報を取得するリゾルバーの例です。

public class UserResolver
{
    private readonly IUserRepository _userRepository;

    public UserResolver(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User GetUserById(int id)
    {
        return _userRepository.GetUserById(id);
    }
}

このリゾルバーは、IUserRepositoryを利用して指定されたIDのユーザー情報を取得します。

ミューテーションリゾルバーの作成

ミューテーションリゾルバーは、ミューテーションで要求されたデータ操作を実行するためのロジックを実装します。以下は、新しいユーザーを作成するリゾルバーの例です。

public class UserMutationResolver
{
    private readonly IUserRepository _userRepository;

    public UserMutationResolver(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User CreateUser(User user)
    {
        return _userRepository.CreateUser(user);
    }
}

このリゾルバーは、新しいユーザーを作成するためのロジックを提供します。

リゾルバーの登録

リゾルバーをGraphQLスキーマに登録します。Startup.csでリゾルバーをサービスとして登録し、クエリやミューテーションで利用します。

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IUserRepository, UserRepository>();
    services.AddSingleton<UserResolver>();
    services.AddSingleton<UserMutationResolver>();
    services.AddSingleton<ISchema, MySchema>();

    services.AddGraphQL(options =>
    {
        options.EnableMetrics = false;
    }).AddSystemTextJson();
}

クエリとミューテーションへのリゾルバーの適用

クエリやミューテーションの定義でリゾルバーを利用します。

public class Query : ObjectGraphType
{
    public Query(UserResolver userResolver)
    {
        Field<UserType>(
            "user",
            arguments: new QueryArguments(new QueryArgument<IdGraphType> { Name = "id" }),
            resolve: context => userResolver.GetUserById(context.GetArgument<int>("id"))
        );
    }
}

public class Mutation : ObjectGraphType
{
    public Mutation(UserMutationResolver userMutationResolver)
    {
        Field<UserType>(
            "createUser",
            arguments: new QueryArguments(
                new QueryArgument<NonNullGraphType<UserInputType>> { Name = "input" }
            ),
            resolve: context => userMutationResolver.CreateUser(context.GetArgument<User>("input"))
        );
    }
}

これで、クエリとミューテーションに対するリゾルバーの作成と適用が完了です。次は、GraphQLを利用したフロントエンドとの連携方法について解説します。

応用例: フロントエンドとの連携

GraphQLを利用することで、フロントエンドとバックエンドのデータ連携が効率的になります。ここでは、具体的なフロントエンドとの連携方法を解説します。

Reactアプリケーションのセットアップ

まず、Reactを使用してフロントエンドアプリケーションをセットアップします。以下のコマンドで新しいReactアプリケーションを作成します。

npx create-react-app my-graphql-app
cd my-graphql-app

Apollo Clientのインストール

Apollo Clientは、GraphQLクエリを簡単に扱えるライブラリです。以下のコマンドでApollo Clientをインストールします。

npm install @apollo/client graphql

Apollo Clientの設定

src/index.jsファイルを開き、Apollo Clientを設定します。

import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider, InMemoryCache, ApolloClient } from '@apollo/client';
import App from './App';

const client = new ApolloClient({
  uri: 'http://localhost:5000/graphql', // GraphQLサーバーのエンドポイント
  cache: new InMemoryCache()
});

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('root')
);

ユーザーデータのクエリ作成

次に、ユーザーデータを取得するためのGraphQLクエリを作成します。src/queries.jsファイルを作成し、以下の内容を追加します。

import { gql } from '@apollo/client';

export const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`;

クエリの実行とデータ表示

Reactコンポーネント内でクエリを実行し、データを表示します。src/App.jsファイルを開き、以下の内容を追加します。

import React, { useState } from 'react';
import { useQuery } from '@apollo/client';
import { GET_USER } from './queries';

function App() {
  const [userId, setUserId] = useState(1);
  const { loading, error, data } = useQuery(GET_USER, {
    variables: { id: userId }
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>User Information</h1>
      <p>ID: {data.user.id}</p>
      <p>Name: {data.user.name}</p>
      <p>Email: {data.user.email}</p>
      <button onClick={() => setUserId(userId + 1)}>Next User</button>
    </div>
  );
}

export default App;

このようにして、フロントエンドのReactアプリケーションとバックエンドのGraphQL APIが連携し、動的にデータを取得して表示できるようになります。

次に、GraphQL APIのテストとデバッグの方法について説明します。

テストとデバッグ

GraphQL APIの開発において、テストとデバッグは非常に重要です。ここでは、GraphQL APIをテストおよびデバッグするための方法を説明します。

Postmanを使用したテスト

Postmanは、APIをテストするための強力なツールです。以下の手順でGraphQL APIをテストします。

  1. Postmanをインストールし、起動します。
  2. 新しいリクエストを作成し、HTTPメソッドをPOSTに設定します。
  3. リクエストURLにGraphQLエンドポイント(例: http://localhost:5000/graphql)を入力します。
  4. 「Body」タブを選択し、「GraphQL」を選びます。
  5. クエリやミューテーションを入力し、「Send」ボタンをクリックします。

例として、ユーザー情報を取得するクエリを実行します。

{
  user(id: 1) {
    id
    name
    email
  }
}

ユニットテストの実装

ユニットテストを実装することで、APIの動作を自動的に検証できます。ここでは、xUnitとGraphQL.NETを使用したユニットテストの例を紹介します。

using Xunit;
using GraphQL;
using GraphQL.Types;
using MyGraphQLApp;

public class UserQueryTests
{
    [Fact]
    public async Task GetUserById_ShouldReturnUser()
    {
        // Arrange
        var schema = new Schema { Query = new Query(new UserResolver(new UserRepository())) };
        var json = await schema.ExecuteAsync(_ =>
        {
            _.Query = "{ user(id: 1) { id name email } }";
        });

        // Assert
        Assert.Contains("user", json);
        Assert.Contains("id", json);
        Assert.Contains("name", json);
        Assert.Contains("email", json);
    }
}

このテストでは、ユーザー情報を取得するクエリが正しく実行されることを確認します。

デバッグの方法

Visual Studioを使用して、GraphQL APIをデバッグすることができます。以下の手順でデバッグを行います。

  1. ブレークポイントを設定したい箇所(例: リゾルバーやミューテーション)にブレークポイントを設定します。
  2. Visual Studioの「デバッグ」メニューから「デバッグの開始」を選択します。
  3. GraphQL PlaygroundやPostmanを使用して、デバッグ対象のクエリやミューテーションを実行します。
  4. ブレークポイントに達すると、Visual Studioが実行を停止し、コードをステップ実行しながら変数の値や実行フローを確認できます。

これにより、GraphQL APIの動作を詳細に確認し、不具合を特定して修正することができます。

次に、C#でGraphQLを実装する際のベストプラクティスと注意点について説明します。

ベストプラクティス

C#でGraphQLを実装する際に考慮すべきベストプラクティスと注意点を紹介します。これにより、効率的で保守性の高いコードを書くことができます。

スキーマの設計

スキーマの設計は、GraphQL APIの基盤です。以下の点に注意してスキーマを設計します。

  • 明確な型定義: 型を明確に定義し、クライアントが期待するデータ構造を理解しやすくします。
  • 適切なフィールド名: フィールド名はわかりやすく、一貫性を保ちます。
  • ドキュメントの追加: 各フィールドや型に説明文を追加して、スキーマを自己説明的にします。

リゾルバーの分離

リゾルバーはビジネスロジックを実装する部分です。リゾルバーを独立したクラスとして分離し、クエリやミューテーションから切り離すことで、コードの再利用性とテストのしやすさが向上します。

public class UserResolver
{
    private readonly IUserRepository _userRepository;

    public UserResolver(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User GetUserById(int id)
    {
        return _userRepository.GetUserById(id);
    }
}

エラーハンドリング

エラーハンドリングは、APIの信頼性を高めるために重要です。リゾルバー内で例外を適切にキャッチし、クライアントにわかりやすいエラーメッセージを返します。

public class UserResolver
{
    public User GetUserById(int id)
    {
        try
        {
            return _userRepository.GetUserById(id);
        }
        catch (Exception ex)
        {
            throw new ExecutionError("Failed to fetch user", ex);
        }
    }
}

バッチ処理の利用

複数のデータ取得リクエストを一度に処理するバッチ処理を利用することで、パフォーマンスを向上させます。DataLoaderライブラリを使用してバッチ処理を実装できます。

public class UserResolver
{
    private readonly IUserRepository _userRepository;
    private readonly IDataLoaderContextAccessor _dataLoaderContextAccessor;

    public UserResolver(IUserRepository userRepository, IDataLoaderContextAccessor dataLoaderContextAccessor)
    {
        _userRepository = userRepository;
        _dataLoaderContextAccessor = dataLoaderContextAccessor;
    }

    public async Task<User> GetUserById(int id)
    {
        var loader = _dataLoaderContextAccessor.Context.GetOrAddBatchLoader<int, User>("GetUsersById", _userRepository.GetUsersByIdAsync);
        return await loader.LoadAsync(id);
    }
}

セキュリティの考慮

GraphQL APIに対して認証と認可を実装し、不正アクセスを防ぎます。JWTなどのトークンベース認証を使用し、各リクエストでユーザーの権限をチェックします。

public class AuthenticatedQuery : ObjectGraphType
{
    public AuthenticatedQuery(IUserRepository userRepository)
    {
        Field<UserType>(
            "user",
            arguments: new QueryArguments(new QueryArgument<IdGraphType> { Name = "id" }),
            resolve: context =>
            {
                var user = context.UserContext as ClaimsPrincipal;
                if (user == null || !user.Identity.IsAuthenticated)
                {
                    throw new ExecutionError("You are not authenticated");
                }

                return userRepository.GetUserById(context.GetArgument<int>("id"));
            }
        );
    }
}

これらのベストプラクティスを遵守することで、効率的で保守性の高いGraphQL APIを構築できます。

次に、本記事のまとめを行います。

まとめ

C#でGraphQLを実装する方法について、基本的な概念から実装手順、テストやデバッグ、そしてベストプラクティスまでを詳しく解説しました。GraphQLの基本概念を理解し、開発環境を整え、スキーマを定義してクエリやミューテーションを作成することで、効率的なAPIを構築できます。さらに、リゾルバーの分離やエラーハンドリング、バッチ処理、セキュリティの考慮など、実際の運用に役立つポイントも学びました。これにより、C#でGraphQLを使った堅牢でスケーラブルなAPI開発が可能となります。

コメント

コメントする

目次