### CHANGELOG # CHANGELOG * [FEATURE] Add `ValidateDeprecated()` schema option to enable validation of deprecated fields, arguments (including directive arguments), input fields, and enum values in queries. When enabled, usage of deprecated schema elements results in validation errors. This opt-in approach allows applications to enforce deprecation policies without breaking existing clients. * [FEATURE] Support executable-document description strings on full-form operations, fragments, and variable definitions. Descriptions remain non-semantic and do not change validation or execution behavior. Executable `#` comments remain ignored. [v1.9.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.9.0) Release v1.9.0 * [IMPROVEMENT] Reduce query execution allocations by reusing internal temporary buffers in the execution hot path. Add `DisableMemoryPooling()` schema option to opt out and enable pooled vs non-pooled benchmark comparison. Added a `MaxPooledBufferCap(n)` method to set the maximum buffer capacity (in bytes) that can be returned to the internal memory pool. The default limit is 16KB. * [FEATURE] Allow schema cloning and applying a resolver to a schema without one. See `Clone`, `MustClone` and `ApplyResolver` schema methods for more details. * [CHORE] Applied `go fix ./...`-style modernization across the repo to align the code with newer Go idioms and standard library helpers. * [CHORE] Bump Go version in go.mod file to v1.25 to be one minor version less than the latest stable Go release. [v1.8.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.8.0) Release v1.8.0 * [FEATURE] Added `DecodeSelectedFieldArgs` helper function to decode argument values for any (nested) selected field path directly from a resolver context, enabling efficient multi-level prefetching without per-resolver argument reflection. This enables selective, multi‑level batching (Category → Products → Reviews) by loading only requested fields, mitigating N+1 issues despite complex filters or pagination. * [CHORE] Bump Go version in go.mod file to v1.24 to be one minor version less than the latest stable Go release. [v1.7.2](https://github.com/graph-gophers/graphql-go/releases/tag/v1.7.2) Release v1.7.2 * [BUGFIX] Fix checksum mismatch between direct git access and golang proxy for v1.7.1. This version contains identical functionality to v1.7.1 but with proper tag creation to ensure consistent checksums across all proxy configurations. [v1.7.1](https://github.com/graph-gophers/graphql-go/releases/tag/v1.7.1) Release v1.7.1 * [IMPROVEMENT] `SelectedFieldNames` now returns dot-delimited nested field paths (e.g. `products`, `products.id`, `products.category`, `products.category.id`). Intermediate container object/list paths are included so resolvers can check for both a branch (`products.category`) and its leaves (`products.category.id`). `HasSelectedField` and `SortedSelectedFieldNames` operate on these paths. This aligns behavior with typical resolver projection needs and fixes missing nested selections. * [BUGFIX] Reject object, interface, and input object type definitions that declare zero fields/input values (spec compliance). * [IMPROVEMENT] Optimize overlapping field validation to avoid quadratic memory blowups on large sibling field lists. * [FEATURE] Add configurable safety valve for overlapping field comparison count with `OverlapValidationLimit(n)` schema option (0 disables the cap). When exceeded validation aborts early with rule `OverlapValidationLimitExceeded`. Disabled by default. * [TEST] Add benchmarks & randomized overlap stress test for mixed field/fragment patterns. [v1.7.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.7.0) Release v1.7.0 * [FEATURE] Add resolver field selection inspection helpers (`SelectedFieldNames`, `HasSelectedField`, `SortedSelectedFieldNames`). Helpers are available by default and compute results lazily only when called. An explicit opt-out (`DisableFieldSelections()` schema option) is provided for applications that want to remove even the minimal context insertion overhead when the helpers are never used. [v1.5.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.5.0) Release v1.5.0 * [FEATURE] Add specifiedBy directive in #532 * [IMPROVEMENT] In this release we improve validation for primitive values, directives, repeat directives, #515, #516, #525, #527 * [IMPROVEMENT] Fix minor unreachable code caused by t.Fatalf #530 * [BUG] Fix __type queries sometimes not returning data in #540 * [BUG] Allow deprecated directive on arguments by @pavelnikolov in #541 * [DOCS] Add array input example #536 [v1.4.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.4.0) Release v1.4.0 * [FEATURE] Add basic first step for Apollo Federation. This does NOT include full subgraph specification. This PR adds support only for `_service` schema level field. This library is long way from supporting the full sub-graph spec and we do not plan to implement that any time soon. [v1.3.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.3.0) Release v1.3.0 * [FEATURE] Support custom panic handler #468 * [FEATURE] Support interfaces implementing interfaces #471 * [BUG] Support parsing nanoseconds time properly #486 * [BUG] Fix a bug in maxDepth fragment spread logic #492 [v1.2.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.2.0) Release v1.2.0 * [DOCS] Added examples of how to add JSON map as input scalar type. The goal of this change was to improve documentation #467 [v1.1.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.1.0) Release v1.1.0 * [FEATURE] Add types package #437 * [FEATURE] Expose `packer.Unmarshaler` as `decode.Unmarshaler` to the public #450 * [FEATURE] Add location fields to type definitions #454 * [FEATURE] `errors.Errorf` preserves original error similar to `fmt.Errorf` #456 * [BUGFIX] Fix duplicated __typename in response (fixes #369) #443 [v1.0.0](https://github.com/graph-gophers/graphql-go/releases/tag/v1.0.0) Initial release --- ### CONTRIBUTING # Contributing - With issues: - Use the search tool before opening a new issue. - Please provide source code and commit sha if you found a bug. - Review existing issues and provide feedback or react to them. - With pull requests: - Open your pull request against `main` - Your pull request should have no more than two commits, if not you should squash them. - It should pass all tests in the available continuous integrations systems such as TravisCI. - You should add/modify tests to cover your proposed code changes. - If your pull request contains a new feature, please document it well: - Consider adding Go executable examples - Comment all new exported types if outside of the `internal` package - (optional) Mention it in the README - Add a comment in the CHANGELOG.md explaining your feature --- ### README # graphql-go [](https://sourcegraph.com/github.com/graph-gophers/graphql-go?badge) [](https://github.com/graph-gophers/graphql-go/actions/workflows/go.yml) [](https://goreportcard.com/report/github.com/graph-gophers/graphql-go) [](https://godoc.org/github.com/graph-gophers/graphql-go)

The goal of this project is to provide full support of the [October 2021 GraphQL specification](https://spec.graphql.org/October2021/) with a set of idiomatic, easy to use Go packages. While still under development (`internal` APIs are almost certainly subject to change), this library is safe for production use. ## Features - minimal API - support for `context.Context` - support for the `OpenTelemetry` and `OpenTracing` standards - schema type-checking against resolvers - resolvers are matched to the schema based on method sets (can resolve a GraphQL schema with a Go interface or Go struct). - handles panics in resolvers - parallel execution of resolvers - subscriptions - [sample WS transport](https://github.com/graph-gophers/graphql-transport-ws) ## (Some) Documentation [](https://godoc.org/github.com/graph-gophers/graphql-go) ### Getting started In order to run a simple GraphQL server locally create a `main.go` file with the following content: ```go package main import ( "log" "net/http" graphql "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/relay" ) type query struct{} func (query) Hello() string { return "Hello, world!" } func main() { s := ` type Query { hello: String! } ` schema := graphql.MustParseSchema(s, &query{}) http.Handle("/query", &relay.Handler{Schema: schema}) log.Fatal(http.ListenAndServe(":8080", nil)) } ``` Then run the file with `go run main.go`. To test: ```sh curl -XPOST -d '{"query": "{ hello }"}' localhost:8080/query ``` For more realistic usecases check our [examples section](https://github.com/graph-gophers/graphql-go/wiki/Examples). ### Resolvers A resolver must have one method or field for each field of the GraphQL type it resolves. The method or field name has to be [exported](https://golang.org/ref/spec#Exported_identifiers) and match the schema's field's name in a non-case-sensitive way. You can use struct fields as resolvers by using `SchemaOpt: UseFieldResolvers()`. For example, ``` opts := []graphql.SchemaOpt{graphql.UseFieldResolvers()} schema := graphql.MustParseSchema(s, &query{}, opts...) ``` When using `UseFieldResolvers` schema option, a struct field will be used *only* when: - there is no method for a struct field - a struct field does not implement an interface method - a struct field does not have arguments The method has up to two arguments: - Optional `context.Context` argument. - Mandatory `*struct { ... }` argument if the corresponding GraphQL field has arguments. The names of the struct fields have to be [exported](https://golang.org/ref/spec#Exported_identifiers) and have to match the names of the GraphQL arguments in a non-case-sensitive way. The method has up to two results: - The GraphQL field's value as determined by the resolver. - Optional `error` result. Example for a simple resolver method: ```go func (r *helloWorldResolver) Hello() string { return "Hello world!" } ``` The following signature is also allowed: ```go func (r *helloWorldResolver) Hello(ctx context.Context) (string, error) { return "Hello world!", nil } ``` ### Separate resolvers for different operations > **NOTE**: This feature is not in the stable release yet. In order to use it you need to run `go get github.com/graph-gophers/graphql-go@master` and in your `go.mod` file you will have something like: > ``` > v1.5.1-0.20230216224648-5aa631d05992 > ``` > It is expected to be released in `v1.6.0` soon. The GraphQL specification allows for fields with the same name defined in different query types. For example, the schema below is a valid schema definition: ```graphql schema { query: Query mutation: Mutation } type Query { hello: String! } type Mutation { hello: String! } ``` The above schema would result in name collision if we use a single resolver struct because fields from both operations correspond to methods in the root resolver (the same Go struct). In order to resolve this issue, the library allows resolvers for query, mutation and subscription operations to be separated using the `Query`, `Mutation` and `Subscription` methods of the root resolver. These special methods are optional and if defined return the resolver for each opeartion. For example, the following is a resolver corresponding to the schema definition above. Note that there is a field named `hello` in both the query and the mutation definitions: ```go type RootResolver struct{} type QueryResolver struct{} type MutationResolver struct{} func(r *RootResolver) Query() *QueryResolver { return &QueryResolver{} } func(r *RootResolver) Mutation() *MutationResolver { return &MutationResolver{} } func (*QueryResolver) Hello() string { return "Hello query!" } func (*MutationResolver) Hello() string { return "Hello mutation!" } schema := graphql.MustParseSchema(sdl, &RootResolver{}, nil) ... ``` ### Schema Options - `UseStringDescriptions()` enables the usage of double quoted and triple quoted. When this is not enabled, comments are parsed as descriptions instead. - `UseFieldResolvers()` specifies whether to use struct field resolvers. - `MaxDepth(n int)` specifies the maximum field nesting depth in a query. The default is 0 which disables max depth checking. - `MaxParallelism(n int)` specifies the maximum number of resolvers per request allowed to run in parallel. The default is 10. - `Tracer(tracer trace.Tracer)` is used to trace queries and fields. It defaults to `noop.Tracer`. - `Logger(logger log.Logger)` is used to log panics during query execution. It defaults to `exec.DefaultLogger`. - `PanicHandler(panicHandler errors.PanicHandler)` is used to transform panics into errors during query execution. It defaults to `errors.DefaultPanicHandler`. - `DisableIntrospection()` disables introspection queries. - `DisableFieldSelections()` disables capturing child field selections used by helper APIs (see below). ### Field Selection Inspection Helpers Resolvers can introspect which immediate child fields were requested using: ```go graphql.SelectedFieldNames(ctx) // []string of direct child schema field names graphql.HasSelectedField(ctx, "name") // bool graphql.SortedSelectedFieldNames(ctx) // sorted copy ``` Use cases include building projection lists for databases or conditionally avoiding expensive sub-fetches. The helpers are intentionally shallow (only direct children) and fragment spreads / inline fragments are flattened with duplicates removed; meta fields (e.g. `__typename`) are excluded. Performance: selection data is computed lazily only when a helper is called. If you never call them there is effectively no additional overhead. To remove even the small context value insertion you can opt out with `DisableFieldSelections()`; helpers then return empty results. For more detail and examples see the [docs](https://godoc.org/github.com/graph-gophers/graphql-go). ### Custom Errors Errors returned by resolvers can include custom extensions by implementing the `ResolverError` interface: ```go type ResolverError interface { error Extensions() map[string]interface{} } ``` Example of a simple custom error: ```go type droidNotFoundError struct { Code string `json:"code"` Message string `json:"message"` } func (e droidNotFoundError) Error() string { return fmt.Sprintf("error [%s]: %s", e.Code, e.Message) } func (e droidNotFoundError) Extensions() map[string]interface{} { return map[string]interface{}{ "code": e.Code, "message": e.Message, } } ``` Which could produce a GraphQL error such as: ```go { "errors": [ { "message": "error [NotFound]: This is not the droid you are looking for", "path": [ "droid" ], "extensions": { "code": "NotFound", "message": "This is not the droid you are looking for" } } ], "data": null } ``` ### Tracing By default the library uses `noop.Tracer`. If you want to change that you can use the OpenTelemetry or the OpenTracing implementations, respectively: ```go // OpenTelemetry tracer package main import ( "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" otelgraphql "github.com/graph-gophers/graphql-go/trace/otel" "github.com/graph-gophers/graphql-go/trace/tracer" ) // ... _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(otelgraphql.DefaultTracer())) // ... ``` Alternatively you can pass an existing trace.Tracer instance: ```go tr := otel.Tracer("example") _, err = graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(&otelgraphql.Tracer{Tracer: tr})) ``` ```go // OpenTracing tracer package main import ( "github.com/graph-gophers/graphql-go" "github.com/graph-gophers/graphql-go/example/starwars" "github.com/graph-gophers/graphql-go/trace/opentracing" "github.com/graph-gophers/graphql-go/trace/tracer" ) // ... _, err := graphql.ParseSchema(starwars.Schema, nil, graphql.Tracer(opentracing.Tracer{})) // ... ``` If you need to implement a custom tracer the library would accept any tracer which implements the interface below: ```go type Tracer interface { TraceQuery(ctx context.Context, queryString string, operationName string, variables map[string]interface{}, varTypes map[string]*introspection.Type) (context.Context, func([]*errors.QueryError)) TraceField(ctx context.Context, label, typeName, fieldName string, trivial bool, args map[string]interface{}) (context.Context, func(*errors.QueryError)) TraceValidation(context.Context) func([]*errors.QueryError) } ``` ### [Examples](https://github.com/graph-gophers/graphql-go/wiki/Examples) --- ### SECURITY # Security Policy ## Supported Versions We always try to maintain the library secure and suggest our users to upgrade to the latest stable version. We realize that sometimes this is not possible. | Version | Supported | | ------- | ------------------ | | 1.x | :white_check_mark: | | < 1.0 | :x: | ## MaxDepth If you are using the `graphql.MaxDepth` schema option, make sure that you upgrade to version v1.3.0 or higher due to a bug causing security vulnerability in earlier versions. ## Reporting a Vulnerability If you find a security vulnerability with this library, please, DO NOT submit a pull request right away. Please, report the issue to @pavelnikolov in the Gophers Slack in a private message. ---