Scenarios

graphql provides consistent api architecture metadata on the front and back ends, while speeding up the performance of web-backend interactions through request merging and on-demand fetching.

Use with ts

Basic idea

  1. scan all gql strings in the code
  2. get the graphql strings in the code and generate type definitions
  3. use these type definitions

Steps to use

Here is a demonstration using github api@v4

Get the back-end metadata

1
curl https://docs.github.com/public/schema.docs.graphql > schema.graphql

Install the base sdk

1
pnpm i @apollo/client graphql

Install code generator related dependencies

  • @graphql-codegen/cli base cli
  • @graphql-codegen/typescript ts plugins
  • @graphql-codegen/typescript-operations ts operation generation plugin
  • @graphql-codegen/near-operation-file-preset ts preset configuration

Create configuration codegen.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
overwrite: true
schema: "schema.graphql"
generates:
  ./src/graphql.gql.ts:
    plugins:
      - typescript
  ./:
    documents:
      - "src/**/*.ts"
      - "!src/**/*.gql.ts"
    preset: near-operation-file
    presetConfig:
      baseTypesPath: ./src/graphql.gql.ts
      extension: .gql.ts
    plugins:
      - typescript-operations

Add some graphql variables to the code

tip: In non-react projects, please import all non-react content from @apollo/client/core.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { gql } from "@apollo/client";

export const findRepoStar = gql`
  query findRepoStar($name: String!, $owner: String!) {
    repository(name: $name, owner: $owner) {
      name
      stargazerCount
    }
  }
`;
export const findRepo = gql`
  query findRepo($name: String!, $owner: String!) {
    repository(name: $name, owner: $owner) {
      name
    }
  }
`;

Using cli to generate type definitions

1
pnpm graphql-codegen --config codegen.yml

The generated results are roughly as follows, basically the parameters and result types

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import * as Types from "../graphql.gql";

export type FindRepoStarQueryVariables = Types.Exact<{
  name: Types.Scalars["String"];
  owner: Types.Scalars["String"];
}>;

export type FindRepoStarQuery = {
  __typename?: "Query";
  repository?:
    | { __typename?: "Repository"; name: string; stargazerCount: number }
    | null
    | undefined;
};

export type FindRepoQueryVariables = Types.Exact<{
  name: Types.Scalars["String"];
  owner: Types.Scalars["String"];
}>;

export type FindRepoQuery = {
  __typename?: "Query";
  repository?: { __typename?: "Repository"; name: string } | null | undefined;
};

We can use it like this

1
2
3
4
5
6
7
8
const res = await client.query<FindRepoStarQuery, FindRepoStarQueryVariables>({
  query: findRepoStar,
  variables: {
    name: "liuli-tools",
    owner: "rxliuli",
  },
});
console.log("res: ", res);

Jetbrains IDE support

  1. install the plugin JS GraphQL

  2. create graphql base configuration file .graphqlconfig

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    
    {
    "name": "github GraphQL Schema",
    "schemaPath": "schema.graphql",
    "extensions": {
        "endpoints": {
        "Default GraphQL Endpoint": {
            "url": "https://api.github.com/graphql",
            "headers": {
            "user-agent": "JS GraphQL",
            "Authorization": "bearer ${env:GITHUB_TOKEN}"
            },
            "introspect": false
        }
        }
    }
    }
    
  3. Final effect

    image

VSCode support

  1. install the plugin Apollo GraphQL

  2. add configuration apollo.config.js

    1
    2
    3
    4
    5
    6
    7
    8
    
    module.exports = {
    client: {
        service: {
        name: "github GraphQL Schema",
        localSchemaFile: "./schema.graphql",
        },
    },
    };
    
  3. Final effect

    image

Integration into vite/rollup

The reason why autogeneration should be integrated as a vite plugin was previously explained in Is it necessary to integrate the cli tool into the build tool.

vite-plugin-graphql-codegen The monitoring mode is actually buggy, so maintain a rollup plugin yourself

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Plugin } from "vite";
import { CodegenContext, generate, loadContext } from "@graphql-codegen/cli";

async function codegen(watch: boolean) {
  const codegenContext = await loadContext();
  codegenContext.updateConfig({ watch });
  try {
    await generate(codegenContext);
  } catch (error) {
    console.log("Something went wrong.");
  }
}

export function graphQLCodegen(): Plugin {
  let codegenContext: CodegenContext;

  return {
    name: "rollup-plugin-graphql-codegen",
    async buildStart() {
      // noinspection ES6MissingAwait
      codegen(this.meta.watchMode);
    },
  };
}

Here you can actually use worker_threads to try to accelerate multiple threads.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { Plugin } from "vite";
import { generate, loadContext } from "@graphql-codegen/cli";
import { isMainThread, parentPort, Worker } from "worker_threads";
import { expose, wrap } from "comlink";
import nodeEndpoint from "comlink/dist/umd/node-adapter";

async function codegen(watch: boolean) {
  const codegenContext = await loadContext();
  codegenContext.updateConfig({ watch });
  try {
    await generate(codegenContext);
  } catch (error) {
    console.log("Something went wrong.");
  }
}

if (!isMainThread) {
  expose(codegen, nodeEndpoint(parentPort!));
}

export function graphQLCodegen(): Plugin {
  return {
    name: "rollup-plugin-graphql-codegen",
    async buildStart() {
      const worker = new Worker(__filename);
      try {
        const codegenWorker = wrap<(watch: boolean) => void>(
          nodeEndpoint(worker)
        );
        // noinspection ES6MissingAwait
        codegenWorker(this.meta.watchMode);
      } finally {
        worker.unref();
      }
    },
  };
}

Posted to @liuli-util/rollup-plugin-graphql-codegen

Install the chrome plugin

GraphQL Network Inspector

image

Reference

Other options?

  • Use the rollup plugin @rollup/plugin-graphql to treat .graphql files directly as importable es modules - the main issue is to deal with compatibility of related tools, mainly jest/eslint.