go-gqlcodegen

module
v0.4.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: MIT

README

go-gqlcodegen

CI

A native Go port of GraphQL Code Generator (@graphql-codegen/cli + core + the flagship TypeScript plugins). It reads your existing codegen.yml and produces byte-identical TypeScript output from a single static binary — no Node, no npm dependency graph, ~9ms cold start.

gqlcodegen generate          # reads codegen.yml, writes your types
gqlcodegen generate -c custom.yml
gqlcodegen init              # scaffold a codegen.yml

Install

# From source
go install github.com/jclyons52/go-gqlcodegen/cmd/gqlcodegen@latest

# Or download a prebuilt binary for your platform from the
# GitHub Releases page (Linux, macOS, Windows; amd64 + arm64).

Why

The Node CLI spends ~700ms before doing any work (boot + loading a ~400-module dependency graph). gqlcodegen is a single binary that generates the same output in ~9ms — a ~90× speedup that makes codegen viable in commit hooks and watch loops.

@graphql-codegen/cli (Node) gqlcodegen (Go)
cold start, small schema ~730ms ~28ms
161-type schema → 3,168-line file ~765ms ~28ms
real-world repo (libplanet explorer, 1,830-line schema, client preset) 805ms 8.9ms

Measured with /usr/bin/time on the parity fixture (see scripts/parity/) and, for the last row, by dogfooding the real planetarium/libplanet-explorer-frontend repo with its codegen.yml used unchanged — all 4 generated files byte-identical, 91× faster. The tool's own parity harness reproduces this continuously (see below).

Ported surface

Layer What works
Host config discovery (codegen.yml, codegen.json, package.json#codegen), env interpolation, YAML/JSON config, generates fan-out, glob schema/document loading, .graphql/.gql parsing, gql-tag pluck from code files, output writing (hash-skip, check mode, stale-file removal), lifecycle hooks, watch mode
Core plugin-chain orchestration, prepend/append hoisting, document validation, union/interface resolution
Plugins typescript, typescript-operations, typescript-resolvers, add, schema-ast, typed-document-node, gen-dts (gql-tag-operations), fragment-masking — all byte-identical to upstream output on the parity fixture
Presets client — fans one generates entry into graphql.ts, gql.ts, fragment-masking.ts, index.ts, all byte-identical to upstream

Verification: scripts/parity/parity.sh runs the real @graphql-codegen/cli and this binary on the same fixture and diffs every generated file. All four configs (typescript, +operations, +resolvers, client preset) are byte-identical, including a 161-type stress schema.

Real-world dogfood

go-gqlcodegen was validated against planetarium/libplanet-explorer-frontend — a production GraphQL client with a 1,830-line schema (12 custom scalars, 8 enums, 11 @deprecated fields, custom root types) and 10 queries + 2 fragments:

  • Config unchanged: its codegen.yml (preset: client + a BigInt scalar mapping) was used as-is.
  • Byte-identical output: all 4 generated files (graphql.ts, gql.ts, fragment-masking.ts, index.ts) match current @graphql-codegen byte-for-byte, and the generated files typecheck under the repo's strict tsconfig.
  • Performance: 805ms (Node) → 8.9ms (Go) — 91× faster.

The dogfooding surfaced three parity gaps the synthetic fixture missed (document-definition ordering, unknown short-circuit in nullable variables, and the graphql-js 80-char argument-printing rule) — all fixed and covered by the parity harness.

Not ported in v1

  • .ts/.js/.mts config files — the Node ecosystem evaluates these via jiti. v1 supports YAML + JSON and rejects TS/JS configs with a clear error. A tsgo-based loader is planned (the tsgo binary is already vendored in the sibling ts-go-morph project).
  • near-operation-file preset — planned; requires a per-document output splitter and would share the ClientSideBaseVisitor machinery already ported for the client preset.
  • The 100+ ecosystem plugins (typescript-react-apollo, etc.) — plugins are compiled into the binary via a registry. The Plugin interface is kept RPC-friendly so hashicorp/go-plugin can be added for third-party plugins.
  • .graphqlrc multi-project (D8) and URL / introspection-JSON schema loading (D9) — later phases. SDL file/glob schemas are fully supported.

Formatting note

The Node pipeline runs Prettier over generated output. Go has no Prettier, so gqlcodegen uses a hand-rolled deterministic code writer. Output is byte-identical to the Node pipeline for the supported surface (verified by the parity harness) — the hand-rolled writer was tuned to match, not just approximate, upstream's output.

Plugin registry

Plugins register themselves via init() (see internal/plugins/). To add a new plugin:

package myplugin

import "github.com/jclyons52/go-gqlcodegen/internal/plugin"

type Plugin struct{}

func init() { plugin.Register(Plugin{}) }

func (Plugin) Name() string                 { return "my-plugin" }
func (Plugin) Generate(...) (*plugin.Output, error) { /* ... */ }

then blank-import it in internal/registry/registry.go.

Development

go build ./cmd/gqlcodegen        # build the binary
go test ./...                    # unit + golden tests
go test -race ./...              # race-detector pass
scripts/parity/parity.sh         # byte-diff against the real tool (needs node)

Releases are cut by tagging a version (git tag v0.2.0 && git push --tags); GitHub Actions builds cross-platform binaries with goreleaser and attaches them to the release. See .goreleaser.yaml and .github/workflows/release.yml.

Layout: cmd/gqlcodegen (CLI), internal/config (config loading), internal/load (schema/document loading), internal/core (plugin orchestration), internal/visitor (shared code writer / naming / scalars), internal/plugins/* (the ported plugins). Full design + phase history in PLAN.md.

Directories

Path Synopsis
cmd
gqlcodegen command
Command gqlcodegen is a Go port of the GraphQL Code Generator CLI (@graphql-codegen/cli).
Command gqlcodegen is a Go port of the GraphQL Code Generator CLI (@graphql-codegen/cli).
internal
cli
Package cli ports config.ts's buildOptions()/parseArgv() — the yargs flag definitions — onto cobra.
Package cli ports config.ts's buildOptions()/parseArgv() — the yargs flag definitions — onto cobra.
config
Package config ports config.ts's config discovery + loading: it finds a codegen config file (codegen.yml/codegen.json/package.json), parses it, and exposes a CodegenContext that carries the config plus CLI-flag overrides.
Package config ports config.ts's config discovery + loading: it finds a codegen config file (codegen.yml/codegen.json/package.json), parses it, and exposes a CodegenContext that carries the config plus CLI-flag overrides.
core
Package core ports @graphql-codegen/core's codegen(): it runs a plugin chain over a resolved schema + documents and concatenates the output, hoisting prepend/append blocks.
Package core ports @graphql-codegen/core's codegen(): it runs a plugin chain over a resolved schema + documents and concatenates the output, hoisting prepend/append blocks.
generate
Package generate ports the orchestration between the CLI and core: executeCodegen (codegen.ts) — for each `generates` entry, load schema + documents, run the plugin chain, and collect FileOutputs.
Package generate ports the orchestration between the CLI and core: executeCodegen (codegen.ts) — for each `generates` entry, load schema + documents, run the plugin chain, and collect FileOutputs.
gqlutil
Package gqlutil holds thin helpers over gqlparser's AST, mirroring the graphql-js type helpers that the TS plugins lean on.
Package gqlutil holds thin helpers over gqlparser's AST, mirroring the graphql-js type helpers that the TS plugins lean on.
interpolation
Package interpolation ports the string-env-interpolation npm package: it expands ${VAR} and ${VAR:-default} in config file content before parsing.
Package interpolation ports the string-env-interpolation npm package: it expands ${VAR} and ${VAR:-default} in config file content before parsing.
load
Package load ports load.ts: schema and document loading from file pointers and globs, backed by gqlparser.
Package load ports load.ts: schema and document loading from file pointers and globs, backed by gqlparser.
normalize
Package normalize ports the normalize* helpers from @graphql-codegen/plugin-helpers/src/helpers.ts and utils.ts.
Package normalize ports the normalize* helpers from @graphql-codegen/plugin-helpers/src/helpers.ts and utils.ts.
plugin
Package plugin defines the core contracts shared by the CLI, core orchestration, and the compiled-in plugins: the Plugin/Preset interfaces and the config types (mirroring @graphql-codegen/plugin-helpers' Types namespace).
Package plugin defines the core contracts shared by the CLI, core orchestration, and the compiled-in plugins: the Plugin/Preset interfaces and the config types (mirroring @graphql-codegen/plugin-helpers' Types namespace).
plugins/add
Package add ports @graphql-codegen/add: it injects arbitrary content before (prepend) or after (append) the generated output.
Package add ports @graphql-codegen/add: it injects arbitrary content before (prepend) or after (append) the generated output.
plugins/fragmentmasking
Package fragmentmasking ports the client preset's fragment-masking plugin: it emits fragment-masking.ts (FragmentType, useFragment, makeFragmentData, isFragmentReady helpers).
Package fragmentmasking ports the client preset's fragment-masking plugin: it emits fragment-masking.ts (FragmentType, useFragment, makeFragmentData, isFragmentReady helpers).
plugins/gqltagoperations
Package gqltagoperations ports @graphql-codegen/gql-tag-operations — the "gen-dts" plugin used by the client preset to emit gql.ts (the Documents registry + graphql() function with typed overloads).
Package gqltagoperations ports @graphql-codegen/gql-tag-operations — the "gen-dts" plugin used by the client preset to emit gql.ts (the Documents registry + graphql() function with typed overloads).
plugins/schemaast
Package schemaast ports @graphql-codegen/schema-ast: it prints the merged schema as SDL.
Package schemaast ports @graphql-codegen/schema-ast: it prints the merged schema as SDL.
plugins/typeddocumentnode
Package typeddocumentnode ports @graphql-codegen/typed-document-node: it serializes each operation/fragment into a JSON-literal DocumentNode (documentMode: documentNodeImportFragments) with a TypedDocumentNode type signature, byte-identical to upstream's output.
Package typeddocumentnode ports @graphql-codegen/typed-document-node: it serializes each operation/fragment into a JSON-literal DocumentNode (documentMode: documentNodeImportFragments) with a TypedDocumentNode type signature, byte-identical to upstream's output.
plugins/typescript
Package typescript ports @graphql-codegen/typescript: it generates base TypeScript types from a GraphQL schema.
Package typescript ports @graphql-codegen/typescript: it generates base TypeScript types from a GraphQL schema.
plugins/typescriptoperations
Package typescriptoperations ports @graphql-codegen/typescript-operations: it generates TypeScript types for the GraphQL operations (queries, mutations, subscriptions, fragments) — the selection-set → object-type lowering.
Package typescriptoperations ports @graphql-codegen/typescript-operations: it generates TypeScript types for the GraphQL operations (queries, mutations, subscriptions, fragments) — the selection-set → object-type lowering.
plugins/typescriptresolvers
Package typescriptresolvers ports @graphql-codegen/typescript-resolvers: it generates TypeScript resolver signatures from a GraphQL schema, including the ResolversTypes/ResolversParentTypes mappings, per-type XxxResolvers blocks, and the root Resolvers type.
Package typescriptresolvers ports @graphql-codegen/typescript-resolvers: it generates TypeScript resolver signatures from a GraphQL schema, including the ResolversTypes/ResolversParentTypes mappings, per-type XxxResolvers blocks, and the root Resolvers type.
presets/client
Package client ports @graphql-codegen/client-preset: the "client" preset fans one `generates` entry into graphql.ts, gql.ts, fragment-masking.ts and index.ts, byte-identical to upstream.
Package client ports @graphql-codegen/client-preset: the "client" preset fans one `generates` entry into graphql.ts, gql.ts, fragment-masking.ts and index.ts, byte-identical to upstream.
presets/nearoperationfile
Package nearoperationfile ports @graphql-codegen/near-operation-file-preset: generates one .ts file per operation document next to the source file, importing base types from a shared types file.
Package nearoperationfile ports @graphql-codegen/near-operation-file-preset: generates one .ts file per operation document next to the source file, importing base types from a shared types file.
registry
Package registry blank-imports every compiled-in plugin and preset so their init() functions register with the plugin.Registry.
Package registry blank-imports every compiled-in plugin and preset so their init() functions register with the plugin.Registry.
visitor
Package visitor ports the shared pieces of @graphql-codegen/visitor-plugin-common: naming conventions, scalar resolution, and the DeclarationBlock code writer.
Package visitor ports the shared pieces of @graphql-codegen/visitor-plugin-common: naming conventions, scalar resolution, and the DeclarationBlock code writer.
watch
Package watch ports utils/watcher.ts: a file watcher that re-runs codegen when schema/documents/config change, backed by fsnotify.
Package watch ports utils/watcher.ts: a file watcher that re-runs codegen when schema/documents/config change, backed by fsnotify.
Phase 0 spike: de-risk gqlparser as the GraphQL foundation for the graphql-codegen Go port.
Phase 0 spike: de-risk gqlparser as the GraphQL foundation for the graphql-codegen Go port.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL