依据graphql schema自动生成客户端类型定义和调用接口

graphql 前端用起来还是真香的。今天我们就来讨论怎么根据后端给的schema自动生成自动生成客户端类型定义和调用接口。

GraphQL代码生成器旨在解决一个问题:在很多情况下,我们发现自己写的是GraphQL已经描述过的东西,只是格式不同;例如:解析器签名,MongoDB模型,Angular服务等。通过对schema分析并对其解析,GraphQL代码生成器可以基于预定义插件或基于用户自定义插件输出多种格式代码。无论你使用哪种语言,GraphQL代码生成器都能满足你的要求。

graphql-code-generator文档

简单案例:

type Author {
  id: Int!
  firstName: String!
  lastName: String!
  posts(findTitle: String): [Post]
}

type Post {
  id: Int!
  title: String!
  author: Author!
}

type Query {
  posts: [Post]
}

schema {
  query: Query
}
export type Maybe<T> = T | null;
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
  ID: string,
  String: string,
  Boolean: boolean,
  Int: number,
  Float: number,
};

export type Author = {
  __typename?: 'Author',
  id: Scalars['Int'],
  firstName: Scalars['String'],
  lastName: Scalars['String'],
  posts?: Maybe<Array<Maybe<Post>>>,
};

export type AuthorPostsArgs = {
  findTitle?: Maybe<Scalars['String']>
};

export type Post = {
  __typename?: 'Post',
  id: Scalars['Int'],
  title: Scalars['String'],
  author: Author,
};

export type Query = {
  __typename?: 'Query',
  posts?: Maybe<Array<Maybe<Post>>>,
};

安装

yarn add graphql

yarn add -D @graphql-codegen/cli

通过运行下面命令,将从路由端获取GraphQL schema,typescript类型定义将在指定的目标位置生成。

yarn generate

我们最好通过定义的codegen.yml文件进行生成。

codegen.yml

通过配置项进行GraphQL代码生成,需要创建codegen.yml或者codegen.json文件,然后运行生成代码。

CLI将自动检查定义的配置文件并相应地生成代码。此外,还可以使用--config选项定义配置文件的路径,如下:

yarn graphql-codegen --config ./path/to/config.yml

这个有个简单的配置:

schema: http://localhost:3000/graphql
documents: ./src/**/*.graphql
generates:
  ./src/types.ts:
    plugins:
      - typescript
      - typescript-operations

schema 字段

schema字段是指向GraphQLSchema-可以通过多种方式指定它并加载GraphQLSchema.可以指定一个schema的字符串,也可以指定string[]指向多个schemas,将被合并。

指定位置可以在根目录,也可以在输出文件的层级,例如:

schema: http://localhost:3000/graphql
generates:
  ./src/types.ts:
    plugins:
      - typescript
generates:
  ./src/types1.ts:
    schema: http://server1.com/graphql
    plugins:
      - typescript
  ./src/types2.ts:
    schema: http://server2.com/graphql
    plugins:
      - typescript

在根目录和输出文件层,同时都指定schema,将合并成一个:

schema: http://localhost:3000/graphql
generates:
  ./src/types.ts:
    schema: ./schema.graphql
    plugins:
      - typescript
      - typescript-operations

schema可用格式:URL、JSON、本地.graphql文件、Code Files、JavaScript export、String、GitHub

documents 字段

documents字段指向你的GraphQL文档:query, mutation, subscription and fragment.
可选项,当你用插件为客服端生成代码时才需要。
可以指定一个string,或者string[]指定多个文档地址。

config 字段

config字段用户将配置传递给插件。可以在.yml文件的多个层级中指定它。

require 字段

require字段允许加载任何外部文件,无需事先进行编译。

require:
  - extension1
  - extension2

上面介绍完基础概念和用法,生成对应的类型文件没问题了。重点来了,如果想生成接口文件怎么做呢?

这里我们拿react项目来举例。
需要用到的插件:typescript, typescript-operations, typescript-react-apollo ,near-operation-file-preset

near-operation-file-preset:这个插件为预设的每个操作文件生成对应的文件。
typescript-react-apollo:扩展了基本的TypeScript插件,@graphql-codegen/typescript, @graphql-codegen/typescript-operations,也具有相似的配置。

如果后端是graphql项目,可以从后端到处一份完成schema文档,可以根据该文档生成对应的typescript types文档和和接口文档。

overwrite: true
schema: ./demo.gql
generates:
  ./src/__generated__/types.ts:
    plugins:
      - typescript
  src/:
    documents: './src/pages/**/gqls.ts'
    schema: ./demo.gql
    preset: near-operation-file
    presetConfig:
      baseTypesPath: __generated__/types.ts
      extension: .generated.tsx
      folder: __generated__
    plugins:
      - typescript-operations
      - typescript-react-apollo

生成的接口文档:

import * as Types from '../../../__generated__/types';

import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
export type ListAuthorQueryVariables = Types.Exact<{
  perPage: Types.Scalars['Int'];
  page: Types.Scalars['Int'];
}>;


export type ListAuthorQuery = (
  { __typename?: 'query' }
  & { listAuthor?: Types.Maybe<(
    { __typename?: 'AuthorList' }
    & Pick<Types.AuthorList, 'count'>
    & { list?: Types.Maybe<Array<Types.Maybe<(
      { __typename?: 'Author' }
      & Pick<Types.Author, 'id' | 'firstName' | 'lastName'>
      & { posts?: Types.Maybe<Array<Types.Maybe<(
        { __typename?: 'Post' }
        & Pick<Types.Post, 'title' | 'author'>
      )>>> }
    )>>> }
  )> }
);

export type AuthorQueryVariables = Types.Exact<{
  id: Types.Scalars['Int'];
}>;


export type AuthorQuery = (
  { __typename?: 'query' }
  & { author?: Types.Maybe<(
    { __typename?: 'Author' }
    & Pick<Types.Author, 'id' | 'firstName' | 'lastName'>
    & { posts?: Types.Maybe<Array<Types.Maybe<(
      { __typename?: 'Post' }
      & Pick<Types.Post, 'title' | 'author'>
    )>>> }
  )> }
);

export type CreateAuthorMutationVariables = Types.Exact<{
  author?: Types.Maybe<Types.AuthorInPut>;
}>;


export type CreateAuthorMutation = (
  { __typename?: 'mutation' }
  & Pick<Types.Mutation, 'createAuthor'>
);

export type UpdateAuthorMutationVariables = Types.Exact<{
  id: Types.Scalars['Int'];
  author?: Types.Maybe<Types.AuthorInPut>;
}>;


export type UpdateAuthorMutation = (
  { __typename?: 'mutation' }
  & Pick<Types.Mutation, 'updateAuthor'>
);


export const ListAuthorDocument = gql`
    query ListAuthor($perPage: Int!, $page: Int!) {
  listAuthor(perPage: $perPage, page: $page) {
    count
    list {
      id
      firstName
      lastName
      posts {
        title
        author
      }
    }
  }
}
    `;

/**
 * __useListAuthorQuery__
 *
 * To run a query within a React component, call `useListAuthorQuery` and pass it any options that fit your needs.
 * When your component renders, `useListAuthorQuery` returns an object from Apollo Client that contains loading, error, and data properties
 * you can use to render your UI.
 *
 * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
 *
 * @example
 * const { data, loading, error } = useListAuthorQuery({
 *   variables: {
 *      perPage: // value for 'perPage'
 *      page: // value for 'page'
 *   },
 * });
 */
export function useListAuthorQuery(baseOptions?: Apollo.QueryHookOptions<ListAuthorQuery, ListAuthorQueryVariables>) {
        return Apollo.useQuery<ListAuthorQuery, ListAuthorQueryVariables>(ListAuthorDocument, baseOptions);
      }
export function useListAuthorLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ListAuthorQuery, ListAuthorQueryVariables>) {
          return Apollo.useLazyQuery<ListAuthorQuery, ListAuthorQueryVariables>(ListAuthorDocument, baseOptions);
        }
export type ListAuthorQueryHookResult = ReturnType<typeof useListAuthorQuery>;
export type ListAuthorLazyQueryHookResult = ReturnType<typeof useListAuthorLazyQuery>;
export type ListAuthorQueryResult = Apollo.QueryResult<ListAuthorQuery, ListAuthorQueryVariables>;
export const AuthorDocument = gql`
    query Author($id: Int!) {
  author(id: $id) {
    id
    firstName
    lastName
    posts {
      title
      author
    }
  }
}
    `;

/**
 * __useAuthorQuery__
 *
 * To run a query within a React component, call `useAuthorQuery` and pass it any options that fit your needs.
 * When your component renders, `useAuthorQuery` returns an object from Apollo Client that contains loading, error, and data properties
 * you can use to render your UI.
 *
 * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
 *
 * @example
 * const { data, loading, error } = useAuthorQuery({
 *   variables: {
 *      id: // value for 'id'
 *   },
 * });
 */
export function useAuthorQuery(baseOptions?: Apollo.QueryHookOptions<AuthorQuery, AuthorQueryVariables>) {
        return Apollo.useQuery<AuthorQuery, AuthorQueryVariables>(AuthorDocument, baseOptions);
      }
export function useAuthorLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<AuthorQuery, AuthorQueryVariables>) {
          return Apollo.useLazyQuery<AuthorQuery, AuthorQueryVariables>(AuthorDocument, baseOptions);
        }
export type AuthorQueryHookResult = ReturnType<typeof useAuthorQuery>;
export type AuthorLazyQueryHookResult = ReturnType<typeof useAuthorLazyQuery>;
export type AuthorQueryResult = Apollo.QueryResult<AuthorQuery, AuthorQueryVariables>;
export const CreateAuthorDocument = gql`
    mutation CreateAuthor($author: authorInPut) {
  createAuthor(author: $author)
}
    `;
export type CreateAuthorMutationFn = Apollo.MutationFunction<CreateAuthorMutation, CreateAuthorMutationVariables>;

/**
 * __useCreateAuthorMutation__
 *
 * To run a mutation, you first call `useCreateAuthorMutation` within a React component and pass it any options that fit your needs.
 * When your component renders, `useCreateAuthorMutation` returns a tuple that includes:
 * - A mutate function that you can call at any time to execute the mutation
 * - An object with fields that represent the current status of the mutation's execution
 *
 * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
 *
 * @example
 * const [createAuthorMutation, { data, loading, error }] = useCreateAuthorMutation({
 *   variables: {
 *      author: // value for 'author'
 *   },
 * });
 */
export function useCreateAuthorMutation(baseOptions?: Apollo.MutationHookOptions<CreateAuthorMutation, CreateAuthorMutationVariables>) {
        return Apollo.useMutation<CreateAuthorMutation, CreateAuthorMutationVariables>(CreateAuthorDocument, baseOptions);
      }
export type CreateAuthorMutationHookResult = ReturnType<typeof useCreateAuthorMutation>;
export type CreateAuthorMutationResult = Apollo.MutationResult<CreateAuthorMutation>;
export type CreateAuthorMutationOptions = Apollo.BaseMutationOptions<CreateAuthorMutation, CreateAuthorMutationVariables>;
export const UpdateAuthorDocument = gql`
    mutation UpdateAuthor($id: Int!, $author: authorInPut) {
  updateAuthor(id: $id, author: $author)
}
    `;
export type UpdateAuthorMutationFn = Apollo.MutationFunction<UpdateAuthorMutation, UpdateAuthorMutationVariables>;

/**
 * __useUpdateAuthorMutation__
 *
 * To run a mutation, you first call `useUpdateAuthorMutation` within a React component and pass it any options that fit your needs.
 * When your component renders, `useUpdateAuthorMutation` returns a tuple that includes:
 * - A mutate function that you can call at any time to execute the mutation
 * - An object with fields that represent the current status of the mutation's execution
 *
 * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
 *
 * @example
 * const [updateAuthorMutation, { data, loading, error }] = useUpdateAuthorMutation({
 *   variables: {
 *      id: // value for 'id'
 *      author: // value for 'author'
 *   },
 * });
 */
export function useUpdateAuthorMutation(baseOptions?: Apollo.MutationHookOptions<UpdateAuthorMutation, UpdateAuthorMutationVariables>) {
        return Apollo.useMutation<UpdateAuthorMutation, UpdateAuthorMutationVariables>(UpdateAuthorDocument, baseOptions);
      }
export type UpdateAuthorMutationHookResult = ReturnType<typeof useUpdateAuthorMutation>;
export type UpdateAuthorMutationResult = Apollo.MutationResult<UpdateAuthorMutation>;
export type UpdateAuthorMutationOptions = Apollo.BaseMutationOptions<UpdateAuthorMutation, UpdateAuthorMutationVariables>;

完整demo

初探graphql-code-generator自动生成类型文档和接口文档,希望大家多多指教!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 228,936评论 6 535
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 98,744评论 3 421
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 176,879评论 0 381
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,181评论 1 315
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 71,935评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,325评论 1 324
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,384评论 3 443
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,534评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,084评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 40,892评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,067评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,623评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,322评论 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,735评论 0 27
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 35,990评论 1 289
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 51,800评论 3 395
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,084评论 2 375