可以通过GraphQL的模式定义和查询来自动生成类型定义
简要
- 2022年のフロントエンドに必要なスキルマップにもあるように、GraphQLという選択肢が一般化しています。
- GraphQLを使用する場合、GraphQLの特性を知らなければなりませんが、今回の記事ではGraphQLとは何か。どう使うのかについては言及致しません。その場合は、こちらの記事を一読していただけますと幸いです。
- 今回は、GraphQLを使用する際にGraphQLのスキーマやクエリからTypeScriptの型定義を自動生成する方法について記事にしていきます。
要自动生成TypeScript类型定义,可以从GraphQL的架构和查询中。
-
- 例えば、フロントエンド開発においてTypeScriptの導入をしている場合、GraphQLのスキーマごとの型定義を作成しなければなりません。
-
- ただ、GraphQLのスキーマを確認しながら型を手動で作っていくのってしんどいですよね。。
- そんな時に使用できるのが、GraphQL Code Generatorです。
GraphQL 代码生成器
-
- GraphQL Code Generatorというのは、GraphQLのスキーマ定義を使用して型定義を自動生成するためのツールです。
-
- このスキーマ定義の型定義を作成することで、クライアントサイドからのGraphQLのリクエストとレスポンスに型をつけることができます。
-
- さまざまなフレームワーク、ライブラリに対応しているので、用途にあったプラグインを組み合わせて使用します。
-
- また、フロントエンドでGraphQLを使用する場合とバックエンドでGraphQLを使用する場合、それぞれプラグインや実装方法が異なるので、注意が必要です。
- 今回はパターンを絞りフロントエンド側でGraphQLを使用するパターンを試していきます。
生成客户端类型定义
如果在前端使用GraphQL的話
-
- まず、GraphQL Code Generatorの便利さを伝えるために、GraphQL Code Generatorを使用しない場合について簡単に説明します。
- 下記のようなGraphQLのschemaがあったとします。
type Author {
id: Int!
firstName: String!
lastName: String!
posts(findTitle: String): [Post]
}
type Post {
id: Int!
title: String!
author: Author
}
type Query {
posts: [Post]
}
-
- フロントエンドでGraphQLを使用する場合は上記のスキーマ定義を確認しつつ、クライアント側の実装は、次のようにしAPIをリクエストします。
- なお下記のsampleはフロントエンド側をReactで実装した場合のものになります。
import { request, gql } from 'graphql-request'
import { useQuery } from 'react-query'
interface PostQuery {
posts: {
id: string
title: string
author?: {
id: string
firstName: string
lastName: string
}
}[]
}
const postsQueryDocument = gql`
query Posts {
posts {
id
title
author {
id
firstName
lastName
}
}
}
`
const Posts = () => {
const { data } = useQuery<PostQuery>('posts', async () => {
const { posts } = await request(endpoint, postsQueryDocument)
return posts
})
// ...
}
-
- GraphQLのリクエストをする際にPostQueryのinterfaceを作成し、それを使用していることがわかりますね。
-
- この手動管理、何が問題かというと例えば、スキーマ定義がバックエンド側で更新された場合、古い型を更新しないといけません。
-
- あとは、スキーマの定義を確認しながら手動で型を作成しているので型定義のミスが発生する可能性もあります。
- そして何より手動で型定義作るのはしんどいです。。
在使用React和GraphQL的情况下,根据每个插件的模式来 paraphrase。
-
- Reactを使用し、GraphQLエンドポイントに対してAPIリクエストを投げているプロジェクトの場合、GraphQLの使用方法がいくつかあります。
-
- GraphQLとTypeScriptでReactQueryを使用するパターン
- Apollo Clientを使用するパターン
让我们分别来看一下上述的两个事项。
使用GraphQL和TypeScript来使用ReactQuery的模式
- 下記にTypeScriptでReactQueryを使用する場合、今までは、下記のように型定義を手動で作成しGraphQLエンドポイントにAPIリクエストを送信します。
import { useQuery } from 'react-query'
import { request, gql } from 'graphql-request'
interface PostQuery {
posts: {
id: string
title: string
author?: {
id: string
firstName: string
lastName: string
}
}[]
}
const postsQueryDocument = gql`
query Posts {
posts {
id
title
author {
id
firstName
lastName
}
}
}
`
const Posts = () => {
const { data } = useQuery<PostQuery>('posts', async () => {
const { posts } = await request(endpoint, postsQueryDocument)
return posts
})
// ...
}
- この場合は、@graphql-codegen/typescript-react-queryプラグインを使用します。
使用npm或yarn来安装插件吧。
对于npm来说
npm install @graphql-codegen/typescript-react-query
npm install @graphql-codegen/typescript
npm install @graphql-codegen/typescript-operations
对于yarn来说
yarn add @graphql-codegen/typescript-react-query
yarn add @graphql-codegen/typescript
yarn add @graphql-codegen/typescript-operations
②: 接下来,我们将创建插件的配置。
- codegen.yamlというファイルを作成し、schemaには、使用しているGraphqlのスキーマ情報のパスを指定します。
schema: http://my-graphql-api.com/graphql
documents: './src/**/*.tsx'
generates:
./graphql/generated.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-query
config:
fetcher: fetch
在中文中用一种方式重新表达以下内容:
第③步:执行 codegen 并编写更新代码的 npm 脚本。
- package.jsonに下記のようなコマンドを追加しましょう。
{
"scripts": {
"generate": "graphql-codegen"
}
}
④:最后执行以下命令,自动生成graphql/generated.tsx(模式定义文件)。
针对npm的情况来说
npm run generate
如果是指纱线的情况
yarn generate
⑤:最后,将API请求发送到GraphQL端点的部分将被修改为以下内容。
- 簡単に言えば、上記で自動生成された型定義ファイルをimportし使用するように書き換える形です。
import gql from 'graphql-tag'
import { useQuery } from 'react-query'
import { usePosts } from '../graphql/generated'
gql`
query Posts {
posts {
id
title
author {
id
firstName
lastName
}
}
}
`
const Posts = () => {
const { data } = usePosts()
// `data` is typed!
// ...
}
使用Apollo Client的模式。
- Apollo Clientを使用している場合は、今までは下記のように型定義を手動で作成しGraphQLエンドポイントにAPIリクエストを送信します。
import { gql, useQuery } from '@apollo/client'
interface PostQuery {
posts: {
id: string
title: string
author?: {
id: string
firstName: string
lastName: string
}
}[]
}
const postsQueryDocument = gql`
query Posts {
posts {
id
title
author {
id
firstName
lastName
}
}
}
`
const Posts = () => {
const { data } = useQuery<PostQuery>(postsQueryDocument)
// ...
}
使用自动生成的类型定义文件也可以使下面的代码更简洁。
import { useQuery } from '@apollo/client'
import { postsQueryDocument } from './graphql/generated'
const Posts = () => {
const { data } = useQuery(postsQueryDocument)
// `result` is fully typed!
// ...
}
“使用 npm 或 yarn 安装插件吧。”
在npm的情况下
npm install @graphql-codegen/typed-document-node
npm install @graphql-codegen/typescript
npm install @graphql-codegen/typescript-operations
Yarn的情况
yarn add @graphql-codegen/typed-document-node
yarn add @graphql-codegen/typescript
yarn add @graphql-codegen/typescript-operations
下一步是创建插件的配置(Configure)部分。
- codegen.yamlというファイルを作成し、schemaには、使用しているGraphqlのスキーマ情報のパスを指定します。
schema: http://my-graphql-api.com/graphql
documents: './src/**/*.graphql'
generates:
./src/generated.ts:
plugins:
- typescript
- typescript-operations
- typed-document-node
③:运行 codegen 并编写更新代码的 npm 脚本。
- package.jsonに下記のようなコマンドを追加しましょう。
{
"scripts": {
"generate": "graphql-codegen"
}
}
④:请执行以下命令,自动生成graphql/generated.tsx(模式定义文件)作为最后一步。
对于npm而言
npm run generate
对于yarn而言
yarn generate
我真的试过了
-
- そしたら実際に型定義が自動生成されるのか試してみましょう。
- 先ほど説明したプラグインのパターン以外にもtypescript-graphql-requestというライブラリを使用したパターンなんかもあるので、今回はそちらを試してみましょう。
CLI安装
-
- 下記のコマンドでCLIのインストールを行います。
- また、graphql-codegenのCLIを使用する場合、graphqlもインストールする必要があります。
使用npm的情况下
npm install graphql
npm install @graphql-codegen/cli
在中国的情况下,对于羊毛来说。
yarn add graphql
yarn add @graphql-codegen/cli
- インストールすると、graphql-codegen/cliのセットアップをする必要があります。
如果使用npm的话
npx graphql-codegen init
npm run install
本次使用的插件
- 今回は、以下のプラグインを使用していきます。
npm install @graphql-codegen/typescript
npm install @graphql-codegen/typescript-operations
npm install @graphql-codegen/typescript-graphql-request
- プラグインのConfigureは下記のようにします。
schema: "https://raw.githubusercontent.com/marmelab/GraphQL-example/master/schema.graphql"
documents: "query.graphql"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- 今回使用するサンプルのスキーマは、下記のものです。
- 上記のスキーマをもとにリクエストを送るクエリを作成しましょう。
query TweetById ($id: ID!) {
Tweet(id: $id) {
id
body
date
Author {
id
username
}
}
}
- ここまでできればあとは、自動生成するコマンドを実行するだけです。
npm run generate
> my-app@0.1.0 generate
> graphql-codegen --config codegen.yml
(node:26606) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
✔ Parse configuration
✔ Generate outputs
-
- 自動生成することができました。
- 最後に自動生成されたファイルの中身を確認してみましょう。
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
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;
Date: any;
Url: any;
};
export type Meta = {
__typename?: 'Meta';
count?: Maybe<Scalars['Int']>;
};
export type Mutation = {
__typename?: 'Mutation';
createTweet?: Maybe<Tweet>;
deleteTweet?: Maybe<Tweet>;
markTweetRead?: Maybe<Scalars['Boolean']>;
};
export type MutationCreateTweetArgs = {
body?: InputMaybe<Scalars['String']>;
};
export type MutationDeleteTweetArgs = {
id: Scalars['ID'];
};
export type MutationMarkTweetReadArgs = {
id: Scalars['ID'];
};
export type Notification = {
__typename?: 'Notification';
date?: Maybe<Scalars['Date']>;
id?: Maybe<Scalars['ID']>;
type?: Maybe<Scalars['String']>;
};
export type Query = {
__typename?: 'Query';
Notifications?: Maybe<Array<Maybe<Notification>>>;
NotificationsMeta?: Maybe<Meta>;
Tweet?: Maybe<Tweet>;
Tweets?: Maybe<Array<Maybe<Tweet>>>;
TweetsMeta?: Maybe<Meta>;
User?: Maybe<User>;
};
export type QueryNotificationsArgs = {
limit?: InputMaybe<Scalars['Int']>;
};
export type QueryTweetArgs = {
id: Scalars['ID'];
};
export type QueryTweetsArgs = {
limit?: InputMaybe<Scalars['Int']>;
skip?: InputMaybe<Scalars['Int']>;
sort_field?: InputMaybe<Scalars['String']>;
sort_order?: InputMaybe<Scalars['String']>;
};
export type QueryUserArgs = {
id: Scalars['ID'];
};
export type Stat = {
__typename?: 'Stat';
likes?: Maybe<Scalars['Int']>;
responses?: Maybe<Scalars['Int']>;
retweets?: Maybe<Scalars['Int']>;
views?: Maybe<Scalars['Int']>;
};
export type Tweet = {
__typename?: 'Tweet';
Author?: Maybe<User>;
Stats?: Maybe<Stat>;
body?: Maybe<Scalars['String']>;
date?: Maybe<Scalars['Date']>;
id: Scalars['ID'];
};
export type User = {
__typename?: 'User';
avatar_url?: Maybe<Scalars['Url']>;
first_name?: Maybe<Scalars['String']>;
full_name?: Maybe<Scalars['String']>;
id: Scalars['ID'];
last_name?: Maybe<Scalars['String']>;
/** @deprecated Field no longer supported */
name?: Maybe<Scalars['String']>;
username?: Maybe<Scalars['String']>;
};
export type TweetByIdQueryVariables = Exact<{
id: Scalars['ID'];
}>;
export type TweetByIdQuery = { __typename?: 'Query', Tweet?: { __typename?: 'Tweet', id: string, body?: string | null, date?: any | null, Author?: { __typename?: 'User', id: string, username?: string | null } | null } | null };
太好了,感觉非常棒! , !)
其他选项
-
- ちなみにGraphQL Code Generator以外にも下記ライブラリもあります。
- こちらはいずれ別記事で紹介できればと思います。
总结
-
- 今まで、GraphQLのスキーマ定義からAPIのリクエストに使用する型定義を手動で作っていたのですが、こんな便利なツールがあったとは、目から鱗です。
- 是非お試しください!
请参考。