本稿では、graphql-codegenでTypeScriptの型定義を生成するとき、スキーマの型定義と操作の型定義を別々に生成する方法を紹介します。
上記を実現するのに重要になってくるimport-types
の使い方を例示しながら説明します。
生成元となるコードの準備
まず、生成元となる次のコードを準備します:
- schema.gql: GraphQLのスキーマファイル
- queries.gql: 操作を記述したファイル
それぞれ内容は次の通り:
scalar DateTime
type BlogPost {
id: ID!
title: String!
description: String
body: String!
author: Author!
createdOn: DateTime!
}
type Author {
id: ID!
name: String!
email: String!
}
type Query {
allBlogPosts: [BlogPost!]!
blogPost(id: ID!): BlogPost
}
type Mutation {
createBlogPost(input: CreateBlogPostInput): BlogPost
}
input CreateBlogPostInput {
title: String!
description: String
body: String!
authorId: ID!
}
query GetBlogPost($id: ID!) {
blogPost(id: $id) {
id
title
description
author {
id
name
}
createdOn
}
}
ここからgraphql-codegenを使い、TypeScriptの型定義ファイルを2つ生成します。
必要なツールの導入
graphql-codegenをはじめTypeScriptの型ファイル生成に必要なツールをインストールします:
yarn add graphql
yarn add -D typescript \
@graphql-codegen/cli \
@graphql-codegen/import-types-preset \
@graphql-codegen/typescript \
@graphql-codegen/typescript-operations \
prettier
このうち、@graphql-codegen/import-types-preset
が重要です。これを入れると、スキーマと操作の型定義を別々のファイルとして生成できるようになります。
graphql-codegenの設定
続いてgraphql-codegenの設定をしていきます。codegen.ymlを作り、内容は次のようにします:
overwrite: true
schema: "./schema.gql"
documents:
- queries.gql
generates:
# スキーマの型定義
types.ts: # [*1]
plugins:
- typescript
config:
scalars:
DateTime: Date
hooks:
afterOneFileWrite:
- prettier --write
# 操作の型定義
operations.ts:
preset: import-types
presetConfig:
typesPath: ./types # [*1]から拡張子tsを除いたファイル名を指定します
plugins:
- typescript-operations
hooks:
afterOneFileWrite:
- prettier --write
この設定で、graphql-codegenを実行すると、次の2つのファイルが生成されます:
- types.ts
- operations.ts
それぞれの中身は次のようになります:
export type Maybe<T> = T | null;
export type Exact<T extends { [key: string]: unknown }> = {
[K in keyof T]: T[K];
};
export type MakeOptional<T, K extends keyof T> = Omit<T, K> &
{ [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> &
{ [SubKey in K]: Maybe<T[SubKey]> };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: string;
String: string;
Boolean: boolean;
Int: number;
Float: number;
DateTime: Date;
};
export type Author = {
__typename?: "Author";
id: Scalars["ID"];
name: Scalars["String"];
email: Scalars["String"];
};
export type BlogPost = {
__typename?: "BlogPost";
id: Scalars["ID"];
title: Scalars["String"];
description?: Maybe<Scalars["String"]>;
body: Scalars["String"];
author: Author;
createdOn: Scalars["DateTime"];
};
export type CreateBlogPostInput = {
title: Scalars["String"];
description?: Maybe<Scalars["String"]>;
body: Scalars["String"];
authorId: Scalars["ID"];
};
export type Mutation = {
__typename?: "Mutation";
createBlogPost?: Maybe<BlogPost>;
};
export type MutationCreateBlogPostArgs = {
input?: Maybe<CreateBlogPostInput>;
};
export type Query = {
__typename?: "Query";
allBlogPosts: Array<BlogPost>;
blogPost?: Maybe<BlogPost>;
};
export type QueryBlogPostArgs = {
id: Scalars["ID"];
};
import * as Types from "./types";
export type GetBlogPostQueryVariables = Types.Exact<{
id: Types.Scalars["ID"];
}>;
export type GetBlogPostQuery = { __typename?: "Query" } & {
blogPost?: Types.Maybe<
{ __typename?: "BlogPost" } & Pick<
Types.BlogPost,
"id" | "title" | "description" | "createdOn"
> & {
author: { __typename?: "Author" } & Pick<Types.Author, "id" | "name">;
}
>;
};
これを見て分かるとおり、operations.tsはtypes.tsの型をimportして使っています。
おわり
この投稿で例示したコードはGitHubにありますのでご参考にどうぞ:
https://github.com/suinplayground/graphql-codegen-split-types-and-operation