master
master v0.17.89 v0.17.88 v0.17.87 v0.17.86 v0.17.85 v0.17.84 v0.17.83 v0.17.82 v0.17.81 v0.17.80 v0.17.79 v0.17.78 v0.17.77 v0.17.76 v0.17.75 v0.17.74 v0.17.73 v0.17.72 v0.17.71 v0.17.70

Configuration

How to configure gqlgen using gqlgen.yml
[edit]
You are looking at the docs for the unreleased master branch. The latest version is v0.17.89.

gqlgen can be configured using a gqlgen.yml file, by default it will be loaded from the current directory, or any parent directory.

As an example, here is the default configuration file generated with gqlgen init:

# Where are all the schema files located? globs are supported eg  src/**/*.graphqls
schema:
  - graph/*.graphqls

# Where should the generated server code go?
exec:
  package: graph
  layout: single-file # Only other option is "follow-schema," ie multi-file.

  # Only for single-file layout:
  filename: graph/generated.go

  # Only for follow-schema layout:
  # dir: graph
  # filename_template: "{name}.generated.go"

  # Optional: Maximum number of goroutines in concurrency to use per child resolvers(default: unlimited)
  # worker_limit: 1000

# Comment or remove this section to skip Apollo Federation support
federation:
  filename: graph/federation.go
  package: graph
  version: 2
  options:
    computed_requires: true

# Where should any generated models go?
model:
  filename: graph/model/models_gen.go
  package: model

  # Optional: Pass in a path to a new gotpl template to use for generating the models
  # model_template: [your/path/model.gotpl]

# Where should the resolver implementations go?
resolver:
  package: graph
  layout: follow-schema # Only other option is "single-file."

  # Only for single-file layout:
  # filename: graph/resolver.go

  # Only for follow-schema layout:
  dir: graph
  filename_template: "{name}.resolvers.go"

  # Optional: turn on to not generate template comments above resolvers
  # omit_template_comment: false
  # Optional: Pass in a path to a new gotpl template to use for generating resolvers
  # resolver_template: [your/path/resolver.gotpl]
  # Optional: turn on to avoid rewriting existing resolver(s) when generating
  # preserve_resolver: false

# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
# struct_tag: json

# Optional: turn on to split imports in generated files into local modules and third-party packages
# local_prefix: github.com/myrepo

# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: false

# Optional: turn on to omit Is<Name>() methods to interface and unions
# omit_interface_checks : true

# Optional: turn on to skip generation of ComplexityRoot struct content and Complexity function
# omit_complexity: false

# Optional: turn on to not generate any file notice comments in generated files
# omit_gqlgen_file_notice: false

# Optional: turn on to exclude the gqlgen version in the generated file notice. No effect if `omit_gqlgen_file_notice` is true.
# omit_gqlgen_version_in_file_notice: false

# Optional: turn on to exclude root models such as Query and Mutation from the generated models file.
# omit_root_models: false

# Optional: turn on to exclude resolver fields from the generated models file.
# omit_resolver_fields: false

# Optional: turn on to set a different prefix to the generated base structs used for embedding.
# embedded_structs_prefix: "Base"

# Optional: turn off to make struct-type struct fields not use pointers
# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing }
# struct_fields_always_pointers: true

# Optional: turn off to make resolvers return values instead of pointers for structs
# resolvers_always_return_pointers: true

# Optional: turn on to return pointers instead of values in unmarshalInput
# return_pointers_in_unmarshalinput: false

# Optional: wrap nullable input fields with Omittable
# nullable_input_omittable: true

# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true

# Optional: set to skip running `go mod tidy` when generating server code
# skip_mod_tidy: true

# Optional: set to use -gcflags="-N -l" during validation to disable compiler
# optimizations. This makes cold cache validation ~2x faster since we only need
# to check for errors, not produce optimized code. Default: false
# fast_validation: false

# Optional: set to use go/format.Source instead of imports.Process for formatting.
# This is significantly faster (~10x) but doesn't group imports by stdlib/external/internal.
# Imports will be sorted alphabetically instead. Default: false
# skip_import_grouping: false

# Optional: set to use light mode (NeedName|NeedFiles|NeedModule) for initial package
# loading instead of full mode with NeedTypes. This avoids triggering compilation until
# types are actually needed. Default: false (enable for large projects)
# use_light_mode_prefetch: false

# Optional: set to reuse byte buffers via sync.Pool during code formatting to reduce
# GC pressure. Default: false (enable for large projects)
# use_buffer_pooling: false

# Optional: set to skip generation of JSON Marshalers and Unmarshalers for enums
# omit_enum_json_marshalers: false

# Optional: if this is set to true, argument directives that
# decorate a field with a null value will still be called.
#
# This enables argumment directives to not just mutate
# argument values but to set them even if they're null.
call_argument_directives_with_null: true

# This enables gql server to use function syntax for execution context
# instead of generating receiver methods of the execution context.
# use_function_syntax_for_execution_context: true

# Optional: set build tags that will be used to load packages
# go_build_tags:
#  - private
#  - enterprise

# Optional: set to modify the initialisms regarded for Go names
# go_initialisms:
#   replace_defaults: false # if true, the default initialisms will get dropped in favor of the new ones instead of being added
#   initialisms: # List of initialisms to for Go names
#     - 'CC'
#     - 'BCC'

# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
#  - "{{.}}/graph/model"

# Optional: turn on to allow binding to getters and hasers (useful for editions protos)
# autobind_getter_haser: true

# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
  ID:
    model:
      - github.com/99designs/gqlgen/graphql.ID
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64
      - github.com/99designs/gqlgen/graphql.Int32
  # gqlgen provides a default GraphQL UUID convenience wrapper for github.com/google/uuid
  # but you can override this to provide your own GraphQL UUID implementation
  UUID:
    model:
      - github.com/99designs/gqlgen/graphql.UUID

  # The GraphQL spec explicitly states that the Int type is a signed 32-bit
  # integer. Using Go int or int64 to represent it can lead to unexpected
  # behavior, and some GraphQL tools like Apollo Router will fail when
  # communicating numbers that overflow 32-bits.
  #
  # You may choose to use the custom, built-in Int64 scalar to represent 64-bit
  # integers, or ignore the spec and bind Int to graphql.Int / graphql.Int64
  # (the default behavior of gqlgen). This is fine in simple use cases when you
  # do not need to worry about interoperability and only expect small numbers.
  Int:
    model:
      - github.com/99designs/gqlgen/graphql.Int32
  Int64:
    model:
      - github.com/99designs/gqlgen/graphql.Int
      - github.com/99designs/gqlgen/graphql.Int64

Everything has defaults, so add things as you need.

Inline config with directives

gqlgen ships with some builtin directives that make it a little easier to manage wiring.

To start using them you first need to define them:

directive @goModel(
	model: String
	models: [String!]
	forceGenerate: Boolean
) on OBJECT | INPUT_OBJECT | SCALAR | ENUM | INTERFACE | UNION

directive @goField(
	forceResolver: Boolean
	name: String
	omittable: Boolean
	type: String
  autoBindGetterHaser: Boolean
  forceGenerate: Boolean
	batch: Boolean
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION

directive @goTag(
	key: String!
	value: String
) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION

directive @goExtraField(
	name: String
	type: String!
	overrideTags: String
	description: String
) repeatable on OBJECT | INPUT_OBJECT

directive @inlineArguments on ARGUMENT_DEFINITION

Here be dragons

gqlgen doesnt currently support user-configurable directives for SCALAR, ENUM, INTERFACE or UNION. This only works for internal directives. You can track the progress here

Now you can use these directives when defining types in your schema:

type User
	@goModel(model: "github.com/my/app/models.User")
	@goExtraField(name: "Activated", type: "bool") {
	id: ID! @goField(name: "todoId")
	name: String!
		@goField(forceResolver: true)
		@goTag(key: "xorm", value: "-")
		@goTag(key: "yaml")
}

# This make sense when autobind activated.
type Person @goModel(forceGenerate: true) {
	id: ID!
	name: String!
}

The builtin directives goField, goModel, goTag and goExtraField are automatically registered to skip_runtime. Any directives registered as skip_runtime will not exposed during introspection and are used during code generation only.

If you have created a new code generation plugin using a directive which does not require runtime execution, the directive will need to be set to skip_runtime.

e.g. a custom directive called constraint would be set as skip_runtime using the following configuration

# custom directives which are not exposed during introspection. These directives are
# used for code generation only
directives:
  constraint:
    skip_runtime: true

Configuring for a big projects

For a single gqlgen project, what gqlgen config works best really changes at a few key points during the course of that project’s normal growth and evolution. For instance:

  1. quick proof of concept - single-file layout
  2. medium-sized team(s) - follow-schema layout
  3. many teams - separate schema and resolvers into separate packages as in this example (see below)
  4. very large type systems (more than 65,000 methods) - use_function_syntax_for_execution_context However, some will instead choose to adopt GraphQL Federation and split into multiple gqlgen instances before one of these growth points is even reached.

Big projects usually divide into a separate domains, grouping all related files and resources under a folder such as:

DomainA
- schema
- resolver
- service
DomainB
- schema
- resolver
- service

After first generating resolvers section you can comment out the entire resolver section of the config.yaml, so that resolvers are not auto-generated so you can then design any desired resolver architecture. This idea is from a discussion https://github.com/99designs/gqlgen/issues/1253

Performance optimization options

gqlgen provides several options to optimize code generation performance, especially useful for large schemas.

skip_validation

skip_validation: true

Skips the final validation step that compiles generated code to check for errors.

When to use:

When NOT to use:

Performance impact: Saves 1-4 minutes depending on project size and cache state.


fast_validation

fast_validation: true  # default: false

Uses -gcflags="-N -l" during validation to disable Go compiler optimizations and inlining. Since validation only checks for compilation errors (not producing production binaries), optimizations are unnecessary.

When to use:

When NOT to use:

Performance impact: ~2x faster cold cache validation (e.g., 4m 30s → 2m 15s).

Trade-off: None for typical use. The validation binary is discarded after checking for errors.


skip_import_grouping

skip_import_grouping: true  # default: false

Uses go/format.Source instead of golang.org/x/tools/imports.Process for formatting generated code. This is significantly faster but produces different import formatting.

When to use:

When NOT to use:

Performance impact: ~10x faster formatting (e.g., 2 minutes → 10 seconds for large projects).

Trade-off:

Example difference:

// skip_import_grouping: false (default) - grouped imports
import (
    "context"
    "fmt"

    "github.com/99designs/gqlgen/graphql"
    "github.com/vektah/gqlparser/v2/ast"

    "myproject/internal/models"
)

// skip_import_grouping: true - alphabetically sorted, no groups
import (
    "context"
    "fmt"
    "github.com/99designs/gqlgen/graphql"
    "github.com/vektah/gqlparser/v2/ast"
    "myproject/internal/models"
)

skip_mod_tidy

skip_mod_tidy: true

Skips running go mod tidy after code generation.

When to use:

When NOT to use:

Performance impact: Saves 1-5 seconds depending on module size.


use_light_mode_prefetch

use_light_mode_prefetch: true  # default: false

Uses NeedName|NeedFiles|NeedModule instead of full NeedTypes for initial package loading. This avoids triggering compilation until types are actually needed (e.g., for autobind).

When to use:

When NOT to use:

Performance impact: ~200x faster initial package loading for large codebases.


use_buffer_pooling

use_buffer_pooling: true  # default: false

Reuses bytes.Buffer via sync.Pool during code formatting to reduce GC pressure.

When to use:

When NOT to use:

Performance impact: Reduces GC pause times by ~25% for large projects.


For maximum speed during development:

skip_validation: true
skip_mod_tidy: true
skip_import_grouping: true
fast_validation: true
use_light_mode_prefetch: true
use_buffer_pooling: true

For CI with validation:

skip_validation: false
fast_validation: true
skip_mod_tidy: true
skip_import_grouping: true  # or false if import style matters
use_light_mode_prefetch: true
use_buffer_pooling: true

For production/release builds:

skip_validation: false
fast_validation: true
skip_mod_tidy: false
# skip_import_grouping: false  # default - consistent import style