GraphQL 使ってみたい~
けどまだ実務では使う機会がないので予習しておく
- C#でGraphQLサーバーを構築
- ライブラリには
HotChocolate
を採用 - ワークショップのページがあるため、実装して体感する
実行環境
- Windows10
- .NET8
- VSCode
- .NET 8.0 Runtime (v8.0.2) - Windows x64
1. プロジェクト作成
まずターミナルでdotnetコマンドからプロジェクト作成
dotnet new sln -n ConferencePlanner
dotnet new web -n GraphQL
dotnet sln add GraphQL
Data
フォルダを用意して、Speaker.cs
クラスを作成
mkdir GraphQL/Data
touch Data/Speaker.cs
using System.ComponentModel.DataAnnotations;
namespace ConferencePlanner.GraphQL.Data
{
public class Speaker
{
public int Id { get; set; }
[Required]
[StringLength(200)]
public string? Name { get; set; }
[StringLength(4000)]
public string? Bio { get; set; }
[StringLength(1000)]
public virtual string? WebSite { get; set; }
}
}
パッケージを追加
dotnet add GraphQL package Microsoft.EntityFrameworkCore.Sqlite --version 8.0.2
追加に成功するとプロジェクトファイルに以下の項目が追加される
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
Entity Framework
でDB接続するためApplicationDbContext.cs
クラスをData
フォルダに作成
touch Data/ApplicationDbContext.cs
using Microsoft.EntityFrameworkCore;
namespace ConferencePlanner.GraphQL.Data
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Speaker> Speakers { get; set; }
}
}
エントリーポイントのProgram.cs
にApplicationDbContext
をサービスとして登録する
using ConferencePlanner.GraphQL.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// サービスの登録
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite("Data Source=conferences.db"));
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
ORM
パッケージを追加
dotnet add GraphQL package Microsoft.EntityFrameworkCore.Tools --version 8.0.2
dotnet add GraphQL package Microsoft.EntityFrameworkCore.Design --version 8.0.2
dotnet-ef
のインストール
DBのマイグレーションの作成や適用、DBスキーマのスキャフォールディングなどをコマンドラインから実行できる
dotnet new tool-manifest
dotnet tool install dotnet-ef --version 8.0.2 --local
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.2",
"commands": [
"dotnet-ef"
]
}
}
}
ビルド
dotnet build GraphQL
マイグレーション実行
dotnet ef migrations add Initial --project GraphQL
dotnet ef database update --project GraphQL
GraphQL
パッケージを追加
dotnet add GraphQL package HotChocolate.AspNetCore --version 13.9.0
Query
リゾルバーとなるQuery.cs
クラスを追加
リソースを取得する(RESTでいうGET)
using System.Linq;
using HotChocolate;
using ConferencePlanner.GraphQL.Data;
namespace ConferencePlanner.GraphQL
{
public class Query
{
public IQueryable<Speaker> GetSpeakers([Service] ApplicationDbContext context) =>
context.Speakers;
}
}
Program.cs
にサービスを追加
依存性注入にGraphQL
スキーマを登録し、Query
タイプを登録している
using ConferencePlanner.GraphQL.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// サービスの登録
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite("Data Source=conferences.db"));
// 追加
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
GraphQL
ミドルウェアを構成して、サーバーがGraphQL
リクエストを実行する方法を認識できるようにする
using ConferencePlanner.GraphQL.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// サービスの登録
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite("Data Source=conferences.db"));
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>();
var app = builder.Build();
// ルーティングのミドルウェアを追加
app.UseRouting();
// エンドポイントのミドルウェアを設定
app.UseEndpoints(endpoints =>
{
endpoints.MapGraphQL();
});
app.MapGet("/", () => "Hello World!");
app.Run();
サーバーを起動
dotnet run --project GraphQL
サーバーが起動した後、http://localhost:5042/graphql
にアクセスするとBanana Cake PopIDEが開く
Brose Schema
ボタンをクリック後、Speaker
フィールドをクリックしてスキーマの返り値の型を確認
Mutation
リソースを変更する(RESTでいうPOST,PUT,DELETE)
以下、ChatGPTによる解説
GraphQLにおけるMutationは、データを変更する操作を表します。
これにはデータの追加、更新、削除などが含まれます。
簡単に言うと、Mutationはデータベースやサーバーに何か変更を加えたいときに使います。
Mutationは大きく3つの部分から構成されています。
Mutationの定義:
これは、あなたが実行したい具体的な操作を定義する部分です。
例えば、addSpeakerという名前のMutationを作るとき、
これが実際に「話者を追加する」という操作を行う定義になります。
入力(Input):
Mutationがデータを変更するために必要な情報を提供するためのものです。
addSpeakerの場合、話者の名前や経歴など、話者を追加するために必要な情報が入力として必要になります。
慣例として、この入力の型はAddSpeakerInputのように、Mutationの名前にInputを付けた形で命名されます。
ペイロード(Payload):
これは、Mutationの実行後に返されるデータの型です。
操作の結果として何が起こったのか、または新しく追加された話者の詳細など、
クライアントが受け取りたい情報を含みます。
慣例に従い、この型はAddSpeakerPayloadのように、Mutationの名前にPayloadを付けた形で命名されます。
AddSpeakerInput.cs
を追加
namespace ConferencePlanner.GraphQL
{
public record AddSpeakerInput(
string Name,
string Bio,
string WebSite);
}
AddSpeakerPayload.cs
を追加
using ConferencePlanner.GraphQL.Data;
namespace ConferencePlanner.GraphQL
{
public class AddSpeakerPayload
{
public AddSpeakerPayload(Speaker speaker)
{
Speaker = speaker;
}
public Speaker Speaker { get; }
}
}
mutation type
を定義したMutation.cs
を追加
using System.Threading.Tasks;
using ConferencePlanner.GraphQL.Data;
using HotChocolate;
namespace ConferencePlanner.GraphQL
{
public class Mutation
{
public async Task<AddSpeakerPayload> AddSpeakerAsync(
AddSpeakerInput input,
[Service] ApplicationDbContext context)
{
var speaker = new Speaker
{
Name = input.Name,
Bio = input.Bio,
WebSite = input.WebSite
};
context.Speakers.Add(speaker);
await context.SaveChangesAsync();
return new AddSpeakerPayload(speaker);
}
}
}
Program.cs
にMutation
を追加
builder.Services
.AddGraphQLServer()
.AddQueryType<Query>()
.AddMutationType<Mutation>(); // 追加
サーバーを起動
dotnet run --project GraphQL
サーバーが起動した後、http://localhost:5042/graphql
のschema Definition
から追加したMutaion
が確認できる
Operation
に以下のmutation
を記述してRun
をクリックすると、
mutation AddSpeaker {
addSpeaker(input: {
name: "Speaker Name"
bio: "Speaker Bio"
webSite: "http://speaker.website" }) {
speaker {
id
}
}
}
レスポンスが返却される
{
"data": {
"addSpeaker": {
"speaker": {
"id": 1
}
}
}
}
Operation
にGetSpeakerNames
のクエリを記述してRun
をクリックすると、
query GetSpeakerNames {
speakers {
name
}
}
mutation
によって実行された結果を取得できる
{
"data": {
"speakers": [
{
"name": "Speaker Name"
}
]
}
}
実行時のGraphQL
サーバーのログ
INSERT
とSELECT
のクエリが走っていることが確認できた
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (11ms) [Parameters=[@p0='?' (Size = 11), @p1='?' (Size = 12), @p2='?' (Size = 22)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Speakers" ("Bio", "Name", "WebSite")
VALUES (@p0, @p1, @p2)
RETURNING "Id";
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT "s"."Id", "s"."Bio", "s"."Name", "s"."WebSite"
FROM "Speakers" AS "s"
ソースコードはGitHubに置きました
2. null許容・非許容のコントロール
- GraphQLの型はデフォルトでnull許容
- C#の型はデフォルトでnull許容ではない
nullを許容する場合、GraphQL.csproj
で<Nullable>enable</Nullable>
とする
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="HotChocolate.AspNetCore" Version="13.9.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
Entity Framework
が実行時にSpeakersプロパティを初期化するため、コンパイラが不必要に警告を発するのを避けるために、null許容性に関するデフォルトの振る舞いをオーバーライドする (default==nullだが、!をつけてnullにならないという意)
public DbSet<Speaker> Speakers { get; set; } = default!;
Speaker.cs
クラスのプロパティにて、プロパティを設定する
[Required]
アノテーションを付与するとNULL非許容となる
C#の場合、string
(null非許容参照型)と string?
(null許容参照型)を?
の有無で設定する (※C#8.0以降)
using System.ComponentModel.DataAnnotations;
namespace ConferencePlanner.GraphQL.Data
{
public class Speaker
{
public int Id { get; set; }
[Required]
[StringLength(200)]
public string? Name { get; set; }
[StringLength(4000)]
public string? Bio { get; set; }
[StringLength(1000)]
public virtual string? WebSite { get; set; }
}
}
namespace ConferencePlanner.GraphQL
{
public record AddSpeakerInput(
string Name,
string? Bio,
string? WebSite);
}
GraphQL
スキーマで確認すると、!
サフィックスが付与されているものがnull非許容で定義されていることが確認できる
-
string
null許容 -
string!
null非許容 -
[Speaker!]!
オブジェクトの配列がnull非許容かつ、配列の要素すべてがnull非許容
詳しくは公式ドキュメント Object types and fields を参照
続く