How to generate GraphQL operations from GraphQL schema
Asked Answered
I

3

9

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"

Inhumation answered 16/8, 2020 at 11:25 Comment(0)
S
8

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)

Sovereignty answered 16/8, 2020 at 11:50 Comment(2)
Just to add, we needed something like that on GraphQL Mesh, so we've created a very simple tool that does that on GraphQL Tools. If anyone is interested, you could extract this code into a separate package, maybe the community could improve it: github.com/ardatan/graphql-tools/blob/master/packages/utils/src/… Here are the tests to see usage: github.com/ardatan/graphql-tools/blob/master/packages/utils/…Quadrireme
Or simply document how to import it from GraphQL Tools and example usagesQuadrireme
C
2

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

Cosy answered 7/4, 2022 at 4:57 Comment(0)
W
1

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 
Wedged answered 28/1, 2023 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.