简体   繁体   中英

How to generate GraphQL operations from GraphQL schema

I am looking for a way to make the developer workflow more efficient while GraphQL.

Currently, using graphql-code-generator to generate types from my GraphQL server on the frontend.

This is great, but this is only generating types. In order to generate methods for mutations, queries and subscriptions, I need to create a GraphQL document for each operation in my frontend project, for example:

file addPost.graphql

mutation addPost {
...
}
...

I find having to create an extra addPost.graphql to generate the method a bit redundant as it is already declared on my GraphQL server.

Is there a plugin/configuration that will generate these methods that I can use in my project without having to manually create the additional GraphQL documents?

Here is my GraphQL generator yml file

# graphql-generator.yml
overwrite: true
schema: https://localhost:8088/query
documents: ./libs/**/*.graphql          <----- generating these *.graphql files would be great! 
generates:
  libs/graphql/src/lib/generated/graphql.ts:
    plugins:
      - "typescript"
      - "typescript-resolvers"
      - "typescript-operations"
      - "typescript-apollo-angular"
  ./graphql.schema.json:
    plugins:
      - "introspection"

One of the core concepts of GraphQL is the ability to allow components, or any other consumer, to choose the fields they need, instead of something else decide it for them (backend developer / server / code).

Also, generating operations based on GraphQL schema is tricky - because of the nature of the graph, you can't really generate it without a set of constraints (for example: what nesting level to reach? what to do with circular links? what do you in case of arguments? what to do if the schema changes? and much more)

GraphQL Code Generator isn't doing that because we believe that the developer consuming the GraphQL layer should decide on the fields.

In some other solutions we developed, we needed such tooling to generate selection sets, but with some pre-defined constraints. This comments sums it and provides a code example for generating those: https://github.com/dotansimha/graphql-code-generator/discussions/2349#discussioncomment-15754

If you wish, you can either generate those into a file and then feed it to codegen, or you can create a custom documents loader in your project to create it dynamically ( https://graphql-code-generator.com/docs/getting-started/documents-field#custom-document-loader )

See also https://github.com/timqian/gql-generator

Generate queries from graphql schema, used for writing api test.

This tool generate 3 folders holding the queries: mutations, queries and subscriptions

I just came across this entry and implemented a solution for graphql-codegen and would like to leave it here.

Solution based on https://github.com/nestjs/graphql/issues/679 and is transformed into typescript.

Create a file named "operationsFromSchema.ts" with following content:

import {
  buildClientSchema,
  DocumentNode,
  getIntrospectionQuery,
  GraphQLSchema,
  OperationTypeNode,
  parse,
  print,
} from 'graphql';

import { buildOperationNodeForField } from '@graphql-tools/utils';

/**
 * @description Method to get schema from URL.
 * @param {string} url
 * @return {Promise<GraphQLSchema>}
 */
async function getSchemaFromUrl(url: string): Promise<GraphQLSchema> {
  // eslint-disable-next-line no-useless-catch
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query: getIntrospectionQuery().toString(),
      }),
    });

    const { data } = await response.json();

    return buildClientSchema(data);
  } catch (e) {
    throw e;
  }
}

/**
 * @description Get operations from schema.
 * See: https://github.com/nestjs/graphql/issues/679
 * @param {string} url
 * @return {Promise<DocumentNode>}
 */
async function operationsFromSchema(url: string): Promise<DocumentNode> {
  const schema: GraphQLSchema = await getSchemaFromUrl(url);

  const operationsDictionary = {
    query: { ...(schema.getQueryType()?.getFields() || {}) },
    mutation: { ...(schema.getMutationType()?.getFields() || {}) },
    subscription: { ...(schema.getSubscriptionType()?.getFields() || {}) },
  };

  let documentString: string = '';

  Object.keys(operationsDictionary).forEach((kind: string) => {
    Object.keys((operationsDictionary as any)[kind]).forEach((field: string) => {
      const operationAST = buildOperationNodeForField({
        schema,
        kind: kind as OperationTypeNode,
        field,
      });

      documentString += print(operationAST);
    });
  });

  return parse(documentString);
}

export default operationsFromSchema;

After the file has been created we can use these lines of code as a loader in the codegen file.

import { CodegenConfig } from '@graphql-codegen/cli'

const config: CodegenConfig = {
  schema: process.env.REACT_APP_ADMIN_API_URL,
  overwrite: true,
  generates: {
    './src/graphql/schema.tsx': {
      documents: {
        [process.env.REACT_APP_ADMIN_API_URL]: {
          loader: './src/graphql/operationsFromSchema.ts',
        }
      },
      plugins: [
        'typescript',
        'typescript-operations',
        'typescript-react-apollo'
      ],
      config: {
        dedupeOperationSuffix: true,
        omitOperationSuffix: true,
      },
    },
  }
}

export default config 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM