├── integration ├── config.yaml ├── user.graphql ├── testomitempty.graphql ├── .babelrc ├── remote_api │ └── user.go ├── codegen.yml ├── testomitempty │ └── testmodel.go ├── models-go │ ├── viewer.go │ └── element.go ├── .graphqlconfig ├── readme.md ├── jest.config.js ├── gqlgen.yml ├── package.json ├── schema.graphql └── schema-expected.graphql ├── codegen ├── config │ └── testdata │ │ ├── cfg │ │ ├── outer │ │ ├── subdir │ │ │ ├── inner │ │ │ └── gqlgen.yaml │ │ ├── otherdir │ │ │ └── .gitkeep │ │ ├── gqlgen.yml │ │ ├── malformedconfig.yml │ │ ├── unknownkeys.yml │ │ ├── glob │ │ │ ├── foo │ │ │ │ └── foo.graphql │ │ │ └── bar │ │ │ │ └── bar with spaces.graphql │ │ ├── glob.yml │ │ └── unwalkable.yml │ │ ├── defaultconfig │ │ └── schema.graphql │ │ ├── example.go │ │ └── autobinding │ │ ├── chat │ │ └── message.go │ │ └── scalars │ │ └── model │ │ └── model.go ├── templates │ ├── test.gotpl │ ├── testdata │ │ ├── a │ │ │ └── bar │ │ │ │ └── bar.go │ │ ├── b │ │ │ └── bar │ │ │ │ └── bar.go │ │ └── pkg_mismatch │ │ │ └── turtles.go │ └── test_.gotpl ├── testserver │ ├── singlefile │ │ ├── introspection │ │ │ └── it.go │ │ ├── loops.graphql │ │ ├── ptr_to_slice.go │ │ ├── recursive.go │ │ ├── fields_order.go │ │ ├── invalid-packagename │ │ │ └── invalid-identifier.go │ │ ├── variadic.graphql │ │ ├── ptr_to_slice.graphql │ │ ├── builtinscalar.graphql │ │ ├── useptr.graphql │ │ ├── typefallback.graphql │ │ ├── otherpkg │ │ │ └── model.go │ │ ├── enum.graphql │ │ ├── weird_type_cases.graphql │ │ ├── slices.graphql │ │ ├── mutation_with_custom_scalar.graphql │ │ ├── scalar_context.graphql │ │ ├── wrapped_type.go │ │ ├── panics.graphql │ │ ├── v-ok.graphql │ │ ├── fields_order.graphql │ │ ├── complexity.graphql │ │ ├── v-ok.go │ │ ├── useptr_test.go │ │ ├── primitive_objects.graphql │ │ ├── variadic.go │ │ ├── scalar_default.graphql │ │ ├── wrapped_type.graphql │ │ ├── ptr_to_ptr_input.go │ │ ├── maps.graphql │ │ ├── defaults.graphql │ │ ├── issue896.graphql │ │ ├── ptr_to_ptr_input.graphql │ │ ├── embedded.graphql │ │ ├── nulls.graphql │ │ ├── bytes.go │ │ ├── thirdparty.go │ │ ├── mutation_with_custom_scalar.go │ │ ├── typefallback_test.go │ │ ├── gqlgen.yml │ │ ├── response_extension_test.go │ │ ├── variadic_test.go │ │ ├── scalar_default_test.go │ │ ├── ptr_to_slice_test.go │ │ ├── validtypes_test.go │ │ ├── embedded.go │ │ └── scalar_context.go │ ├── followschema │ │ ├── introspection │ │ │ └── it.go │ │ ├── loops.graphql │ │ ├── ptr_to_slice.go │ │ ├── recursive.go │ │ ├── fields_order.go │ │ ├── invalid-packagename │ │ │ └── invalid-identifier.go │ │ ├── variadic.graphql │ │ ├── ptr_to_slice.graphql │ │ ├── useptr.graphql │ │ ├── builtinscalar.graphql │ │ ├── otherpkg │ │ │ └── model.go │ │ ├── typefallback.graphql │ │ ├── enum.graphql │ │ ├── slices.graphql │ │ ├── weird_type_cases.graphql │ │ ├── mutation_with_custom_scalar.graphql │ │ ├── scalar_context.graphql │ │ ├── wrapped_type.go │ │ ├── panics.graphql │ │ ├── v-ok.graphql │ │ ├── fields_order.graphql │ │ ├── complexity.graphql │ │ ├── v-ok.go │ │ ├── primitive_objects.graphql │ │ ├── useptr_test.go │ │ ├── variadic.go │ │ ├── scalar_default.graphql │ │ ├── wrapped_type.graphql │ │ ├── ptr_to_ptr_input.go │ │ ├── maps.graphql │ │ ├── defaults.graphql │ │ ├── issue896.graphql │ │ ├── nulls.graphql │ │ ├── ptr_to_ptr_input.graphql │ │ ├── embedded.graphql │ │ ├── bytes.go │ │ ├── thirdparty.go │ │ ├── mutation_with_custom_scalar.go │ │ ├── typefallback_test.go │ │ ├── gqlgen.yml │ │ ├── response_extension_test.go │ │ ├── scalar_default_test.go │ │ ├── ptr_to_slice_test.go │ │ ├── validtypes_test.go │ │ ├── embedded.go │ │ └── scalar_context.go │ └── empty.go ├── complexity.go ├── interface.gotpl ├── type.go └── util.go ├── docs ├── content │ ├── _introduction.md │ └── introduction.md ├── static │ ├── favicon.ico │ ├── schema_layout.png │ ├── request_anatomy.png │ └── external-link-alt.svg ├── layouts │ ├── 404.html │ ├── partials │ │ ├── version-switcher.html │ │ └── version-banner.html │ ├── _default │ │ └── single.html │ ├── sitemap.xml │ └── index.html ├── readme.md └── config.yml ├── api ├── testdata │ ├── default │ │ └── graph │ │ │ ├── model │ │ │ └── doc.go │ │ │ └── schema.graphqls │ └── federation2 │ │ └── graph │ │ ├── model │ │ └── doc.go │ │ ├── schema.graphqls │ │ └── federation.go └── option.go ├── _examples ├── fileupload │ ├── testfiles │ │ ├── a.txt │ │ ├── b.txt │ │ └── c.txt │ ├── .gqlgen.yml │ ├── model │ │ └── generated.go │ └── schema.graphql ├── federation │ ├── accounts │ │ ├── graph │ │ │ ├── model │ │ │ │ ├── model.go │ │ │ │ └── models_gen.go │ │ │ ├── resolver.go │ │ │ ├── schema.graphqls │ │ │ ├── schema.resolvers.go │ │ │ └── entity.resolvers.go │ │ └── server.go │ ├── products │ │ ├── graph │ │ │ ├── model │ │ │ │ ├── model.go │ │ │ │ └── models_gen.go │ │ │ ├── resolver.go │ │ │ ├── schema.graphqls │ │ │ ├── products.go │ │ │ └── schema.resolvers.go │ │ └── server.go │ ├── readme.md │ ├── reviews │ │ ├── graph │ │ │ ├── resolver.go │ │ │ ├── model │ │ │ │ ├── models_gen.go │ │ │ │ └── models.go │ │ │ ├── schema.graphqls │ │ │ ├── reviews.go │ │ │ └── schema.resolvers.go │ │ └── server.go │ ├── jest.config.js │ ├── start.sh │ ├── package.json │ └── gateway │ │ └── index.js ├── scalars │ ├── external │ │ └── model.go │ ├── model │ │ └── generated.go │ ├── server │ │ └── server.go │ ├── .gqlgen.yml │ └── schema.graphql ├── embedding │ ├── parent.graphqls │ └── subdir │ │ ├── subdir.graphqls │ │ ├── schemadir │ │ └── root.graphqls │ │ ├── model.go │ │ ├── gendir │ │ ├── model.go │ │ └── federation_gen.go │ │ ├── cfgdir │ │ ├── generate_in_subdir.yml │ │ └── generate_in_gendir.yml │ │ ├── federation_gen.go │ │ └── resolvers.go ├── chat │ ├── .gqlgen.yml │ ├── models_gen.go │ ├── .gitignore │ ├── public │ │ └── index.html │ ├── schema.graphql │ ├── tsconfig.json │ ├── src │ │ ├── App.js │ │ └── graphql-sse.ts │ ├── package.json │ └── readme.md ├── selection │ ├── .gqlgen.yml │ ├── readme.md │ ├── schema.graphql │ └── server │ │ └── server.go ├── type-system-extension │ ├── schemas │ │ ├── enum-extension.graphql │ │ ├── union-extension.graphql │ │ ├── object-extension.graphql │ │ ├── scalar-extension.graphql │ │ ├── input-object-extension.graphql │ │ ├── interface-extension.graphql │ │ ├── type-extension.graphql │ │ ├── schema-extension.graphql │ │ └── schema.graphql │ ├── gqlgen.yml │ ├── README.md │ └── server │ │ └── server.go ├── tools.go ├── dataloader │ ├── .gqlgen.yml │ ├── readme.md │ ├── models_gen.go │ ├── schema.graphql │ └── server │ │ └── server.go ├── todo │ ├── readme.md │ ├── gqlgen.yml │ ├── models.go │ ├── server │ │ └── server.go │ ├── schema.graphql │ └── models_gen.go ├── config │ ├── todo.graphql │ ├── user.graphql │ ├── schema.graphql │ ├── model.go │ ├── models_gen.go │ ├── server │ │ └── server.go │ ├── .gqlgen.yml │ ├── resolver.go │ ├── user.resolvers.go │ ├── todo.resolvers.go │ └── schema.resolvers.go ├── readme.md ├── websocket-initfunc │ └── server │ │ ├── graph │ │ ├── resolver.go │ │ ├── model │ │ │ └── models_gen.go │ │ ├── schema.graphqls │ │ └── schema.resolvers.go │ │ ├── Makefile │ │ ├── go.mod │ │ └── readme.md └── starwars │ ├── readme.md │ ├── .gqlgen.yml │ ├── server │ └── server.go │ └── benchmarks_test.go ├── internal ├── code │ ├── testdata │ │ ├── a │ │ │ └── a.go │ │ ├── b │ │ │ └── b.go │ │ └── c │ │ │ └── c.go │ └── util_test.go ├── tools.go ├── imports │ ├── testdata │ │ ├── unused.go │ │ └── unused.expected.go │ └── prune_test.go └── rewrite │ ├── testdata │ └── example.go │ └── rewriter_test.go ├── .dockerignore ├── graphql ├── version.go ├── root.go ├── oneshot.go ├── any.go ├── context_path_test.go ├── context_root_field_test.go ├── handler │ ├── transport │ │ ├── headers.go │ │ ├── websocket_close_reason.go │ │ ├── util.go │ │ ├── error.go │ │ ├── options.go │ │ └── reader.go │ ├── extension │ │ ├── introspection_test.go │ │ └── introspection.go │ └── lru │ │ └── lru.go ├── bool_test.go ├── recovery.go ├── map.go ├── upload.go ├── time.go ├── bool.go ├── float_test.go ├── time_test.go ├── context_root_field.go ├── context_field_test.go ├── response.go ├── error.go ├── cache.go ├── float.go └── jsonw_test.go ├── plugin ├── federation │ ├── testdata │ │ ├── entities │ │ │ ├── nokey.graphql │ │ │ └── nokey.yml │ │ ├── interfaces │ │ │ ├── key.graphqls │ │ │ ├── key.yml │ │ │ ├── extends.yml │ │ │ └── extends.graphqls │ │ ├── schema │ │ │ ├── customquerytype.graphql │ │ │ └── customquerytype.yml │ │ ├── entityresolver │ │ │ ├── resolver.go │ │ │ ├── schema.resolvers.go │ │ │ ├── generated │ │ │ │ └── errors.go │ │ │ └── gqlgen.yml │ │ ├── allthethings │ │ │ ├── gqlgen.yml │ │ │ ├── model │ │ │ │ └── federation.go │ │ │ └── schema.graphql │ │ └── federation2 │ │ │ ├── federation2.yml │ │ │ └── federation2.graphql │ ├── fedruntime │ │ └── runtime.go │ └── test_data │ │ └── model │ │ └── federation.go ├── resolvergen │ └── testdata │ │ ├── schema.graphql │ │ ├── return_values │ │ ├── schema.graphqls │ │ ├── model.go │ │ ├── gqlgen.yml │ │ ├── resolvers.go │ │ └── return_values_test.go │ │ ├── invalid_model_path │ │ └── gqlgen.yml │ │ ├── filetemplate │ │ ├── out │ │ │ ├── resolver.go │ │ │ └── model.go │ │ └── gqlgen.yml │ │ ├── followschema │ │ ├── out │ │ │ ├── resolver.go │ │ │ └── model.go │ │ └── gqlgen.yml │ │ └── singlefile │ │ ├── out │ │ ├── model.go │ │ └── resolver.go │ │ └── gqlgen.yml ├── modelgen │ ├── out │ │ └── existing.go │ ├── out_struct_pointers │ │ └── existing.go │ └── testdata │ │ ├── gqlgen.yml │ │ └── gqlgen_struct_field_pointers.yml ├── servergen │ └── server.gotpl └── plugin.go ├── .gitattributes ├── testdata └── gomod-with-leading-comments.mod ├── .github ├── ISSUE_TEMPLATE.md ├── workflows │ ├── check-fmt │ ├── check-generate │ ├── check-federation │ ├── check-init │ ├── check-coverage │ ├── security.yml │ ├── check-integration │ └── test.yml └── PULL_REQUEST_TEMPLATE.md ├── client ├── errors.go └── readme.md ├── .gitignore ├── .editorconfig ├── .chglog └── config.yml ├── init-templates └── schema.graphqls ├── .golangci.yml ├── RELEASE-CHECKLIST.md ├── LICENSE ├── go.mod └── bin └── release /integration/config.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/outer: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/subdir/inner: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/otherdir/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/_introduction.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /codegen/config/testdata/defaultconfig/schema.graphql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/testdata/default/graph/model/doc.go: -------------------------------------------------------------------------------- 1 | package model 2 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: outer 2 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/malformedconfig.yml: -------------------------------------------------------------------------------- 1 | asdf 2 | -------------------------------------------------------------------------------- /codegen/templates/test.gotpl: -------------------------------------------------------------------------------- 1 | this is my test package 2 | -------------------------------------------------------------------------------- /codegen/templates/testdata/a/bar/bar.go: -------------------------------------------------------------------------------- 1 | package bar 2 | -------------------------------------------------------------------------------- /codegen/templates/testdata/b/bar/bar.go: -------------------------------------------------------------------------------- 1 | package bar 2 | -------------------------------------------------------------------------------- /_examples/fileupload/testfiles/a.txt: -------------------------------------------------------------------------------- 1 | Alpha file content 2 | -------------------------------------------------------------------------------- /_examples/fileupload/testfiles/b.txt: -------------------------------------------------------------------------------- 1 | Bravo file content 2 | -------------------------------------------------------------------------------- /_examples/fileupload/testfiles/c.txt: -------------------------------------------------------------------------------- 1 | Charlie file content 2 | -------------------------------------------------------------------------------- /api/testdata/federation2/graph/model/doc.go: -------------------------------------------------------------------------------- 1 | package model 2 | -------------------------------------------------------------------------------- /codegen/config/testdata/example.go: -------------------------------------------------------------------------------- 1 | package config_test_data 2 | -------------------------------------------------------------------------------- /codegen/templates/test_.gotpl: -------------------------------------------------------------------------------- 1 | this will not be included 2 | -------------------------------------------------------------------------------- /_examples/federation/accounts/graph/model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | -------------------------------------------------------------------------------- /_examples/federation/products/graph/model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/subdir/gqlgen.yaml: -------------------------------------------------------------------------------- 1 | schema: inner 2 | -------------------------------------------------------------------------------- /internal/code/testdata/a/a.go: -------------------------------------------------------------------------------- 1 | package a 2 | 3 | var A = "A" 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /**/node_modules 2 | /codegen/tests/gen 3 | /vendor 4 | -------------------------------------------------------------------------------- /codegen/templates/testdata/pkg_mismatch/turtles.go: -------------------------------------------------------------------------------- 1 | package turtles 2 | -------------------------------------------------------------------------------- /_examples/fileupload/.gqlgen.yml: -------------------------------------------------------------------------------- 1 | model: 2 | filename: model/generated.go 3 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/unknownkeys.yml: -------------------------------------------------------------------------------- 1 | schema: outer 2 | unknown: foo 3 | -------------------------------------------------------------------------------- /graphql/version.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | const Version = "v0.17.27-dev" 4 | -------------------------------------------------------------------------------- /_examples/scalars/external/model.go: -------------------------------------------------------------------------------- 1 | package external 2 | 3 | type ObjectID int 4 | -------------------------------------------------------------------------------- /_examples/embedding/parent.graphqls: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | parentdir: String! 3 | } 4 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/subdir.graphqls: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | subdir: String! 3 | } 4 | -------------------------------------------------------------------------------- /integration/user.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | name: String! 3 | likes: [String!]! 4 | } 5 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/schemadir/root.graphqls: -------------------------------------------------------------------------------- 1 | type Query { 2 | inSchemadir: String! 3 | } 4 | -------------------------------------------------------------------------------- /docs/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/si3nloong/gqlgen/master/docs/static/favicon.ico -------------------------------------------------------------------------------- /integration/testomitempty.graphql: -------------------------------------------------------------------------------- 1 | type RemoteModelWithOmitempty { 2 | newDesc: String 3 | } 4 | -------------------------------------------------------------------------------- /integration/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@babel/env", {"exclude": ["transform-regenerator"]}]] 3 | } 4 | -------------------------------------------------------------------------------- /docs/static/schema_layout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/si3nloong/gqlgen/master/docs/static/schema_layout.png -------------------------------------------------------------------------------- /_examples/federation/readme.md: -------------------------------------------------------------------------------- 1 | ### Federation 2 | 3 | [Read the docs](https://gqlgen.com/recipes/federation/) 4 | -------------------------------------------------------------------------------- /docs/static/request_anatomy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/si3nloong/gqlgen/master/docs/static/request_anatomy.png -------------------------------------------------------------------------------- /_examples/chat/.gqlgen.yml: -------------------------------------------------------------------------------- 1 | models: 2 | Chatroom: 3 | model: github.com/99designs/gqlgen/_examples/chat.Chatroom 4 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/introspection/it.go: -------------------------------------------------------------------------------- 1 | package introspection 2 | 3 | type It struct { 4 | ID string 5 | } 6 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/model.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package subdir 4 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/introspection/it.go: -------------------------------------------------------------------------------- 1 | package introspection 2 | 3 | type It struct { 4 | ID string 5 | } 6 | -------------------------------------------------------------------------------- /integration/remote_api/user.go: -------------------------------------------------------------------------------- 1 | package remote_api 2 | 3 | type User struct { 4 | Name string 5 | Likes []string 6 | } 7 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/gendir/model.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package gendir 4 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/loops.graphql: -------------------------------------------------------------------------------- 1 | type LoopA { 2 | b: LoopB! 3 | } 4 | 5 | type LoopB { 6 | a: LoopA! 7 | } 8 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/loops.graphql: -------------------------------------------------------------------------------- 1 | type LoopA { 2 | b: LoopB! 3 | } 4 | 5 | type LoopB { 6 | a: LoopA! 7 | } 8 | -------------------------------------------------------------------------------- /graphql/root.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | type Query struct{} 4 | 5 | type Mutation struct{} 6 | 7 | type Subscription struct{} 8 | -------------------------------------------------------------------------------- /_examples/selection/.gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: schema.graphql 2 | model: 3 | filename: models_gen.go 4 | exec: 5 | filename: generated.go 6 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/enum-extension.graphql: -------------------------------------------------------------------------------- 1 | directive @enumLogging on ENUM 2 | 3 | extend enum State @enumLogging 4 | -------------------------------------------------------------------------------- /internal/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package main 5 | 6 | import ( 7 | _ "github.com/matryer/moq" 8 | ) 9 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/union-extension.graphql: -------------------------------------------------------------------------------- 1 | directive @unionLogging on UNION 2 | 3 | extend union Data @unionLogging 4 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/ptr_to_slice.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | type PtrToSliceContainer struct { 4 | PtrToSlice *[]string 5 | } 6 | -------------------------------------------------------------------------------- /internal/code/testdata/b/b.go: -------------------------------------------------------------------------------- 1 | package b 2 | 3 | import "github.com/99designs/gqlgen/internal/code/testdata/a" 4 | 5 | var B = a.A + " B" 6 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/object-extension.graphql: -------------------------------------------------------------------------------- 1 | directive @objectLogging on OBJECT 2 | 3 | extend type Todo @objectLogging 4 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/scalar-extension.graphql: -------------------------------------------------------------------------------- 1 | directive @scalarLogging on SCALAR 2 | 3 | extend scalar ID @scalarLogging 4 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/ptr_to_slice.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | type PtrToSliceContainer struct { 4 | PtrToSlice *[]string 5 | } 6 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/recursive.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | type RecursiveInputSlice struct { 4 | Self []RecursiveInputSlice 5 | } 6 | -------------------------------------------------------------------------------- /plugin/federation/testdata/entities/nokey.graphql: -------------------------------------------------------------------------------- 1 | type Hello { 2 | name: String! 3 | } 4 | 5 | type Query { 6 | hello: Hello! 7 | } 8 | 9 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/schema.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | resolver: Resolver! 3 | } 4 | 5 | type Resolver { 6 | name: String! 7 | } 8 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/recursive.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | type RecursiveInputSlice struct { 4 | Self []RecursiveInputSlice 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /codegen/templates/data.go linguist-generated 2 | /_examples/dataloader/*_gen.go linguist-generated 3 | generated.go linguist-generated 4 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/fields_order.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | type FieldsOrderInput struct { 4 | FirstField *string `json:"firstField"` 5 | } 6 | -------------------------------------------------------------------------------- /docs/layouts/404.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |

Page not found

3 | 4 | I'm sorry, but the requested page wasn’t found on the server. 5 | {{ end }} -------------------------------------------------------------------------------- /internal/code/testdata/c/c.go: -------------------------------------------------------------------------------- 1 | package c 2 | 3 | import ( 4 | "github.com/99designs/gqlgen/internal/code/testdata/b" 5 | ) 6 | 7 | var C = b.B + " C" 8 | -------------------------------------------------------------------------------- /plugin/federation/testdata/interfaces/key.graphqls: -------------------------------------------------------------------------------- 1 | extend interface Hello @key(fields: "name") { 2 | name: String! 3 | secondary: String! 4 | } 5 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/input-object-extension.graphql: -------------------------------------------------------------------------------- 1 | directive @inputLogging on INPUT_OBJECT 2 | 3 | extend input TodoInput @inputLogging 4 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/interface-extension.graphql: -------------------------------------------------------------------------------- 1 | directive @interfaceLogging on INTERFACE 2 | 3 | extend interface Node @interfaceLogging 4 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/fields_order.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | type FieldsOrderInput struct { 4 | FirstField *string `json:"firstField"` 5 | } 6 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/invalid-packagename/invalid-identifier.go: -------------------------------------------------------------------------------- 1 | package invalid_packagename 2 | 3 | type InvalidIdentifier struct { 4 | ID int 5 | } 6 | -------------------------------------------------------------------------------- /docs/content/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | linkTitle: Introduction 3 | title: Type-safe GraphQL for Go 4 | type: homepage 5 | date: 2018-03-17T13:06:47+11:00 6 | --- 7 | -------------------------------------------------------------------------------- /integration/codegen.yml: -------------------------------------------------------------------------------- 1 | schema: http://localhost:8080/query 2 | overwrite: true 3 | generates: 4 | ./schema-fetched.graphql: 5 | plugins: 6 | - schema-ast -------------------------------------------------------------------------------- /codegen/testserver/followschema/invalid-packagename/invalid-identifier.go: -------------------------------------------------------------------------------- 1 | package invalid_packagename 2 | 3 | type InvalidIdentifier struct { 4 | ID int 5 | } 6 | -------------------------------------------------------------------------------- /codegen/testserver/empty.go: -------------------------------------------------------------------------------- 1 | package testserver 2 | 3 | // Empty file to silence go build error complaining that codegen/testserver/ has no non-test Go source files. 4 | -------------------------------------------------------------------------------- /integration/testomitempty/testmodel.go: -------------------------------------------------------------------------------- 1 | package testomitempty 2 | 3 | type RemoteModelWithOmitempty struct { 4 | Description string `json:"newDesc,omitempty"` 5 | } 6 | -------------------------------------------------------------------------------- /_examples/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package main 5 | 6 | import ( 7 | _ "github.com/vektah/dataloaden" 8 | _ "golang.org/x/text" 9 | ) 10 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/type-extension.graphql: -------------------------------------------------------------------------------- 1 | directive @fieldLogging on FIELD_DEFINITION 2 | 3 | extend type Todo { 4 | verified: Boolean! @fieldLogging 5 | } 6 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/variadic.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | variadicModel: VariadicModel 3 | } 4 | 5 | type VariadicModel { 6 | value(rank: Int!): String 7 | } 8 | -------------------------------------------------------------------------------- /integration/models-go/viewer.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "github.com/99designs/gqlgen/integration/remote_api" 4 | 5 | type Viewer struct { 6 | User *remote_api.User 7 | } 8 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/variadic.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | variadicModel: VariadicModel 3 | } 4 | 5 | type VariadicModel { 6 | value(rank: Int!): String 7 | } 8 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/return_values/schema.graphqls: -------------------------------------------------------------------------------- 1 | type User { 2 | id: ID! 3 | name: String! 4 | } 5 | 6 | type Query { 7 | user: User! 8 | userPointer: User 9 | } 10 | -------------------------------------------------------------------------------- /integration/models-go/element.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Element struct { 4 | ID int 5 | } 6 | 7 | func (e *Element) Mismatched() []Element { 8 | return []Element{*e} 9 | } 10 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/ptr_to_slice.graphql: -------------------------------------------------------------------------------- 1 | type PtrToSliceContainer { 2 | ptrToSlice: [String!] 3 | } 4 | 5 | extend type Query { 6 | ptrToSliceContainer: PtrToSliceContainer! 7 | } 8 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/ptr_to_slice.graphql: -------------------------------------------------------------------------------- 1 | type PtrToSliceContainer { 2 | ptrToSlice: [String!] 3 | } 4 | 5 | extend type Query { 6 | ptrToSliceContainer: PtrToSliceContainer! 7 | } 8 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/invalid_model_path/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/schema.graphql" 3 | 4 | models: 5 | Resolver: 6 | model: github.com/99designs/invalid/invalid/invalid/nope.Resolver 7 | -------------------------------------------------------------------------------- /_examples/dataloader/.gqlgen.yml: -------------------------------------------------------------------------------- 1 | models: 2 | Order: 3 | model: github.com/99designs/gqlgen/_examples/dataloader.Order 4 | Customer: 5 | model: github.com/99designs/gqlgen/_examples/dataloader.Customer 6 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/glob/foo/foo.graphql: -------------------------------------------------------------------------------- 1 | type Todo { 2 | id: ID! 3 | text: String! 4 | done: Boolean! 5 | user: User! 6 | } 7 | 8 | type User { 9 | id: ID! 10 | name: String! 11 | } 12 | -------------------------------------------------------------------------------- /plugin/federation/testdata/schema/customquerytype.graphql: -------------------------------------------------------------------------------- 1 | schema { 2 | query: CustomQuery 3 | } 4 | 5 | type Hello { 6 | name: String! 7 | } 8 | 9 | type CustomQuery { 10 | hello: Hello! 11 | } 12 | 13 | -------------------------------------------------------------------------------- /testdata/gomod-with-leading-comments.mod: -------------------------------------------------------------------------------- 1 | // main module of gqlgen 2 | 3 | // and another module to test stripping of comment lines 4 | 5 | module github.com/99designs/gqlgen // replace it for new project 6 | 7 | go 1.18 8 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/glob.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - testdata/cfg/glob/**/*.graphql 3 | exec: 4 | filename: generated.go 5 | model: 6 | filename: models_gen.go 7 | resolver: 8 | filename: resolver.go 9 | type: Resolver 10 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/useptr.graphql: -------------------------------------------------------------------------------- 1 | type A { 2 | id: ID! 3 | } 4 | 5 | type B { 6 | id: ID! 7 | } 8 | 9 | union TestUnion = A | B 10 | 11 | extend type Query { 12 | optionalUnion: TestUnion 13 | } 14 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/builtinscalar.graphql: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Since gqlgen defines default implementation for a Map scalar, this tests that the builtin is _not_ 4 | added to the TypeMap 5 | """ 6 | type Map { 7 | id: ID! 8 | } 9 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/useptr.graphql: -------------------------------------------------------------------------------- 1 | type A { 2 | id: ID! 3 | } 4 | 5 | type B { 6 | id: ID! 7 | } 8 | 9 | union TestUnion = A | B 10 | 11 | extend type Query { 12 | optionalUnion: TestUnion 13 | } 14 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/unwalkable.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - not_walkable/**/*.graphql 3 | exec: 4 | filename: generated.go 5 | model: 6 | filename: models_gen.go 7 | resolver: 8 | filename: resolver.go 9 | type: Resolver 10 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/builtinscalar.graphql: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Since gqlgen defines default implementation for a Map scalar, this tests that the builtin is _not_ 4 | added to the TypeMap 5 | """ 6 | type Map { 7 | id: ID! 8 | } 9 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/typefallback.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | fallback(arg: FallbackToStringEncoding!): FallbackToStringEncoding! 3 | } 4 | 5 | enum FallbackToStringEncoding { 6 | A 7 | B 8 | C 9 | } 10 | -------------------------------------------------------------------------------- /plugin/federation/testdata/interfaces/key.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/interfaces/key.graphqls" 3 | exec: 4 | filename: testdata/interfaces/generated/exec.go 5 | federation: 6 | filename: testdata/interfaces/generated/federation.go 7 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/return_values/model.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package return_values 4 | 5 | type User struct { 6 | ID string `json:"id"` 7 | Name string `json:"name"` 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What happened? 2 | 3 | ### What did you expect? 4 | 5 | ### Minimal graphql.schema and models to reproduce 6 | 7 | ### versions 8 | - `go run github.com/99designs/gqlgen version`? 9 | - `go version`? 10 | -------------------------------------------------------------------------------- /_examples/todo/readme.md: -------------------------------------------------------------------------------- 1 | ### todo app 2 | 3 | This is the simplest example of a graphql server. 4 | 5 | to run this server 6 | ```bash 7 | go run ./server/server.go 8 | ``` 9 | 10 | and open http://localhost:8081 in your browser 11 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/otherpkg/model.go: -------------------------------------------------------------------------------- 1 | package otherpkg 2 | 3 | type ( 4 | Scalar string 5 | Map map[string]string 6 | Slice []string 7 | ) 8 | 9 | type Struct struct { 10 | Name Scalar 11 | Desc *Scalar 12 | } 13 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/typefallback.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | fallback(arg: FallbackToStringEncoding!): FallbackToStringEncoding! 3 | } 4 | 5 | enum FallbackToStringEncoding { 6 | A 7 | B 8 | C 9 | } 10 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/otherpkg/model.go: -------------------------------------------------------------------------------- 1 | package otherpkg 2 | 3 | type ( 4 | Scalar string 5 | Map map[string]string 6 | Slice []string 7 | ) 8 | 9 | type Struct struct { 10 | Name Scalar 11 | Desc *Scalar 12 | } 13 | -------------------------------------------------------------------------------- /plugin/federation/testdata/interfaces/extends.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/interfaces/extends.graphqls" 3 | exec: 4 | filename: testdata/interfaces/generated/exec.go 5 | federation: 6 | filename: testdata/interfaces/generated/federation.go 7 | -------------------------------------------------------------------------------- /_examples/federation/reviews/graph/resolver.go: -------------------------------------------------------------------------------- 1 | // This file will not be regenerated automatically. 2 | // 3 | // It serves as dependency injection for your app, add any dependencies you require here. 4 | package graph 5 | 6 | type Resolver struct{} 7 | -------------------------------------------------------------------------------- /_examples/federation/accounts/graph/resolver.go: -------------------------------------------------------------------------------- 1 | // This file will not be regenerated automatically. 2 | // 3 | // It serves as dependency injection for your app, add any dependencies you require here. 4 | package graph 5 | 6 | type Resolver struct{} 7 | -------------------------------------------------------------------------------- /_examples/federation/products/graph/resolver.go: -------------------------------------------------------------------------------- 1 | // This file will not be regenerated automatically. 2 | // 3 | // It serves as dependency injection for your app, add any dependencies you require here. 4 | package graph 5 | 6 | type Resolver struct{} 7 | -------------------------------------------------------------------------------- /_examples/selection/readme.md: -------------------------------------------------------------------------------- 1 | ### selection app 2 | 3 | This is the simplest example of a graphql server. 4 | 5 | to run this server 6 | ```bash 7 | go run ./server/server.go 8 | ``` 9 | 10 | and open http://localhost:8086 in your browser 11 | -------------------------------------------------------------------------------- /_examples/config/todo.graphql: -------------------------------------------------------------------------------- 1 | type Todo { 2 | id: ID! @goField(forceResolver: true) 3 | databaseId: Int! 4 | text: String! 5 | done: Boolean! 6 | user: User! 7 | } 8 | 9 | input NewTodo { 10 | text: String! 11 | userId: String! 12 | } 13 | -------------------------------------------------------------------------------- /codegen/config/testdata/cfg/glob/bar/bar with spaces.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | todos: [Todo!]! 3 | } 4 | 5 | input NewTodo { 6 | text: String! 7 | userId: String! 8 | } 9 | 10 | type Mutation { 11 | createTodo(input: NewTodo!): Todo! 12 | } 13 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/enum.graphql: -------------------------------------------------------------------------------- 1 | enum EnumTest { 2 | OK 3 | NG 4 | } 5 | 6 | input InputWithEnumValue { 7 | enum: EnumTest! 8 | } 9 | 10 | extend type Query { 11 | enumInInput(input: InputWithEnumValue): EnumTest! 12 | } 13 | -------------------------------------------------------------------------------- /_examples/readme.md: -------------------------------------------------------------------------------- 1 | ### examples 2 | 3 | - todo: A simple todo checklist. A good place to get the basics down 4 | - starwars: A starwars movie database. It has examples of advanced graphql features 5 | - dataloader: How to avoid n+1 database query problems 6 | -------------------------------------------------------------------------------- /_examples/websocket-initfunc/server/graph/resolver.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will not be regenerated automatically. 4 | // 5 | // It serves as dependency injection for your app, add any dependencies you require here. 6 | 7 | type Resolver struct{} 8 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/enum.graphql: -------------------------------------------------------------------------------- 1 | enum EnumTest { 2 | OK 3 | NG 4 | } 5 | 6 | input InputWithEnumValue { 7 | enum: EnumTest! 8 | } 9 | 10 | extend type Query { 11 | enumInInput(input: InputWithEnumValue): EnumTest! 12 | } 13 | -------------------------------------------------------------------------------- /codegen/complexity.go: -------------------------------------------------------------------------------- 1 | package codegen 2 | 3 | func (o *Object) UniqueFields() map[string][]*Field { 4 | m := map[string][]*Field{} 5 | 6 | for _, f := range o.Fields { 7 | m[f.GoFieldName] = append(m[f.GoFieldName], f) 8 | } 9 | 10 | return m 11 | } 12 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/return_values/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - schema.graphqls 3 | 4 | exec: 5 | filename: ignored.go 6 | model: 7 | filename: model.go 8 | resolver: 9 | filename: resolvers.go 10 | 11 | resolvers_always_return_pointers: false 12 | -------------------------------------------------------------------------------- /plugin/federation/testdata/entityresolver/resolver.go: -------------------------------------------------------------------------------- 1 | package entityresolver 2 | 3 | // This file will not be regenerated automatically. 4 | // 5 | // It serves as dependency injection for your app, add any dependencies you require here. 6 | 7 | type Resolver struct{} 8 | -------------------------------------------------------------------------------- /_examples/websocket-initfunc/server/graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | type Dummy struct { 6 | ID string `json:"id"` 7 | Text string `json:"text"` 8 | Done bool `json:"done"` 9 | } 10 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/weird_type_cases.graphql: -------------------------------------------------------------------------------- 1 | # regression test for https://github.com/99designs/gqlgen/issues/583 2 | 3 | type asdfIt { id: ID! } 4 | type iIt { id: ID! } 5 | type AIt { id: ID! } 6 | type XXIt { id: ID! } 7 | type AbIt { id: ID! } 8 | type XxIt { id: ID! } 9 | -------------------------------------------------------------------------------- /plugin/federation/testdata/entityresolver/schema.resolvers.go: -------------------------------------------------------------------------------- 1 | package entityresolver 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/filetemplate/out/resolver.go: -------------------------------------------------------------------------------- 1 | package customresolver 2 | 3 | // This file will not be regenerated automatically. 4 | // 5 | // It serves as dependency injection for your app, add any dependencies you require here. 6 | 7 | type CustomResolverType struct{} 8 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/followschema/out/resolver.go: -------------------------------------------------------------------------------- 1 | package customresolver 2 | 3 | // This file will not be regenerated automatically. 4 | // 5 | // It serves as dependency injection for your app, add any dependencies you require here. 6 | 7 | type CustomResolverType struct{} 8 | -------------------------------------------------------------------------------- /.github/workflows/check-fmt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | go fmt ./... 6 | cd _examples && go fmt ./... 7 | if [[ $(git --no-pager diff) ]] ; then 8 | echo "you need to run "go fmt" and commit the changes" 9 | git --no-pager diff 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /_examples/todo/gqlgen.yml: -------------------------------------------------------------------------------- 1 | models: 2 | Todo: 3 | model: github.com/99designs/gqlgen/_examples/todo.Todo 4 | ID: 5 | model: # override the default id marshaller to use ints 6 | - github.com/99designs/gqlgen/graphql.IntID 7 | - github.com/99designs/gqlgen/graphql.ID 8 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/slices.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | slices: Slices 3 | scalarSlice: Bytes! 4 | } 5 | 6 | type Slices { 7 | test1: [String] 8 | test2: [String!] 9 | test3: [String]! 10 | test4: [String!]! 11 | } 12 | 13 | scalar Bytes 14 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/weird_type_cases.graphql: -------------------------------------------------------------------------------- 1 | # regression test for https://github.com/99designs/gqlgen/issues/583 2 | 3 | type asdfIt { id: ID! } 4 | type iIt { id: ID! } 5 | type AIt { id: ID! } 6 | type XXIt { id: ID! } 7 | type AbIt { id: ID! } 8 | type XxIt { id: ID! } 9 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/slices.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | slices: Slices 3 | scalarSlice: Bytes! 4 | } 5 | 6 | type Slices { 7 | test1: [String] 8 | test2: [String!] 9 | test3: [String]! 10 | test4: [String!]! 11 | } 12 | 13 | scalar Bytes 14 | -------------------------------------------------------------------------------- /client/errors.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "encoding/json" 4 | 5 | // RawJsonError is a json formatted error from a GraphQL server. 6 | type RawJsonError struct { 7 | json.RawMessage 8 | } 9 | 10 | func (r RawJsonError) Error() string { 11 | return string(r.RawMessage) 12 | } 13 | -------------------------------------------------------------------------------- /_examples/dataloader/readme.md: -------------------------------------------------------------------------------- 1 | ### dataloader 2 | 3 | This example uses [dataloaden](https://github.com/vektah/dataloaden) to avoiding n+1 queries. 4 | 5 | 6 | There is also [nicksrandall/dataloader](https://github.com/nicksrandall/dataloader) if you wanted to avoid 7 | doing more codegeneration. 8 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/mutation_with_custom_scalar.graphql: -------------------------------------------------------------------------------- 1 | extend type Mutation { 2 | updateSomething(input: SpecialInput!): String! 3 | } 4 | 5 | scalar Email 6 | 7 | input SpecialInput { 8 | nesting: NestedInput! 9 | } 10 | 11 | input NestedInput { 12 | field: Email! 13 | } 14 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/scalar_context.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | infinity: Float! 3 | stringFromContextInterface: StringFromContextInterface! 4 | stringFromContextFunction: StringFromContextFunction! 5 | } 6 | 7 | scalar StringFromContextInterface 8 | scalar StringFromContextFunction 9 | -------------------------------------------------------------------------------- /codegen/config/testdata/autobinding/chat/message.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Message struct { 8 | ID string `json:"id"` 9 | Text string `json:"text"` 10 | CreatedBy string `json:"createdBy"` 11 | CreatedAt time.Time `json:"createdAt"` 12 | } 13 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/mutation_with_custom_scalar.graphql: -------------------------------------------------------------------------------- 1 | extend type Mutation { 2 | updateSomething(input: SpecialInput!): String! 3 | } 4 | 5 | scalar Email 6 | 7 | input SpecialInput { 8 | nesting: NestedInput! 9 | } 10 | 11 | input NestedInput { 12 | field: Email! 13 | } 14 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/scalar_context.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | infinity: Float! 3 | stringFromContextInterface: StringFromContextInterface! 4 | stringFromContextFunction: StringFromContextFunction! 5 | } 6 | 7 | scalar StringFromContextInterface 8 | scalar StringFromContextFunction 9 | -------------------------------------------------------------------------------- /plugin/federation/testdata/interfaces/extends.graphqls: -------------------------------------------------------------------------------- 1 | interface Hello @extends { 2 | name: String! 3 | secondary: String! 4 | } 5 | 6 | extend type World implements Hello @key(fields: "name") { 7 | name: String! @external 8 | secondary: String! 9 | 10 | tertiary: String! 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/check-generate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | go generate ./... 6 | cd _examples && go generate ./... 7 | if [[ $(git --no-pager diff) ]] ; then 8 | echo "you need to run "go generate ./..." and commit the changes" 9 | git --no-pager diff 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /graphql/oneshot.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import "context" 4 | 5 | func OneShot(resp *Response) ResponseHandler { 6 | var oneshot bool 7 | 8 | return func(context context.Context) *Response { 9 | if oneshot { 10 | return nil 11 | } 12 | oneshot = true 13 | 14 | return resp 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/schema-extension.graphql: -------------------------------------------------------------------------------- 1 | extend schema { 2 | mutation: MyMutation 3 | } 4 | 5 | extend type MyQuery { 6 | todo(id: ID!): Todo 7 | } 8 | 9 | type MyMutation { 10 | createTodo(todo: TodoInput!): Todo! 11 | } 12 | 13 | input TodoInput { 14 | text: String! 15 | } 16 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/wrapped_type.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import "github.com/99designs/gqlgen/codegen/testserver/singlefile/otherpkg" 4 | 5 | type ( 6 | WrappedScalar = otherpkg.Scalar 7 | WrappedStruct otherpkg.Struct 8 | WrappedMap otherpkg.Map 9 | WrappedSlice otherpkg.Slice 10 | ) 11 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/wrapped_type.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import "github.com/99designs/gqlgen/codegen/testserver/followschema/otherpkg" 4 | 5 | type ( 6 | WrappedScalar = otherpkg.Scalar 7 | WrappedStruct otherpkg.Struct 8 | WrappedMap otherpkg.Map 9 | WrappedSlice otherpkg.Slice 10 | ) 11 | -------------------------------------------------------------------------------- /integration/.graphqlconfig: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "Project Name": { 4 | "schemaPath": "schema-fetched.graphql", 5 | "extensions": { 6 | "endpoints": { 7 | "dev": { 8 | "url": "${env:SERVER_URL}" 9 | } 10 | } 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /_examples/config/user.graphql: -------------------------------------------------------------------------------- 1 | type User 2 | @goModel(model:"github.com/99designs/gqlgen/_examples/config.User") { 3 | id: ID! 4 | name: String! @goField(name:"FullName") 5 | role: role! 6 | } 7 | 8 | type role 9 | @goModel(model:"github.com/99designs/gqlgen/_examples/config.UserRole") { 10 | name: String! 11 | } 12 | -------------------------------------------------------------------------------- /_examples/starwars/readme.md: -------------------------------------------------------------------------------- 1 | ### starwars example 2 | 3 | This server demonstrates a few advanced features of graphql: 4 | - connections 5 | - unions 6 | - interfaces 7 | - enums 8 | 9 | to run this server 10 | ```bash 11 | go run ./server/server.go 12 | ``` 13 | 14 | and open http://localhost:8080 in your browser 15 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/panics.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | panics: Panics 3 | } 4 | 5 | type Panics { 6 | fieldScalarMarshal: [MarshalPanic!]! 7 | fieldFuncMarshal(u: [MarshalPanic!]!): [MarshalPanic!]! 8 | argUnmarshal(u: [MarshalPanic!]!): Boolean! 9 | 10 | } 11 | 12 | scalar MarshalPanic 13 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/panics.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | panics: Panics 3 | } 4 | 5 | type Panics { 6 | fieldScalarMarshal: [MarshalPanic!]! 7 | fieldFuncMarshal(u: [MarshalPanic!]!): [MarshalPanic!]! 8 | argUnmarshal(u: [MarshalPanic!]!): Boolean! 9 | 10 | } 11 | 12 | scalar MarshalPanic 13 | -------------------------------------------------------------------------------- /plugin/federation/testdata/entities/nokey.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/entities/nokey.graphql" 3 | exec: 4 | filename: testdata/entities/generated/exec.go 5 | federation: 6 | filename: testdata/entities/generated/federation.go 7 | 8 | autobind: 9 | - "github.com/99designs/gqlgen/plugin/federation/test_data/model" 10 | -------------------------------------------------------------------------------- /_examples/federation/accounts/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | me: User 3 | } 4 | 5 | type EmailHost @key(fields: "id") { 6 | id: String! 7 | name: String! 8 | } 9 | 10 | type User @key(fields: "id") { 11 | id: ID! 12 | host: EmailHost! 13 | email: String! 14 | username: String! 15 | } 16 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/v-ok.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | vOkCaseValue: VOkCaseValue 3 | vOkCaseNil: VOkCaseNil 4 | } 5 | 6 | type VOkCaseValue @goModel(model:"singlefile.VOkCaseValue") { 7 | value: String 8 | } 9 | 10 | type VOkCaseNil @goModel(model:"singlefile.VOkCaseNil") { 11 | value: String 12 | } 13 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/v-ok.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | vOkCaseValue: VOkCaseValue 3 | vOkCaseNil: VOkCaseNil 4 | } 5 | 6 | type VOkCaseValue @goModel(model:"followschema.VOkCaseValue") { 7 | value: String 8 | } 9 | 10 | type VOkCaseNil @goModel(model:"followschema.VOkCaseNil") { 11 | value: String 12 | } 13 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/fields_order.graphql: -------------------------------------------------------------------------------- 1 | type FieldsOrderPayload { 2 | firstFieldValue: String 3 | } 4 | 5 | input FieldsOrderInput { 6 | firstField: String 7 | overrideFirstField: String 8 | } 9 | 10 | extend type Mutation { 11 | overrideValueViaInput(input: FieldsOrderInput!): FieldsOrderPayload! 12 | } 13 | -------------------------------------------------------------------------------- /plugin/federation/testdata/schema/customquerytype.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/schema/customquerytype.graphql" 3 | exec: 4 | filename: testdata/schema/generated/exec.go 5 | federation: 6 | filename: testdata/schema/generated/federation.go 7 | 8 | autobind: 9 | - "github.com/99designs/gqlgen/plugin/federation/test_data/model" 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Describe your PR and link to any relevant issues. 2 | 3 | I have: 4 | - [ ] Added tests covering the bug / feature (see [testing](https://github.com/99designs/gqlgen/blob/master/TESTING.md)) 5 | - [ ] Updated any relevant documentation (see [docs](https://github.com/99designs/gqlgen/tree/master/docs/content)) 6 | -------------------------------------------------------------------------------- /_examples/dataloader/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package dataloader 4 | 5 | type Address struct { 6 | ID int `json:"id"` 7 | Street string `json:"street"` 8 | Country string `json:"country"` 9 | } 10 | 11 | type Item struct { 12 | Name string `json:"name"` 13 | } 14 | -------------------------------------------------------------------------------- /_examples/todo/models.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | type Ownable interface { 4 | Owner() *User 5 | } 6 | 7 | type Todo struct { 8 | ID int 9 | Text string 10 | Done bool 11 | owner *User 12 | } 13 | 14 | func (t Todo) Owner() *User { 15 | return t.owner 16 | } 17 | 18 | type User struct { 19 | ID int 20 | Name string 21 | } 22 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/fields_order.graphql: -------------------------------------------------------------------------------- 1 | type FieldsOrderPayload { 2 | firstFieldValue: String 3 | } 4 | 5 | input FieldsOrderInput { 6 | firstField: String 7 | overrideFirstField: String 8 | } 9 | 10 | extend type Mutation { 11 | overrideValueViaInput(input: FieldsOrderInput!): FieldsOrderPayload! 12 | } 13 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/complexity.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | overlapping: OverlappingFields 3 | } 4 | 5 | type OverlappingFields { 6 | oneFoo: Int! @goField(name: "foo") 7 | twoFoo: Int! @goField(name: "foo") 8 | oldFoo: Int! @goField(name: "foo", forceResolver: true) 9 | newFoo: Int! 10 | new_foo: Int! 11 | } 12 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/complexity.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | overlapping: OverlappingFields 3 | } 4 | 5 | type OverlappingFields { 6 | oneFoo: Int! @goField(name: "foo") 7 | twoFoo: Int! @goField(name: "foo") 8 | oldFoo: Int! @goField(name: "foo", forceResolver: true) 9 | newFoo: Int! 10 | new_foo: Int! 11 | } 12 | -------------------------------------------------------------------------------- /integration/readme.md: -------------------------------------------------------------------------------- 1 | # Integration tests 2 | 3 | These tests run a gqlgen server against the apollo client to test real world connectivity. 4 | 5 | First start the go server 6 | ```bash 7 | go run integration/server/server.go 8 | ``` 9 | 10 | And in another terminal: 11 | ```bash 12 | cd integration 13 | npm ci 14 | npm run test 15 | ``` 16 | -------------------------------------------------------------------------------- /_examples/starwars/.gqlgen.yml: -------------------------------------------------------------------------------- 1 | exec: 2 | filename: generated/exec.go 3 | model: 4 | filename: models/generated.go 5 | 6 | autobind: 7 | - github.com/99designs/gqlgen/_examples/starwars/models 8 | 9 | models: 10 | ReviewInput: 11 | model: models.Review 12 | Starship: 13 | fields: 14 | length: 15 | resolver: true 16 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/v-ok.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | // VOkCaseValue model 4 | type VOkCaseValue struct{} 5 | 6 | func (v VOkCaseValue) Value() (string, bool) { 7 | return "hi", true 8 | } 9 | 10 | // VOkCaseNil model 11 | type VOkCaseNil struct{} 12 | 13 | func (v VOkCaseNil) Value() (string, bool) { 14 | return "", false 15 | } 16 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/filetemplate/out/model.go: -------------------------------------------------------------------------------- 1 | package customresolver 2 | 3 | import "context" 4 | 5 | type Resolver struct{} 6 | 7 | type QueryResolver interface { 8 | Resolver(ctx context.Context) (*Resolver, error) 9 | } 10 | 11 | type ResolverResolver interface { 12 | Name(ctx context.Context, obj *Resolver) (string, error) 13 | } 14 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/followschema/out/model.go: -------------------------------------------------------------------------------- 1 | package customresolver 2 | 3 | import "context" 4 | 5 | type Resolver struct{} 6 | 7 | type QueryResolver interface { 8 | Resolver(ctx context.Context) (*Resolver, error) 9 | } 10 | 11 | type ResolverResolver interface { 12 | Name(ctx context.Context, obj *Resolver) (string, error) 13 | } 14 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/singlefile/out/model.go: -------------------------------------------------------------------------------- 1 | package customresolver 2 | 3 | import "context" 4 | 5 | type Resolver struct{} 6 | 7 | type QueryResolver interface { 8 | Resolver(ctx context.Context) (*Resolver, error) 9 | } 10 | 11 | type ResolverResolver interface { 12 | Name(ctx context.Context, obj *Resolver) (string, error) 13 | } 14 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/cfgdir/generate_in_subdir.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - schemadir/*.graphqls 3 | - ../*.graphqls 4 | - '*.graphqls' 5 | exec: 6 | layout: follow-schema 7 | dir: . 8 | package: subdir 9 | 10 | federation: 11 | filename: federation_gen.go 12 | package: subdir 13 | 14 | model: 15 | filename: model.go 16 | package: subdir -------------------------------------------------------------------------------- /_examples/scalars/model/generated.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | import ( 6 | "github.com/99designs/gqlgen/_examples/scalars/external" 7 | ) 8 | 9 | type Address struct { 10 | ID external.ObjectID `json:"id"` 11 | Location *Point `json:"location,omitempty"` 12 | } 13 | -------------------------------------------------------------------------------- /_examples/websocket-initfunc/server/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | # GraphQL schema example 2 | # 3 | 4 | type Dummy { 5 | id: ID! 6 | text: String! 7 | done: Boolean! 8 | } 9 | 10 | type Mutation { 11 | postMessageTo(subscriber: String!, content: String!): ID! 12 | } 13 | 14 | type Subscription { 15 | subscribe(subscriber: String!): String! 16 | } 17 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/v-ok.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | // VOkCaseValue model 4 | type VOkCaseValue struct{} 5 | 6 | func (v VOkCaseValue) Value() (string, bool) { 7 | return "hi", true 8 | } 9 | 10 | // VOkCaseNil model 11 | type VOkCaseNil struct{} 12 | 13 | func (v VOkCaseNil) Value() (string, bool) { 14 | return "", false 15 | } 16 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/useptr_test.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestUserPtr(t *testing.T) { 11 | s := &Stub{} 12 | r := reflect.TypeOf(s.QueryResolver.OptionalUnion) 13 | require.True(t, r.Out(0).Kind() == reflect.Interface) 14 | } 15 | -------------------------------------------------------------------------------- /integration/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: "node", 3 | testMatch: ["/**/*-test.js"], 4 | testPathIgnorePatterns: ["/node_modules/"], 5 | moduleFileExtensions: ["js"], 6 | modulePaths: ["/node_modules"], 7 | transform: { 8 | '^.+\\.jsx?$': 'babel-jest', 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /plugin/federation/testdata/allthethings/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/allthethings/schema.graphql" 3 | exec: 4 | filename: testdata/allthethings/generated/exec.go 5 | federation: 6 | filename: testdata/allthethings/generated/federation.go 7 | 8 | autobind: 9 | - "github.com/99designs/gqlgen/plugin/federation/testdata/allthethings/model" 10 | -------------------------------------------------------------------------------- /_examples/chat/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package chat 4 | 5 | import ( 6 | "time" 7 | ) 8 | 9 | type Message struct { 10 | ID string `json:"id"` 11 | Text string `json:"text"` 12 | CreatedBy string `json:"createdBy"` 13 | CreatedAt time.Time `json:"createdAt"` 14 | } 15 | -------------------------------------------------------------------------------- /_examples/type-system-extension/gqlgen.yml: -------------------------------------------------------------------------------- 1 | # .gqlgen.yml example 2 | # 3 | # Refer to https://gqlgen.com/config/ 4 | # for detailed .gqlgen.yml documentation. 5 | 6 | schema: 7 | - ./schemas/*.graphql 8 | 9 | exec: 10 | filename: generated.go 11 | model: 12 | filename: models_gen.go 13 | resolver: 14 | filename: resolver.go 15 | type: Resolver 16 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/primitive_objects.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | primitiveObject: [Primitive!]! 3 | primitiveStringObject: [PrimitiveString!]! 4 | } 5 | 6 | type Primitive { 7 | value: Int! 8 | squared: Int! 9 | } 10 | 11 | type PrimitiveString { 12 | value: String! 13 | doubled: String! 14 | len: Int! 15 | } 16 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/useptr_test.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestUserPtr(t *testing.T) { 11 | s := &Stub{} 12 | r := reflect.TypeOf(s.QueryResolver.OptionalUnion) 13 | require.True(t, r.Out(0).Kind() == reflect.Interface) 14 | } 15 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/primitive_objects.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | primitiveObject: [Primitive!]! 3 | primitiveStringObject: [PrimitiveString!]! 4 | } 5 | 6 | type Primitive { 7 | value: Int! 8 | squared: Int! 9 | } 10 | 11 | type PrimitiveString { 12 | value: String! 13 | doubled: String! 14 | len: Int! 15 | } 16 | -------------------------------------------------------------------------------- /_examples/federation/reviews/graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | type EmailHost struct { 6 | ID string `json:"id"` 7 | } 8 | 9 | func (EmailHost) IsEntity() {} 10 | 11 | type Manufacturer struct { 12 | ID string `json:"id"` 13 | } 14 | 15 | func (Manufacturer) IsEntity() {} 16 | -------------------------------------------------------------------------------- /plugin/federation/testdata/federation2/federation2.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/federation2/federation2.graphql" 3 | exec: 4 | filename: testdata/federation2/generated/exec.go 5 | federation: 6 | filename: testdata/federation2/generated/federation.go 7 | version: 2 8 | 9 | autobind: 10 | - "github.com/99designs/gqlgen/plugin/federation/test_data/model" 11 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/variadic.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | ) 7 | 8 | type VariadicModel struct{} 9 | 10 | type VariadicModelOption func(*VariadicModel) 11 | 12 | func (v VariadicModel) Value(ctx context.Context, rank int, opts ...VariadicModelOption) (string, error) { 13 | return strconv.Itoa(rank), nil 14 | } 15 | -------------------------------------------------------------------------------- /_examples/config/schema.graphql: -------------------------------------------------------------------------------- 1 | directive @goModel(model: String, models: [String!]) on OBJECT | INPUT_OBJECT | SCALAR | ENUM | INTERFACE | UNION 2 | directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION 3 | 4 | type Query { 5 | todos: [Todo!]! 6 | } 7 | 8 | type Mutation { 9 | createTodo(input: NewTodo!): Todo! 10 | } 11 | -------------------------------------------------------------------------------- /_examples/federation/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: "node", 3 | testMatch: ["/**/*-test.js"], 4 | testPathIgnorePatterns: ["/node_modules/"], 5 | moduleFileExtensions: ["js"], 6 | modulePaths: ["/node_modules"], 7 | // transform: { 8 | // '^.+\\.jsx?$': 'babel-jest', 9 | // }, 10 | }; 11 | -------------------------------------------------------------------------------- /client/readme.md: -------------------------------------------------------------------------------- 1 | This client is used internally for testing. I wanted a simple graphql client sent user specified queries. 2 | 3 | You might want to look at: 4 | - https://github.com/shurcooL/graphql: Uses reflection to build queries from structs. 5 | - https://github.com/machinebox/graphql: Probably would have been a perfect fit, but it uses form encoding instead of json... 6 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/variadic.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | ) 7 | 8 | type VariadicModel struct{} 9 | 10 | type VariadicModelOption func(*VariadicModel) 11 | 12 | func (v VariadicModel) Value(ctx context.Context, rank int, opts ...VariadicModelOption) (string, error) { 13 | return strconv.Itoa(rank), nil 14 | } 15 | -------------------------------------------------------------------------------- /_examples/config/model.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "fmt" 4 | 5 | type User struct { 6 | ID string 7 | FirstName, LastName string 8 | Role UserRole 9 | } 10 | 11 | func (user *User) FullName() string { 12 | return fmt.Sprintf("%s %s", user.FirstName, user.LastName) 13 | } 14 | 15 | type UserRole struct { 16 | RoleName string 17 | } 18 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/cfgdir/generate_in_gendir.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - schemadir/*.graphqls 3 | - ../*.graphqls 4 | - '*.graphqls' 5 | exec: 6 | dir: gendir 7 | filename: gendir/generated.go 8 | package: gendir 9 | 10 | federation: 11 | filename: gendir/federation_gen.go 12 | package: gendir 13 | 14 | model: 15 | filename: gendir/model.go 16 | package: gendir -------------------------------------------------------------------------------- /graphql/any.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | func MarshalAny(v interface{}) Marshaler { 9 | return WriterFunc(func(w io.Writer) { 10 | err := json.NewEncoder(w).Encode(v) 11 | if err != nil { 12 | panic(err) 13 | } 14 | }) 15 | } 16 | 17 | func UnmarshalAny(v interface{}) (interface{}, error) { 18 | return v, nil 19 | } 20 | -------------------------------------------------------------------------------- /graphql/context_path_test.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestGetFieldInputContext(t *testing.T) { 11 | require.Nil(t, GetFieldContext(context.Background())) 12 | 13 | rc := &PathContext{} 14 | require.Equal(t, rc, GetPathContext(WithPathContext(context.Background(), rc))) 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /docs/public 3 | /docs/.hugo_build.lock 4 | /_examples/chat/node_modules 5 | /integration/node_modules 6 | /integration/schema-fetched.graphql 7 | /_examples/chat/package-lock.json 8 | /_examples/federation/package-lock.json 9 | /_examples/federation/node_modules 10 | /codegen/gen 11 | /gen 12 | 13 | /.vscode 14 | .idea/ 15 | *.test 16 | *.out 17 | gqlgen 18 | *.exe 19 | -------------------------------------------------------------------------------- /_examples/websocket-initfunc/server/Makefile: -------------------------------------------------------------------------------- 1 | bin_name=server 2 | 3 | build: 4 | @echo "building binary..." 5 | # go generate gives missing go sum entry for module errors 6 | # https://github.com/99designs/gqlgen/issues/1483 7 | # you will need to first do a go get -u github.com/99designs/gqlgen 8 | go run -mod=mod github.com/99designs/gqlgen generate . 9 | go build -o ${bin_name} server.go 10 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/scalar_default.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | defaultScalar(arg: DefaultScalarImplementation! = "default"): DefaultScalarImplementation! 3 | } 4 | 5 | """ This doesnt have an implementation in the typemap, so it should act like a string """ 6 | scalar DefaultScalarImplementation 7 | 8 | type EmbeddedDefaultScalar { 9 | value: DefaultScalarImplementation 10 | } 11 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/scalar_default.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | defaultScalar(arg: DefaultScalarImplementation! = "default"): DefaultScalarImplementation! 3 | } 4 | 5 | """ This doesnt have an implementation in the typemap, so it should act like a string """ 6 | scalar DefaultScalarImplementation 7 | 8 | type EmbeddedDefaultScalar { 9 | value: DefaultScalarImplementation 10 | } 11 | -------------------------------------------------------------------------------- /graphql/context_root_field_test.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestGetRootFieldContext(t *testing.T) { 11 | require.Nil(t, GetRootFieldContext(context.Background())) 12 | 13 | rc := &RootFieldContext{} 14 | require.Equal(t, rc, GetRootFieldContext(WithRootFieldContext(context.Background(), rc))) 15 | } 16 | -------------------------------------------------------------------------------- /plugin/federation/fedruntime/runtime.go: -------------------------------------------------------------------------------- 1 | package fedruntime 2 | 3 | // Service is the service object that the 4 | // generated.go file will return for the _service 5 | // query 6 | type Service struct { 7 | SDL string `json:"sdl"` 8 | } 9 | 10 | // Everything with a @key implements this 11 | type Entity interface { 12 | IsEntity() 13 | } 14 | 15 | // Used for the Link directive 16 | type Link interface{} 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [*.{go,gotpl}] 12 | indent_style = tab 13 | 14 | [*.yml] 15 | indent_size = 2 16 | 17 | # These often end up with go code inside, so lets keep tabs 18 | [*.{html,md}] 19 | indent_size = 2 20 | indent_style = tab 21 | -------------------------------------------------------------------------------- /_examples/chat/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | /corpus 9 | 10 | # production 11 | /build 12 | 13 | # misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /.chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: CHANGELOG-full-history.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/99designs/gqlgen 6 | options: 7 | commits: 8 | # filters: 9 | # Type: [] 10 | commit_groups: 11 | # title_maps: [] 12 | header: 13 | pattern: "^(.*)$" 14 | pattern_maps: 15 | - Subject 16 | notes: 17 | keywords: 18 | - BREAKING CHANGE -------------------------------------------------------------------------------- /plugin/federation/testdata/entityresolver/generated/errors.go: -------------------------------------------------------------------------------- 1 | package generated 2 | 3 | import "errors" 4 | 5 | // Errors defined for retained code that we want to stick around between generations. 6 | var ( 7 | ErrResolvingHelloWithErrorsByName = errors.New("error resolving HelloWithErrorsByName") 8 | ErrEmptyKeyResolvingHelloWithErrorsByName = errors.New("error (empty key) resolving HelloWithErrorsByName") 9 | ) 10 | -------------------------------------------------------------------------------- /graphql/handler/transport/headers.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import "net/http" 4 | 5 | func writeHeaders(w http.ResponseWriter, headers map[string][]string) { 6 | if len(headers) == 0 { 7 | headers = map[string][]string{ 8 | "Content-Type": {"application/json"}, 9 | } 10 | } 11 | 12 | for key, values := range headers { 13 | for _, value := range values { 14 | w.Header().Add(key, value) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /_examples/federation/products/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | topProducts(first: Int = 5): [Product] 3 | } 4 | 5 | type Manufacturer @key(fields: "id") { 6 | id: String! 7 | name: String! 8 | } 9 | 10 | type Product @key(fields: "manufacturer { id } id") @key(fields: "upc") { 11 | id: String! 12 | manufacturer: Manufacturer! 13 | upc: String! 14 | name: String! 15 | price: Int! 16 | } 17 | -------------------------------------------------------------------------------- /graphql/bool_test.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestBoolean(t *testing.T) { 11 | assert.Equal(t, "true", doBooleanMarshal(true)) 12 | assert.Equal(t, "false", doBooleanMarshal(false)) 13 | } 14 | 15 | func doBooleanMarshal(b bool) string { 16 | var buf bytes.Buffer 17 | MarshalBoolean(b).MarshalGQL(&buf) 18 | return buf.String() 19 | } 20 | -------------------------------------------------------------------------------- /internal/imports/testdata/unused.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | a "fmt" 5 | "time" 6 | _ "underscore" 7 | ) 8 | 9 | type foo struct { 10 | Time time.Time `json:"text"` 11 | } 12 | 13 | func fn() { 14 | a.Println("hello") 15 | } 16 | 17 | type Message struct { 18 | ID string `json:"id"` 19 | Text string `json:"text"` 20 | CreatedBy string `json:"createdBy"` 21 | CreatedAt time.Time `json:"createdAt"` 22 | } 23 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/singlefile/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/schema.graphql" 3 | 4 | exec: 5 | filename: testdata/singlefile/out/ignored.go 6 | model: 7 | filename: testdata/singlefile/out/generated.go 8 | resolver: 9 | filename: testdata/singlefile/out/resolver.go 10 | type: CustomResolverType 11 | 12 | models: 13 | Resolver: 14 | model: github.com/99designs/gqlgen/plugin/resolvergen/testdata/singlefile/out.Resolver 15 | -------------------------------------------------------------------------------- /_examples/config/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package config 4 | 5 | type NewTodo struct { 6 | Text string `json:"text"` 7 | UserID string `json:"userId"` 8 | } 9 | 10 | type Todo struct { 11 | ID string `json:"id"` 12 | DatabaseID int `json:"databaseId"` 13 | Description string `json:"text"` 14 | Done bool `json:"done"` 15 | User *User `json:"user"` 16 | } 17 | -------------------------------------------------------------------------------- /internal/imports/testdata/unused.expected.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | a "fmt" 5 | "time" 6 | _ "underscore" 7 | ) 8 | 9 | type foo struct { 10 | Time time.Time `json:"text"` 11 | } 12 | 13 | func fn() { 14 | a.Println("hello") 15 | } 16 | 17 | type Message struct { 18 | ID string `json:"id"` 19 | Text string `json:"text"` 20 | CreatedBy string `json:"createdBy"` 21 | CreatedAt time.Time `json:"createdAt"` 22 | } 23 | -------------------------------------------------------------------------------- /internal/rewrite/testdata/example.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "fmt" 5 | 6 | lol "bytes" 7 | ) 8 | 9 | type Foo struct { 10 | Field int 11 | } 12 | 13 | func (m *Foo) Method(arg int) { 14 | // leading comment 15 | 16 | // field comment 17 | m.Field++ 18 | 19 | // trailing comment 20 | } 21 | 22 | func (m *Foo) String() string { 23 | var buf lol.Buffer 24 | buf.WriteString(fmt.Sprintf("%d", m.Field)) 25 | return buf.String() 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/check-federation: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | cd _examples/federation 6 | 7 | ./start.sh & 8 | 9 | sleep 5 10 | curl -s --connect-timeout 5 \ 11 | --max-time 10 \ 12 | --retry 5 \ 13 | --retry-delay 5 \ 14 | --retry-max-time 40 \ 15 | --retry-connrefused \ 16 | localhost:4003 > /dev/null 17 | 18 | sleep 1 19 | 20 | echo "### running jest integration spec" 21 | ./node_modules/.bin/jest --color 22 | 23 | -------------------------------------------------------------------------------- /_examples/type-system-extension/schemas/schema.graphql: -------------------------------------------------------------------------------- 1 | # GraphQL schema example 2 | # 3 | # https://gqlgen.com/getting-started/ 4 | 5 | schema { 6 | query: MyQuery 7 | } 8 | 9 | interface Node { 10 | id: ID! 11 | } 12 | 13 | type Todo implements Node { 14 | id: ID! 15 | text: String! 16 | state: State! 17 | } 18 | 19 | type MyQuery { 20 | todos: [Todo!]! 21 | } 22 | 23 | union Data = Todo 24 | 25 | enum State { 26 | NOT_YET 27 | DONE 28 | } 29 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/followschema/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/schema.graphql" 3 | 4 | exec: 5 | filename: testdata/singlefile/out/ignored.go 6 | model: 7 | filename: testdata/singlefile/out/generated.go 8 | resolver: 9 | type: CustomResolverType 10 | layout: follow-schema 11 | dir: testdata/followschema/out 12 | 13 | models: 14 | Resolver: 15 | model: github.com/99designs/gqlgen/plugin/resolvergen/testdata/singlefile/out.Resolver 16 | -------------------------------------------------------------------------------- /_examples/chat/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React App 8 | 9 | 10 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /init-templates/schema.graphqls: -------------------------------------------------------------------------------- 1 | # GraphQL schema example 2 | # 3 | # https://gqlgen.com/getting-started/ 4 | 5 | type Todo { 6 | id: ID! 7 | text: String! 8 | done: Boolean! 9 | user: User! 10 | } 11 | 12 | type User { 13 | id: ID! 14 | name: String! 15 | } 16 | 17 | type Query { 18 | todos: [Todo!]! 19 | } 20 | 21 | input NewTodo { 22 | text: String! 23 | userId: String! 24 | } 25 | 26 | type Mutation { 27 | createTodo(input: NewTodo!): Todo! 28 | } 29 | -------------------------------------------------------------------------------- /api/testdata/default/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | # GraphQL schema example 2 | # 3 | # https://gqlgen.com/getting-started/ 4 | 5 | type Todo { 6 | id: ID! 7 | text: String! 8 | done: Boolean! 9 | user: User! 10 | } 11 | 12 | type User { 13 | id: ID! 14 | name: String! 15 | } 16 | 17 | type Query { 18 | todos: [Todo!]! 19 | } 20 | 21 | input NewTodo { 22 | text: String! 23 | userId: String! 24 | } 25 | 26 | type Mutation { 27 | createTodo(input: NewTodo!): Todo! 28 | } 29 | -------------------------------------------------------------------------------- /graphql/handler/extension/introspection_test.go: -------------------------------------------------------------------------------- 1 | package extension 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/graphql" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestIntrospection(t *testing.T) { 12 | rc := &graphql.OperationContext{ 13 | DisableIntrospection: true, 14 | } 15 | require.Nil(t, Introspection{}.MutateOperationContext(context.Background(), rc)) 16 | require.Equal(t, false, rc.DisableIntrospection) 17 | } 18 | -------------------------------------------------------------------------------- /graphql/recovery.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "runtime/debug" 8 | 9 | "github.com/vektah/gqlparser/v2/gqlerror" 10 | ) 11 | 12 | type RecoverFunc func(ctx context.Context, err interface{}) (userMessage error) 13 | 14 | func DefaultRecover(ctx context.Context, err interface{}) error { 15 | fmt.Fprintln(os.Stderr, err) 16 | fmt.Fprintln(os.Stderr) 17 | debug.PrintStack() 18 | 19 | return gqlerror.Errorf("internal system error") 20 | } 21 | -------------------------------------------------------------------------------- /_examples/selection/schema.graphql: -------------------------------------------------------------------------------- 1 | interface Event { 2 | selection: [String!] 3 | collected: [String!] 4 | } 5 | 6 | type Post implements Event { 7 | message: String! 8 | sent: Time! 9 | selection: [String!] 10 | collected: [String!] 11 | } 12 | 13 | type Like implements Event { 14 | reaction: String! 15 | sent: Time! 16 | selection: [String!] 17 | collected: [String!] 18 | } 19 | 20 | type Query { 21 | events: [Event!] 22 | } 23 | 24 | scalar Time 25 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/wrapped_type.graphql: -------------------------------------------------------------------------------- 1 | # regression test for https://github.com/99designs/gqlgen/issues/721 2 | 3 | extend type Query { 4 | wrappedStruct: WrappedStruct! 5 | wrappedScalar: WrappedScalar! 6 | wrappedMap: WrappedMap! 7 | wrappedSlice: WrappedSlice! 8 | } 9 | 10 | type WrappedStruct { name: WrappedScalar!, desc: WrappedScalar } 11 | scalar WrappedScalar 12 | type WrappedMap { get(key: String!): String! } 13 | type WrappedSlice { get(idx: Int!): String! } 14 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/wrapped_type.graphql: -------------------------------------------------------------------------------- 1 | # regression test for https://github.com/99designs/gqlgen/issues/721 2 | 3 | extend type Query { 4 | wrappedStruct: WrappedStruct! 5 | wrappedScalar: WrappedScalar! 6 | wrappedMap: WrappedMap! 7 | wrappedSlice: WrappedSlice! 8 | } 9 | 10 | type WrappedStruct { name: WrappedScalar!, desc: WrappedScalar } 11 | scalar WrappedScalar 12 | type WrappedMap { get(key: String!): String! } 13 | type WrappedSlice { get(idx: Int!): String! } 14 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/filetemplate/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/schema.graphql" 3 | 4 | exec: 5 | filename: testdata/singlefile/out/ignored.go 6 | model: 7 | filename: testdata/singlefile/out/generated.go 8 | resolver: 9 | type: CustomResolverType 10 | layout: follow-schema 11 | dir: testdata/filetemplate/out 12 | filename_template: "{name}.custom.go" 13 | 14 | models: 15 | Resolver: 16 | model: github.com/99designs/gqlgen/plugin/resolvergen/testdata/singlefile/out.Resolver 17 | -------------------------------------------------------------------------------- /_examples/config/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | todo "github.com/99designs/gqlgen/_examples/config" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/99designs/gqlgen/graphql/playground" 10 | ) 11 | 12 | func main() { 13 | http.Handle("/", playground.Handler("Todo", "/query")) 14 | http.Handle("/query", handler.NewDefaultServer( 15 | todo.NewExecutableSchema(todo.New()), 16 | )) 17 | log.Fatal(http.ListenAndServe(":8081", nil)) 18 | } 19 | -------------------------------------------------------------------------------- /_examples/federation/accounts/graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | type EmailHost struct { 6 | ID string `json:"id"` 7 | Name string `json:"name"` 8 | } 9 | 10 | func (EmailHost) IsEntity() {} 11 | 12 | type User struct { 13 | ID string `json:"id"` 14 | Host *EmailHost `json:"host"` 15 | Email string `json:"email"` 16 | Username string `json:"username"` 17 | } 18 | 19 | func (User) IsEntity() {} 20 | -------------------------------------------------------------------------------- /plugin/federation/testdata/entityresolver/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/entityresolver/schema.graphql" 3 | exec: 4 | filename: testdata/entityresolver/generated/exec.go 5 | federation: 6 | filename: testdata/entityresolver/generated/federation.go 7 | model: 8 | filename: testdata/entityresolver/generated/model/models.go 9 | package: model 10 | resolver: 11 | filename: testdata/entityresolver/resolver.go 12 | layout: follow-schema 13 | dir: testdata/entityresolver 14 | package: entityresolver 15 | -------------------------------------------------------------------------------- /_examples/federation/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function cleanup { 4 | kill "$ACCOUNTS_PID" 5 | kill "$PRODUCTS_PID" 6 | kill "$REVIEWS_PID" 7 | } 8 | trap cleanup EXIT 9 | 10 | go build -o /tmp/srv-accounts ./accounts 11 | go build -o /tmp/srv-products ./products 12 | go build -o /tmp/srv-reviews ./reviews 13 | 14 | /tmp/srv-accounts & 15 | ACCOUNTS_PID=$! 16 | 17 | /tmp/srv-products & 18 | PRODUCTS_PID=$! 19 | 20 | /tmp/srv-reviews & 21 | REVIEWS_PID=$! 22 | 23 | sleep 1 24 | 25 | node gateway/index.js 26 | -------------------------------------------------------------------------------- /graphql/map.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | func MarshalMap(val map[string]interface{}) Marshaler { 10 | return WriterFunc(func(w io.Writer) { 11 | err := json.NewEncoder(w).Encode(val) 12 | if err != nil { 13 | panic(err) 14 | } 15 | }) 16 | } 17 | 18 | func UnmarshalMap(v interface{}) (map[string]interface{}, error) { 19 | if m, ok := v.(map[string]interface{}); ok { 20 | return m, nil 21 | } 22 | 23 | return nil, fmt.Errorf("%T is not a map", v) 24 | } 25 | -------------------------------------------------------------------------------- /_examples/websocket-initfunc/server/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gqlgen/_examples/websocket-initfunc/server 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/99designs/gqlgen v0.17.25 7 | github.com/go-chi/chi v1.5.4 8 | github.com/gorilla/websocket v1.5.0 9 | github.com/rs/cors v1.8.3 10 | github.com/vektah/gqlparser/v2 v2.5.1 11 | ) 12 | 13 | require ( 14 | github.com/agnivade/levenshtein v1.1.1 // indirect 15 | github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect 16 | github.com/mitchellh/mapstructure v1.5.0 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/ptr_to_ptr_input.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | type PtrToPtrOuter struct { 4 | Name string 5 | Inner *PtrToPtrInner 6 | StupidInner *******PtrToPtrInner 7 | } 8 | 9 | type PtrToPtrInner struct { 10 | Key string 11 | Value string 12 | } 13 | 14 | type UpdatePtrToPtrOuter struct { 15 | Name *string 16 | Inner **UpdatePtrToPtrInner 17 | StupidInner ********UpdatePtrToPtrInner 18 | } 19 | 20 | type UpdatePtrToPtrInner struct { 21 | Key *string 22 | Value *string 23 | } 24 | -------------------------------------------------------------------------------- /_examples/scalars/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/99designs/gqlgen/_examples/scalars" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/99designs/gqlgen/graphql/playground" 10 | ) 11 | 12 | func main() { 13 | http.Handle("/", playground.Handler("Starwars", "/query")) 14 | http.Handle("/query", handler.NewDefaultServer(scalars.NewExecutableSchema(scalars.Config{Resolvers: &scalars.Resolver{}}))) 15 | 16 | log.Fatal(http.ListenAndServe(":8084", nil)) 17 | } 18 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/ptr_to_ptr_input.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | type PtrToPtrOuter struct { 4 | Name string 5 | Inner *PtrToPtrInner 6 | StupidInner *******PtrToPtrInner 7 | } 8 | 9 | type PtrToPtrInner struct { 10 | Key string 11 | Value string 12 | } 13 | 14 | type UpdatePtrToPtrOuter struct { 15 | Name *string 16 | Inner **UpdatePtrToPtrInner 17 | StupidInner ********UpdatePtrToPtrInner 18 | } 19 | 20 | type UpdatePtrToPtrInner struct { 21 | Key *string 22 | Value *string 23 | } 24 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/maps.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | mapStringInterface(in: MapStringInterfaceInput): MapStringInterfaceType 3 | mapNestedStringInterface(in: NestedMapInput): MapStringInterfaceType 4 | } 5 | 6 | type MapStringInterfaceType @goModel(model: "map[string]interface{}") { 7 | a: String 8 | b: Int 9 | } 10 | 11 | input MapStringInterfaceInput @goModel(model: "map[string]interface{}") { 12 | a: String 13 | b: Int 14 | } 15 | 16 | input NestedMapInput { 17 | map: MapStringInterfaceInput 18 | } 19 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/maps.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | mapStringInterface(in: MapStringInterfaceInput): MapStringInterfaceType 3 | mapNestedStringInterface(in: NestedMapInput): MapStringInterfaceType 4 | } 5 | 6 | type MapStringInterfaceType @goModel(model: "map[string]interface{}") { 7 | a: String 8 | b: Int 9 | } 10 | 11 | input MapStringInterfaceInput @goModel(model: "map[string]interface{}") { 12 | a: String 13 | b: Int 14 | } 15 | 16 | input NestedMapInput { 17 | map: MapStringInterfaceInput 18 | } 19 | -------------------------------------------------------------------------------- /_examples/selection/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/99designs/gqlgen/_examples/selection" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/99designs/gqlgen/graphql/playground" 10 | ) 11 | 12 | func main() { 13 | http.Handle("/", playground.Handler("Selection Demo", "/query")) 14 | http.Handle("/query", handler.NewDefaultServer(selection.NewExecutableSchema(selection.Config{Resolvers: &selection.Resolver{}}))) 15 | log.Fatal(http.ListenAndServe(":8086", nil)) 16 | } 17 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/defaults.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | defaultParameters( 3 | falsyBoolean: Boolean = false 4 | truthyBoolean: Boolean = true 5 | ): DefaultParametersMirror! 6 | } 7 | 8 | extend type Mutation { 9 | defaultInput(input: DefaultInput!): DefaultParametersMirror! 10 | } 11 | 12 | input DefaultInput { 13 | falsyBoolean: Boolean = false 14 | truthyBoolean: Boolean = true 15 | } 16 | 17 | type DefaultParametersMirror { 18 | falsyBoolean: Boolean 19 | truthyBoolean: Boolean 20 | } 21 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/defaults.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | defaultParameters( 3 | falsyBoolean: Boolean = false 4 | truthyBoolean: Boolean = true 5 | ): DefaultParametersMirror! 6 | } 7 | 8 | extend type Mutation { 9 | defaultInput(input: DefaultInput!): DefaultParametersMirror! 10 | } 11 | 12 | input DefaultInput { 13 | falsyBoolean: Boolean = false 14 | truthyBoolean: Boolean = true 15 | } 16 | 17 | type DefaultParametersMirror { 18 | falsyBoolean: Boolean 19 | truthyBoolean: Boolean 20 | } 21 | -------------------------------------------------------------------------------- /_examples/config/.gqlgen.yml: -------------------------------------------------------------------------------- 1 | # .gqlgen.yml _examples 2 | # 3 | # Refer to https://gqlgen.com/config/ 4 | # for detailed .gqlgen.yml documentation. 5 | 6 | schema: "*.graphql" 7 | exec: 8 | filename: generated.go 9 | model: 10 | filename: models_gen.go 11 | resolver: 12 | type: Resolver 13 | layout: follow-schema 14 | dir: . 15 | 16 | models: 17 | Todo: # Object 18 | fields: 19 | text: 20 | fieldName: Description # Field 21 | NewTodo: # Input 22 | fields: 23 | userId: 24 | fieldName: UserID # Field 25 | -------------------------------------------------------------------------------- /internal/code/util_test.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestNormalizeVendor(t *testing.T) { 10 | require.Equal(t, "bar/baz", NormalizeVendor("foo/vendor/bar/baz")) 11 | require.Equal(t, "[]bar/baz", NormalizeVendor("[]foo/vendor/bar/baz")) 12 | require.Equal(t, "*bar/baz", NormalizeVendor("*foo/vendor/bar/baz")) 13 | require.Equal(t, "*[]*bar/baz", NormalizeVendor("*[]*foo/vendor/bar/baz")) 14 | require.Equal(t, "[]*bar/baz", NormalizeVendor("[]*foo/vendor/bar/baz")) 15 | } 16 | -------------------------------------------------------------------------------- /graphql/upload.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | type Upload struct { 9 | File io.ReadSeeker 10 | Filename string 11 | Size int64 12 | ContentType string 13 | } 14 | 15 | func MarshalUpload(f Upload) Marshaler { 16 | return WriterFunc(func(w io.Writer) { 17 | io.Copy(w, f.File) 18 | }) 19 | } 20 | 21 | func UnmarshalUpload(v interface{}) (Upload, error) { 22 | upload, ok := v.(Upload) 23 | if !ok { 24 | return Upload{}, fmt.Errorf("%T is not an Upload", v) 25 | } 26 | return upload, nil 27 | } 28 | -------------------------------------------------------------------------------- /_examples/chat/schema.graphql: -------------------------------------------------------------------------------- 1 | type Chatroom { 2 | name: String! 3 | messages: [Message!]! 4 | } 5 | 6 | type Message { 7 | id: ID! 8 | text: String! 9 | createdBy: String! 10 | createdAt: Time! 11 | } 12 | 13 | type Query { 14 | room(name:String!): Chatroom 15 | } 16 | 17 | type Mutation { 18 | post(text: String!, username: String!, roomName: String!): Message! 19 | } 20 | 21 | type Subscription { 22 | messageAdded(roomName: String!): Message! 23 | } 24 | 25 | scalar Time 26 | 27 | directive @user(username: String!) on SUBSCRIPTION 28 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/issue896.graphql: -------------------------------------------------------------------------------- 1 | # This example should build stable output. If the file content starts 2 | # alternating nondeterministically between two outputs, then see 3 | # https://github.com/99designs/gqlgen/issues/896. 4 | 5 | extend schema { 6 | query: Query 7 | subscription: Subscription 8 | } 9 | 10 | type CheckIssue896 {id: Int} 11 | 12 | extend type Query { 13 | issue896a: [CheckIssue896!] # Note the "!" or lack thereof. 14 | } 15 | 16 | extend type Subscription { 17 | issue896b: [CheckIssue896] # Note the "!" or lack thereof. 18 | } 19 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/issue896.graphql: -------------------------------------------------------------------------------- 1 | # This example should build stable output. If the file content starts 2 | # alternating nondeterministically between two outputs, then see 3 | # https://github.com/99designs/gqlgen/issues/896. 4 | 5 | extend schema { 6 | query: Query 7 | subscription: Subscription 8 | } 9 | 10 | type CheckIssue896 {id: Int} 11 | 12 | extend type Query { 13 | issue896a: [CheckIssue896!] # Note the "!" or lack thereof. 14 | } 15 | 16 | extend type Subscription { 17 | issue896b: [CheckIssue896] # Note the "!" or lack thereof. 18 | } 19 | -------------------------------------------------------------------------------- /_examples/config/resolver.go: -------------------------------------------------------------------------------- 1 | //go:generate go run ../../testdata/gqlgen.go 2 | 3 | package config 4 | 5 | func New() Config { 6 | c := Config{ 7 | Resolvers: &Resolver{ 8 | todos: []*Todo{ 9 | {DatabaseID: 1, Description: "A todo not to forget", Done: false}, 10 | {DatabaseID: 2, Description: "This is the most important", Done: false}, 11 | {DatabaseID: 3, Description: "Please do this or else", Done: false}, 12 | }, 13 | nextID: 3, 14 | }, 15 | } 16 | return c 17 | } 18 | 19 | type Resolver struct { 20 | todos []*Todo 21 | nextID int 22 | } 23 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/ptr_to_ptr_input.graphql: -------------------------------------------------------------------------------- 1 | type PtrToPtrOuter { 2 | name: String! 3 | inner: PtrToPtrInner 4 | stupidInner: PtrToPtrInner 5 | } 6 | 7 | type PtrToPtrInner { 8 | key: String! 9 | value: String! 10 | } 11 | 12 | input UpdatePtrToPtrOuter { 13 | name: String 14 | inner: UpdatePtrToPtrInner 15 | stupidInner: UpdatePtrToPtrInner 16 | } 17 | 18 | input UpdatePtrToPtrInner { 19 | key: String 20 | value: String 21 | } 22 | 23 | extend type Mutation { 24 | updatePtrToPtr(input: UpdatePtrToPtrOuter!): PtrToPtrOuter! 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/check-init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | gqlgen_dir=$(pwd) 6 | cd $(mktemp -d) 7 | go mod init inittest 8 | printf '// +build tools\npackage tools\nimport _ "github.com/99designs/gqlgen"' | gofmt > tools.go 9 | go mod tidy 10 | go mod edit -replace=github.com/99designs/gqlgen="$gqlgen_dir" 11 | go mod tidy 12 | 13 | if ! go run github.com/99designs/gqlgen init ; then 14 | echo "gqlgen init failed" 15 | exit 125 16 | fi 17 | 18 | if ! go run github.com/99designs/gqlgen generate ; then 19 | echo "gqlgen generate failed" 20 | exit 125 21 | fi 22 | -------------------------------------------------------------------------------- /_examples/websocket-initfunc/server/readme.md: -------------------------------------------------------------------------------- 1 | # WebSocket Init App 2 | 3 | Example server app using websocket `InitFunc`. 4 | 5 | ## Build and Run the server app 6 | 7 | First get an update from gqlgen: 8 | ```bash 9 | go mod tidy 10 | go get -u github.com/99designs/gqlgen 11 | ``` 12 | 13 | Next just make the build: 14 | ```bash 15 | make build 16 | ``` 17 | 18 | Run the server: 19 | ```bash 20 | ./server 21 | 2022/07/07 16:49:46 connect to http://localhost:8080/ for GraphQL playground 22 | ``` 23 | 24 | You may now implement a websocket client to subscribe for websocket messages. -------------------------------------------------------------------------------- /codegen/testserver/followschema/nulls.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | errorBubble: Error 3 | errorBubbleList: [Error!] 4 | errorList: [Error] 5 | errors: Errors 6 | valid: String! 7 | invalid: String! 8 | } 9 | 10 | extend type Subscription { 11 | errorRequired: Error! 12 | } 13 | 14 | type Errors { 15 | a: Error! 16 | b: Error! 17 | c: Error! 18 | d: Error! 19 | e: Error! 20 | } 21 | 22 | type Error { 23 | id: ID! 24 | errorOnNonRequiredField: String 25 | errorOnRequiredField: String! 26 | nilOnRequiredField: String! 27 | } 28 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/ptr_to_ptr_input.graphql: -------------------------------------------------------------------------------- 1 | type PtrToPtrOuter { 2 | name: String! 3 | inner: PtrToPtrInner 4 | stupidInner: PtrToPtrInner 5 | } 6 | 7 | type PtrToPtrInner { 8 | key: String! 9 | value: String! 10 | } 11 | 12 | input UpdatePtrToPtrOuter { 13 | name: String 14 | inner: UpdatePtrToPtrInner 15 | stupidInner: UpdatePtrToPtrInner 16 | } 17 | 18 | input UpdatePtrToPtrInner { 19 | key: String 20 | value: String 21 | } 22 | 23 | extend type Mutation { 24 | updatePtrToPtr(input: UpdatePtrToPtrOuter!): PtrToPtrOuter! 25 | } 26 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/embedded.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | embeddedCase1: EmbeddedCase1 3 | embeddedCase2: EmbeddedCase2 4 | embeddedCase3: EmbeddedCase3 5 | } 6 | 7 | type EmbeddedCase1 @goModel(model:"singlefile.EmbeddedCase1") { 8 | exportedEmbeddedPointerExportedMethod: String! 9 | } 10 | 11 | type EmbeddedCase2 @goModel(model:"singlefile.EmbeddedCase2") { 12 | unexportedEmbeddedPointerExportedMethod: String! 13 | } 14 | 15 | type EmbeddedCase3 @goModel(model:"singlefile.EmbeddedCase3") { 16 | unexportedEmbeddedInterfaceExportedMethod: String! 17 | } 18 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/nulls.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | errorBubble: Error 3 | errorBubbleList: [Error!] 4 | errorList: [Error] 5 | errors: Errors 6 | valid: String! 7 | invalid: String! 8 | } 9 | 10 | extend type Subscription { 11 | errorRequired: Error! 12 | } 13 | 14 | type Errors { 15 | a: Error! 16 | b: Error! 17 | c: Error! 18 | d: Error! 19 | e: Error! 20 | } 21 | 22 | type Error { 23 | id: ID! 24 | errorOnNonRequiredField: String 25 | errorOnRequiredField: String! 26 | nilOnRequiredField: String! 27 | } 28 | -------------------------------------------------------------------------------- /graphql/time.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | func MarshalTime(t time.Time) Marshaler { 11 | if t.IsZero() { 12 | return Null 13 | } 14 | 15 | return WriterFunc(func(w io.Writer) { 16 | io.WriteString(w, strconv.Quote(t.Format(time.RFC3339Nano))) 17 | }) 18 | } 19 | 20 | func UnmarshalTime(v interface{}) (time.Time, error) { 21 | if tmpStr, ok := v.(string); ok { 22 | return time.Parse(time.RFC3339Nano, tmpStr) 23 | } 24 | return time.Time{}, errors.New("time should be RFC3339Nano formatted string") 25 | } 26 | -------------------------------------------------------------------------------- /_examples/federation/products/graph/model/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | type Manufacturer struct { 6 | ID string `json:"id"` 7 | Name string `json:"name"` 8 | } 9 | 10 | func (Manufacturer) IsEntity() {} 11 | 12 | type Product struct { 13 | ID string `json:"id"` 14 | Manufacturer *Manufacturer `json:"manufacturer"` 15 | Upc string `json:"upc"` 16 | Name string `json:"name"` 17 | Price int `json:"price"` 18 | } 19 | 20 | func (Product) IsEntity() {} 21 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/embedded.graphql: -------------------------------------------------------------------------------- 1 | extend type Query { 2 | embeddedCase1: EmbeddedCase1 3 | embeddedCase2: EmbeddedCase2 4 | embeddedCase3: EmbeddedCase3 5 | } 6 | 7 | type EmbeddedCase1 @goModel(model:"followschema.EmbeddedCase1") { 8 | exportedEmbeddedPointerExportedMethod: String! 9 | } 10 | 11 | type EmbeddedCase2 @goModel(model:"followschema.EmbeddedCase2") { 12 | unexportedEmbeddedPointerExportedMethod: String! 13 | } 14 | 15 | type EmbeddedCase3 @goModel(model:"followschema.EmbeddedCase3") { 16 | unexportedEmbeddedInterfaceExportedMethod: String! 17 | } 18 | -------------------------------------------------------------------------------- /_examples/federation/reviews/graph/model/models.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Product struct { 4 | ID string `json:"id"` 5 | Manufacturer *Manufacturer `json:"manufacturer"` 6 | Reviews []*Review `json:"reviews"` 7 | } 8 | 9 | func (Product) IsEntity() {} 10 | 11 | type Review struct { 12 | Body string 13 | Author *User 14 | Product *Product 15 | } 16 | 17 | type User struct { 18 | ID string `json:"id"` 19 | Host *EmailHost `json:"host"` 20 | Email string `json:"email"` 21 | // Reviews []*Review `json:"reviews"` 22 | } 23 | 24 | func (User) IsEntity() {} 25 | -------------------------------------------------------------------------------- /codegen/config/testdata/autobinding/scalars/model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | type Banned bool 10 | 11 | func (b Banned) MarshalGQL(w io.Writer) { 12 | if b { 13 | w.Write([]byte("true")) 14 | } else { 15 | w.Write([]byte("false")) 16 | } 17 | } 18 | 19 | func (b *Banned) UnmarshalGQL(v interface{}) error { 20 | switch v := v.(type) { 21 | case string: 22 | *b = strings.ToLower(v) == "true" 23 | return nil 24 | case bool: 25 | *b = Banned(v) 26 | return nil 27 | default: 28 | return fmt.Errorf("%T is not a bool", v) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /graphql/bool.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | func MarshalBoolean(b bool) Marshaler { 10 | if b { 11 | return WriterFunc(func(w io.Writer) { w.Write(trueLit) }) 12 | } 13 | return WriterFunc(func(w io.Writer) { w.Write(falseLit) }) 14 | } 15 | 16 | func UnmarshalBoolean(v interface{}) (bool, error) { 17 | switch v := v.(type) { 18 | case string: 19 | return strings.ToLower(v) == "true", nil 20 | case int: 21 | return v != 0, nil 22 | case bool: 23 | return v, nil 24 | default: 25 | return false, fmt.Errorf("%T is not a bool", v) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /_examples/chat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /docs/layouts/partials/version-switcher.html: -------------------------------------------------------------------------------- 1 | {{ $VersionString := getenv "VERSIONS" }} 2 | {{ $Versions := split $VersionString "," }} 3 | {{ $currentVersion := getenv "CURRENT_VERSION" }} 4 | 5 |
6 | {{ $currentVersion }} 7 |
8 | {{$currentVersion}} 9 | {{ range $i, $version := $Versions }} 10 | {{ if not (eq $currentVersion $version) }} 11 | {{$version}} 12 | {{ end }} 13 | {{ end }} 14 |
15 |
16 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/bytes.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/99designs/gqlgen/graphql" 8 | ) 9 | 10 | func MarshalBytes(b []byte) graphql.Marshaler { 11 | return graphql.WriterFunc(func(w io.Writer) { 12 | _, _ = fmt.Fprintf(w, "%q", string(b)) 13 | }) 14 | } 15 | 16 | func UnmarshalBytes(v interface{}) ([]byte, error) { 17 | switch v := v.(type) { 18 | case string: 19 | return []byte(v), nil 20 | case *string: 21 | return []byte(*v), nil 22 | case []byte: 23 | return v, nil 24 | default: 25 | return nil, fmt.Errorf("%T is not []byte", v) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /graphql/float_test.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestFloat(t *testing.T) { 11 | assert.Equal(t, "123", m2s(MarshalFloat(123))) 12 | assert.Equal(t, "1.2345678901", m2s(MarshalFloat(1.2345678901))) 13 | assert.Equal(t, "1.2345678901234567", m2s(MarshalFloat(1.234567890123456789))) 14 | assert.Equal(t, "1.2e+20", m2s(MarshalFloat(1.2e+20))) 15 | assert.Equal(t, "1.2e-20", m2s(MarshalFloat(1.2e-20))) 16 | } 17 | 18 | func m2s(m Marshaler) string { 19 | var b bytes.Buffer 20 | m.MarshalGQL(&b) 21 | return b.String() 22 | } 23 | -------------------------------------------------------------------------------- /_examples/federation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gateway", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gateway/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@apollo/gateway": "^0.42.0", 13 | "apollo-server": "^3.3.0", 14 | "graphql": "^15.6.1" 15 | }, 16 | "devDependencies": { 17 | "apollo-cache-inmemory": "^1.6.5", 18 | "apollo-client": "^2.6.8", 19 | "apollo-link-http": "^1.5.16", 20 | "jest": "^25.1.0", 21 | "node-fetch": "^2.6.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/bytes.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/99designs/gqlgen/graphql" 8 | ) 9 | 10 | func MarshalBytes(b []byte) graphql.Marshaler { 11 | return graphql.WriterFunc(func(w io.Writer) { 12 | _, _ = fmt.Fprintf(w, "%q", string(b)) 13 | }) 14 | } 15 | 16 | func UnmarshalBytes(v interface{}) ([]byte, error) { 17 | switch v := v.(type) { 18 | case string: 19 | return []byte(v), nil 20 | case *string: 21 | return []byte(*v), nil 22 | case []byte: 23 | return v, nil 24 | default: 25 | return nil, fmt.Errorf("%T is not []byte", v) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/check-coverage: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | GO111MODULE=off go get github.com/mattn/goveralls 5 | 6 | go test -covermode atomic -coverprofile=/tmp/coverage.out.tmp -coverpkg=./... $(go list github.com/99designs/gqlgen/... | grep -v _examples) 7 | # ignore protobuf files 8 | cat /tmp/coverage.out.tmp | grep -v ".pb.go" > /tmp/coverage.out 9 | 10 | goveralls -coverprofile=/tmp/coverage.out -service=github -ignore='_examples/*/*,_examples/*/*/*,integration/*,integration/*/*,codegen/testserver/*/*,plugin/federation/testdata/*/*/*,*/generated.go,*/*/generated.go,*/*/*/generated.go,graphql/executable_schema_mock.go' 11 | -------------------------------------------------------------------------------- /_examples/type-system-extension/README.md: -------------------------------------------------------------------------------- 1 | # Type System Extension example 2 | 3 | https://graphql.github.io/graphql-spec/draft/#sec-Type-System-Extensions 4 | 5 | ``` 6 | $ go run ./server/server.go 7 | 2018/10/25 12:46:45 connect to http://localhost:8080/ for GraphQL playground 8 | 9 | $ curl -X POST 'http://localhost:8080/query' --data-binary '{"query":"{ todos { id text state verified } }"}' 10 | {"data":{"todos":[{"id":"Todo:1","text":"Buy a cat food","state":"NOT_YET","verified":false},{"id":"Todo:2","text":"Check cat water","state":"DONE","verified":true},{"id":"Todo:3","text":"Check cat meal","state":"DONE","verified":true}]}} 11 | ``` 12 | -------------------------------------------------------------------------------- /docs/layouts/_default/single.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 |
3 |
4 |

{{ .LinkTitle }}

5 |
{{ .Title }}
6 | 9 | [edit] 10 | 11 |
12 |
13 | 14 |
15 |
16 | {{partial "version-banner"}} 17 | {{ .Content }} 18 |
19 |
20 | {{ end }} 21 | -------------------------------------------------------------------------------- /graphql/time_test.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestTime(t *testing.T) { 13 | t.Run("symmetry", func(t *testing.T) { 14 | initialTime := time.Now() 15 | buf := bytes.NewBuffer([]byte{}) 16 | MarshalTime(initialTime).MarshalGQL(buf) 17 | 18 | str, err := strconv.Unquote(buf.String()) 19 | require.Nil(t, err) 20 | newTime, err := UnmarshalTime(str) 21 | require.Nil(t, err) 22 | 23 | require.True(t, initialTime.Equal(newTime), "expected times %v and %v to equal", initialTime, newTime) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /_examples/federation/gateway/index.js: -------------------------------------------------------------------------------- 1 | const { ApolloServer } = require('apollo-server'); 2 | const { ApolloGateway } = require("@apollo/gateway"); 3 | 4 | const gateway = new ApolloGateway({ 5 | serviceList: [ 6 | { name: 'accounts', url: 'http://localhost:4001/query' }, 7 | { name: 'products', url: 'http://localhost:4002/query' }, 8 | { name: 'reviews', url: 'http://localhost:4003/query' } 9 | ], 10 | }); 11 | 12 | const server = new ApolloServer({ 13 | gateway, 14 | 15 | subscriptions: false, 16 | }); 17 | 18 | server.listen().then(({ url }) => { 19 | console.log(`🚀 Server ready at ${url}`); 20 | }); 21 | -------------------------------------------------------------------------------- /_examples/fileupload/model/generated.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package model 4 | 5 | import ( 6 | "github.com/99designs/gqlgen/graphql" 7 | ) 8 | 9 | // The `File` type, represents the response of uploading a file. 10 | type File struct { 11 | ID int `json:"id"` 12 | Name string `json:"name"` 13 | Content string `json:"content"` 14 | ContentType string `json:"contentType"` 15 | } 16 | 17 | // The `UploadFile` type, represents the request for uploading a file with certain payload. 18 | type UploadFile struct { 19 | ID int `json:"id"` 20 | File graphql.Upload `json:"file"` 21 | } 22 | -------------------------------------------------------------------------------- /graphql/context_root_field.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | const rootResolverCtx key = "root_resolver_context" 8 | 9 | type RootFieldContext struct { 10 | // The name of the type this field belongs to 11 | Object string 12 | // The raw field 13 | Field CollectedField 14 | } 15 | 16 | func GetRootFieldContext(ctx context.Context) *RootFieldContext { 17 | if val, ok := ctx.Value(rootResolverCtx).(*RootFieldContext); ok { 18 | return val 19 | } 20 | return nil 21 | } 22 | 23 | func WithRootFieldContext(ctx context.Context, rc *RootFieldContext) context.Context { 24 | return context.WithValue(ctx, rootResolverCtx, rc) 25 | } 26 | -------------------------------------------------------------------------------- /_examples/dataloader/schema.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | customers: [Customer!] 3 | 4 | # these methods are here to test code generation of nested arrays 5 | torture1d(customerIds: [Int!]): [Customer!] 6 | torture2d(customerIds: [[Int!]]): [[Customer!]] 7 | } 8 | 9 | type Customer { 10 | id: Int! 11 | name: String! 12 | address: Address 13 | orders: [Order!] 14 | } 15 | 16 | type Address { 17 | id: Int! 18 | street: String! 19 | country: String! 20 | } 21 | 22 | type Order { 23 | id: Int! 24 | date: Time! 25 | amount: Float! 26 | items: [Item!] 27 | } 28 | 29 | type Item { 30 | name: String! 31 | } 32 | scalar Time 33 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/return_values/resolvers.go: -------------------------------------------------------------------------------- 1 | package return_values 2 | 3 | // THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | type Resolver struct{} 10 | 11 | // // foo 12 | func (r *queryResolver) User(ctx context.Context) (User, error) { 13 | panic("not implemented") 14 | } 15 | 16 | // // foo 17 | func (r *queryResolver) UserPointer(ctx context.Context) (*User, error) { 18 | panic("not implemented") 19 | } 20 | 21 | // Query returns QueryResolver implementation. 22 | func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } 23 | 24 | type queryResolver struct{ *Resolver } 25 | -------------------------------------------------------------------------------- /api/testdata/federation2/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | # GraphQL schema example 2 | # 3 | # https://gqlgen.com/getting-started/ 4 | extend schema 5 | @link(url: "https://specs.apollo.dev/federation/v2.0", 6 | import: ["@key", "@shareable", "@provides", "@external", "@tag", "@extends", "@override", "@inaccessible"]) 7 | 8 | type Todo { 9 | id: ID! 10 | text: String! 11 | done: Boolean! 12 | user: User! 13 | } 14 | 15 | type User { 16 | id: ID! 17 | name: String! 18 | } 19 | 20 | type Query { 21 | todos: [Todo!]! 22 | } 23 | 24 | input NewTodo { 25 | text: String! 26 | userId: String! 27 | } 28 | 29 | type Mutation { 30 | createTodo(input: NewTodo!): Todo! 31 | } 32 | -------------------------------------------------------------------------------- /plugin/federation/testdata/federation2/federation2.graphql: -------------------------------------------------------------------------------- 1 | extend schema 2 | @link(url: "https://specs.apollo.dev/federation/v2.0", 3 | import: ["@key", "@shareable", "@provides", "@external", "@tag", "@extends", "@override", "@inaccessible"]) 4 | 5 | schema { 6 | query: CustomQuery 7 | } 8 | 9 | type Hello @key(fields:"name", resolvable: false) { 10 | name: String! 11 | } 12 | 13 | type World @key(fields: "foo bar", resolvable: false) { 14 | foo: String! 15 | bar: Int! 16 | } 17 | 18 | extend type ExternalExtension @key(fields: " upc ") { 19 | upc: String! 20 | reviews: [Hello] 21 | } 22 | 23 | type CustomQuery { 24 | hello: Hello! 25 | } 26 | 27 | -------------------------------------------------------------------------------- /docs/layouts/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | {{ range .Data.Pages }} 3 | {{ if or .Description (eq .Kind "home") }} 4 | 5 | {{ .Permalink }} 6 | {{ if not .Lastmod.IsZero }}{{ safeHTML ( .Lastmod.Format "2006-01-02T15:04:05-07:00" ) }}{{ end }} 7 | {{ with .Sitemap.ChangeFreq }}{{ . }}{{ end }} 8 | {{ if ge .Sitemap.Priority 0.0 }}{{ .Sitemap.Priority }}{{ end }} 9 | 10 | {{ end }} 11 | {{ end }} 12 | 13 | 14 | -------------------------------------------------------------------------------- /plugin/modelgen/out/existing.go: -------------------------------------------------------------------------------- 1 | package out 2 | 3 | type ExistingType struct { 4 | Name *string `json:"name"` 5 | Enum *ExistingEnum `json:"enum"` 6 | Int ExistingInterface `json:"int"` 7 | Existing *MissingTypeNullable `json:"existing"` 8 | } 9 | 10 | type ExistingModel struct { 11 | Name string 12 | Enum ExistingEnum 13 | Int ExistingInterface 14 | } 15 | 16 | type ExistingInput struct { 17 | Name string 18 | Enum ExistingEnum 19 | Int ExistingInterface 20 | } 21 | 22 | type ExistingEnum string 23 | 24 | type ExistingInterface interface { 25 | IsExistingInterface() 26 | } 27 | 28 | type ExistingUnion interface { 29 | IsExistingUnion() 30 | } 31 | -------------------------------------------------------------------------------- /graphql/handler/transport/websocket_close_reason.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // A private key for context that only this package can access. This is important 8 | // to prevent collisions between different context uses 9 | var closeReasonCtxKey = &wsCloseReasonContextKey{"close-reason"} 10 | 11 | type wsCloseReasonContextKey struct { 12 | name string 13 | } 14 | 15 | func AppendCloseReason(ctx context.Context, reason string) context.Context { 16 | return context.WithValue(ctx, closeReasonCtxKey, reason) 17 | } 18 | 19 | func closeReasonForContext(ctx context.Context) string { 20 | reason, _ := ctx.Value(closeReasonCtxKey).(string) 21 | return reason 22 | } 23 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/return_values/return_values_test.go: -------------------------------------------------------------------------------- 1 | package return_values 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | //go:generate rm -f resolvers.go 11 | //go:generate go run ../../../../testdata/gqlgen.go -config gqlgen.yml 12 | 13 | func TestResolverReturnTypes(t *testing.T) { 14 | // verify that the return value of the User resolver is a struct, not a pointer 15 | require.Equal(t, "struct", reflect.TypeOf((&queryResolver{}).User).Out(0).Kind().String()) 16 | // the UserPointer resolver should return a pointer 17 | require.Equal(t, "ptr", reflect.TypeOf((&queryResolver{}).UserPointer).Out(0).Kind().String()) 18 | } 19 | -------------------------------------------------------------------------------- /codegen/interface.gotpl: -------------------------------------------------------------------------------- 1 | {{- range $interface := .Interfaces }} 2 | 3 | func (ec *executionContext) _{{$interface.Name}}(ctx context.Context, sel ast.SelectionSet, obj {{$interface.Type | ref}}) graphql.Marshaler { 4 | switch obj := (obj).(type) { 5 | case nil: 6 | return graphql.Null 7 | {{- range $implementor := $interface.Implementors }} 8 | case {{$implementor.Type | ref}}: 9 | {{- if $implementor.CanBeNil }} 10 | if obj == nil { 11 | return graphql.Null 12 | } 13 | {{- end }} 14 | return ec._{{$implementor.Name}}(ctx, sel, {{ if $implementor.TakeRef }}&{{ end }}obj) 15 | {{- end }} 16 | default: 17 | panic(fmt.Errorf("unexpected type %T", obj)) 18 | } 19 | } 20 | 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /integration/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "schema.graphql" 3 | - "user.graphql" 4 | - "testomitempty.graphql" 5 | 6 | exec: 7 | filename: generated.go 8 | model: 9 | filename: models-go/generated.go 10 | resolver: 11 | filename: resolver.go 12 | type: Resolver 13 | 14 | struct_tag: json 15 | 16 | autobind: 17 | - "github.com/99designs/gqlgen/integration/testomitempty" 18 | 19 | models: 20 | Element: 21 | model: github.com/99designs/gqlgen/integration/models-go.Element 22 | Viewer: 23 | model: github.com/99designs/gqlgen/integration/models-go.Viewer 24 | User: 25 | model: github.com/99designs/gqlgen/integration/remote_api.User 26 | fields: 27 | likes: 28 | resolver: true 29 | -------------------------------------------------------------------------------- /_examples/dataloader/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/99designs/gqlgen/_examples/dataloader" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/99designs/gqlgen/graphql/playground" 10 | ) 11 | 12 | func main() { 13 | router := http.NewServeMux() 14 | 15 | router.Handle("/", playground.Handler("Dataloader", "/query")) 16 | router.Handle("/query", handler.NewDefaultServer( 17 | dataloader.NewExecutableSchema(dataloader.Config{Resolvers: &dataloader.Resolver{}}), 18 | )) 19 | 20 | log.Println("connect to http://localhost:8082/ for graphql playground") 21 | log.Fatal(http.ListenAndServe(":8082", dataloader.LoaderMiddleware(router))) 22 | } 23 | -------------------------------------------------------------------------------- /docs/readme.md: -------------------------------------------------------------------------------- 1 | Documentation 2 | ==== 3 | 4 | This directory contains the markdown source files for the static doc site hosted at [gqlgen.com](https://gqlgen.com) 5 | 6 | 7 | ## Install hugo 8 | 9 | Before working with these docs you will need to install hugo, see [Quickstart](https://gohugo.io/getting-started/quick-start/) for instructions. 10 | 11 | 12 | ## Editing docs 13 | 14 | When editing docs run `hugo serve` and a live reload server will start, then navigate to http://localhost:1313 in your browser. Any changes made will be updated in the browser. 15 | 16 | 17 | ## Publishing docs 18 | 19 | Docs are hosted using [render.com](https://render.com/) and will be automatically deployed when merging to master. 20 | 21 | -------------------------------------------------------------------------------- /plugin/modelgen/out_struct_pointers/existing.go: -------------------------------------------------------------------------------- 1 | package out_struct_pointers 2 | 3 | type ExistingType struct { 4 | Name *string `json:"name"` 5 | Enum *ExistingEnum `json:"enum"` 6 | Int ExistingInterface `json:"int"` 7 | Existing *MissingTypeNullable `json:"existing"` 8 | } 9 | 10 | type ExistingModel struct { 11 | Name string 12 | Enum ExistingEnum 13 | Int ExistingInterface 14 | } 15 | 16 | type ExistingInput struct { 17 | Name string 18 | Enum ExistingEnum 19 | Int ExistingInterface 20 | } 21 | 22 | type ExistingEnum string 23 | 24 | type ExistingInterface interface { 25 | IsExistingInterface() 26 | } 27 | 28 | type ExistingUnion interface { 29 | IsExistingUnion() 30 | } 31 | -------------------------------------------------------------------------------- /_examples/federation/reviews/graph/schema.graphqls: -------------------------------------------------------------------------------- 1 | type Review { 2 | body: String! 3 | author: User! @provides(fields: "username") 4 | product: Product! 5 | } 6 | 7 | extend type EmailHost @key(fields: "id") { 8 | id: String! @external 9 | } 10 | 11 | extend type User @key(fields: "id") { 12 | id: ID! @external 13 | host: EmailHost! @external 14 | email: String! @external 15 | reviews: [Review] @requires(fields: "host {id} email") 16 | } 17 | 18 | extend type Manufacturer @key(fields: "id") { 19 | id: String! @external 20 | } 21 | 22 | extend type Product @key(fields: " manufacturer{ id} id") { 23 | id: String! @external 24 | manufacturer: Manufacturer! @external 25 | reviews: [Review] 26 | } 27 | -------------------------------------------------------------------------------- /internal/imports/prune_test.go: -------------------------------------------------------------------------------- 1 | package imports 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/99designs/gqlgen/internal/code" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestPrune(t *testing.T) { 13 | // prime the packages cache so that it's not considered uninitialized 14 | 15 | b, err := Prune("testdata/unused.go", mustReadFile("testdata/unused.go"), &code.Packages{}) 16 | require.NoError(t, err) 17 | require.Equal(t, strings.ReplaceAll(string(mustReadFile("testdata/unused.expected.go")), "\r\n", "\n"), string(b)) 18 | } 19 | 20 | func mustReadFile(filename string) []byte { 21 | b, err := os.ReadFile(filename) 22 | if err != nil { 23 | panic(err) 24 | } 25 | return b 26 | } 27 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/thirdparty.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strconv" 7 | 8 | "github.com/99designs/gqlgen/graphql" 9 | ) 10 | 11 | type ThirdParty struct { 12 | str string 13 | } 14 | 15 | func MarshalThirdParty(tp ThirdParty) graphql.Marshaler { 16 | return graphql.WriterFunc(func(w io.Writer) { 17 | _, err := io.WriteString(w, strconv.Quote(tp.str)) 18 | if err != nil { 19 | panic(err) 20 | } 21 | }) 22 | } 23 | 24 | func UnmarshalThirdParty(input interface{}) (ThirdParty, error) { 25 | switch input := input.(type) { 26 | case string: 27 | return ThirdParty{str: input}, nil 28 | default: 29 | return ThirdParty{}, fmt.Errorf("unknown type for input: %s", input) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/thirdparty.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strconv" 7 | 8 | "github.com/99designs/gqlgen/graphql" 9 | ) 10 | 11 | type ThirdParty struct { 12 | str string 13 | } 14 | 15 | func MarshalThirdParty(tp ThirdParty) graphql.Marshaler { 16 | return graphql.WriterFunc(func(w io.Writer) { 17 | _, err := io.WriteString(w, strconv.Quote(tp.str)) 18 | if err != nil { 19 | panic(err) 20 | } 21 | }) 22 | } 23 | 24 | func UnmarshalThirdParty(input interface{}) (ThirdParty, error) { 25 | switch input := input.(type) { 26 | case string: 27 | return ThirdParty{str: input}, nil 28 | default: 29 | return ThirdParty{}, fmt.Errorf("unknown type for input: %s", input) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/layouts/partials/version-banner.html: -------------------------------------------------------------------------------- 1 | {{ $currentVersion := getenv "CURRENT_VERSION" }} 2 | {{ $versionString := getenv "VERSIONS" }} 3 | {{ $versions := split $versionString "," }} 4 | {{ $latestVersion := index $versions 0 }} 5 | 6 | {{ if (eq $currentVersion "master") }} 7 |
8 | You are looking at the docs for the unreleased master branch. The latest version is {{ $latestVersion }}. 9 |
10 | {{ else if not (eq $latestVersion $currentVersion) }} 11 |
12 | You are looking at the docs for an older version ({{ $currentVersion }}). The latest version is {{ $latestVersion }}. 13 |
14 | {{ end }} 15 | -------------------------------------------------------------------------------- /integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "test": "jest" 4 | }, 5 | "devDependencies": { 6 | "@babel/core": "^7.11.4", 7 | "@graphql-codegen/cli": "^2.12.1", 8 | "@graphql-codegen/schema-ast": "^2.4.1", 9 | "apollo-cache-inmemory": "^1.6.6", 10 | "apollo-client": "^2.6.10", 11 | "apollo-link-http": "^1.5.17", 12 | "apollo-link-persisted-queries": "^0.2.2", 13 | "apollo-link-ws": "^1.0.20", 14 | "babel-jest": "^24.9.0", 15 | "graphql": "^16.3.0", 16 | "graphql-tag": "^2.12.6", 17 | "jest": "^29.0.3", 18 | "node-fetch": "^2.6.7", 19 | "subscriptions-transport-ws": "^0.9.18", 20 | "ws": "^7.4.6" 21 | }, 22 | "dependencies": { 23 | "@babel/preset-env": "^7.11.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /_examples/config/user.resolvers.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | // Code generated by github.com/99designs/gqlgen version v0.17.27-dev 6 | 7 | import ( 8 | "context" 9 | ) 10 | 11 | // Name is the resolver for the name field. 12 | func (r *roleResolver) Name(ctx context.Context, obj *UserRole) (string, error) { 13 | if obj == nil { 14 | return "", nil 15 | } 16 | return obj.RoleName, nil 17 | } 18 | 19 | // Role returns RoleResolver implementation. 20 | func (r *Resolver) Role() RoleResolver { return &roleResolver{r} } 21 | 22 | type roleResolver struct{ *Resolver } 23 | -------------------------------------------------------------------------------- /docs/config.yml: -------------------------------------------------------------------------------- 1 | baseurl: https://gqlgen.com/ 2 | metadataformat: yaml 3 | title: gqlgen 4 | enableGitInfo: true 5 | pygmentsCodeFences: true 6 | pygmentsUseClasses: true 7 | canonifyURLs: true 8 | 9 | params: 10 | name: gqlgen 11 | description: graphql servers the easy way 12 | 13 | menu: 14 | main: 15 | - name: Introduction 16 | url: / 17 | weight: -10 18 | - name: Reference 19 | identifier: reference 20 | weight: 5 21 | - name: Recipes 22 | identifier: recipes 23 | weight: 10 24 | - name: pkg.go.dev → 25 | parent: reference 26 | url: https://pkg.go.dev/github.com/99designs/gqlgen 27 | 28 | security: 29 | funcs: 30 | getenv: 31 | - '^HUGO_' 32 | - 'VERSIONS' 33 | - 'CURRENT_VERSION' 34 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | tests: true 3 | skip-dirs: 4 | - bin 5 | 6 | linters-settings: 7 | errcheck: 8 | ignore: fmt:.*,[rR]ead|[wW]rite|[cC]lose,io:Copy 9 | 10 | linters: 11 | disable-all: true 12 | enable: 13 | - bodyclose 14 | - deadcode 15 | - depguard 16 | - dupl 17 | - errcheck 18 | - gocritic 19 | - gofmt 20 | - goimports 21 | - gosimple 22 | - govet 23 | - ineffassign 24 | - misspell 25 | - nakedret 26 | - prealloc 27 | - staticcheck 28 | - structcheck 29 | - typecheck 30 | - unconvert 31 | - unused 32 | - varcheck 33 | 34 | issues: 35 | exclude-rules: 36 | # Exclude some linters from running on tests files. 37 | - path: _test\.go 38 | linters: 39 | - dupl 40 | -------------------------------------------------------------------------------- /graphql/context_field_test.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | "github.com/vektah/gqlparser/v2/ast" 9 | ) 10 | 11 | func TestGetResolverContext(t *testing.T) { 12 | require.Nil(t, GetFieldContext(context.Background())) 13 | 14 | rc := &FieldContext{} 15 | require.Equal(t, rc, GetFieldContext(WithFieldContext(context.Background(), rc))) 16 | } 17 | 18 | func testContext(sel ast.SelectionSet) context.Context { 19 | ctx := context.Background() 20 | 21 | rqCtx := &OperationContext{} 22 | ctx = WithOperationContext(ctx, rqCtx) 23 | 24 | root := &FieldContext{ 25 | Field: CollectedField{ 26 | Selections: sel, 27 | }, 28 | } 29 | ctx = WithFieldContext(ctx, root) 30 | 31 | return ctx 32 | } 33 | -------------------------------------------------------------------------------- /_examples/todo/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "log" 7 | "net/http" 8 | "runtime/debug" 9 | 10 | "github.com/99designs/gqlgen/_examples/todo" 11 | "github.com/99designs/gqlgen/graphql/handler" 12 | "github.com/99designs/gqlgen/graphql/playground" 13 | ) 14 | 15 | func main() { 16 | srv := handler.NewDefaultServer(todo.NewExecutableSchema(todo.New())) 17 | srv.SetRecoverFunc(func(ctx context.Context, err interface{}) (userMessage error) { 18 | // send this panic somewhere 19 | log.Print(err) 20 | debug.PrintStack() 21 | return errors.New("user message on panic") 22 | }) 23 | 24 | http.Handle("/", playground.Handler("Todo", "/query")) 25 | http.Handle("/query", srv) 26 | log.Fatal(http.ListenAndServe(":8081", nil)) 27 | } 28 | -------------------------------------------------------------------------------- /_examples/federation/products/graph/products.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "github.com/99designs/gqlgen/_examples/federation/products/graph/model" 4 | 5 | var hats = []*model.Product{ 6 | { 7 | ID: "111", 8 | Manufacturer: &model.Manufacturer{ 9 | ID: "1234", 10 | Name: "Millinery 1234", 11 | }, 12 | Upc: "top-1", 13 | Name: "Trilby", 14 | Price: 11, 15 | }, 16 | { 17 | ID: "222", 18 | Manufacturer: &model.Manufacturer{ 19 | ID: "2345", 20 | Name: "Millinery 2345", 21 | }, 22 | Upc: "top-2", 23 | Name: "Fedora", 24 | Price: 22, 25 | }, 26 | { 27 | ID: "333", 28 | Manufacturer: &model.Manufacturer{ 29 | ID: "2345", 30 | Name: "Millinery 2345", 31 | }, 32 | Upc: "top-3", 33 | Name: "Boater", 34 | Price: 33, 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /_examples/scalars/.gqlgen.yml: -------------------------------------------------------------------------------- 1 | model: 2 | filename: model/generated.go 3 | 4 | models: 5 | User: 6 | model: github.com/99designs/gqlgen/_examples/scalars/model.User 7 | Timestamp: 8 | model: github.com/99designs/gqlgen/_examples/scalars/model.Timestamp 9 | SearchArgs: 10 | model: github.com/99designs/gqlgen/_examples/scalars/model.SearchArgs 11 | Point: 12 | model: github.com/99designs/gqlgen/_examples/scalars/model.Point 13 | ID: 14 | model: github.com/99designs/gqlgen/_examples/scalars/model.ID 15 | Tier: 16 | model: github.com/99designs/gqlgen/_examples/scalars/model.Tier 17 | Banned: 18 | model: github.com/99designs/gqlgen/_examples/scalars/model.Banned 19 | DarkMode: 20 | model: github.com/99designs/gqlgen/_examples/scalars/model.Preferences 21 | -------------------------------------------------------------------------------- /.github/workflows/security.yml: -------------------------------------------------------------------------------- 1 | name: Security 2 | on: [push, pull_request] 3 | # When a new revision is pushed to a PR, cancel all in-progress CI runs for that 4 | # PR. See https://docs.github.com/en/actions/using-jobs/using-concurrency 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 7 | cancel-in-progress: true 8 | jobs: 9 | nancy: 10 | strategy: 11 | matrix: 12 | go: ["1.20"] # nancy is a little flaky 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | - uses: actions/setup-go@v3 17 | with: 18 | go-version: ${{ matrix.go }} 19 | - run: go mod download && go list -json -deps all > go.list 20 | - uses: sonatype-nexus-community/nancy-github-action@main 21 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/mutation_with_custom_scalar.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "regexp" 8 | ) 9 | 10 | var re = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") 11 | 12 | type Email string 13 | 14 | func (value *Email) UnmarshalGQL(v interface{}) error { 15 | input, ok := v.(string) 16 | if !ok { 17 | return fmt.Errorf("email expects a string value") 18 | } 19 | if !re.MatchString(input) { 20 | return fmt.Errorf("invalid email format") 21 | } 22 | *value = Email(input) 23 | return nil 24 | } 25 | 26 | func (value Email) MarshalGQL(w io.Writer) { 27 | output, _ := json.Marshal(string(value)) 28 | w.Write(output) 29 | } 30 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/mutation_with_custom_scalar.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "regexp" 8 | ) 9 | 10 | var re = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") 11 | 12 | type Email string 13 | 14 | func (value *Email) UnmarshalGQL(v interface{}) error { 15 | input, ok := v.(string) 16 | if !ok { 17 | return fmt.Errorf("email expects a string value") 18 | } 19 | if !re.MatchString(input) { 20 | return fmt.Errorf("invalid email format") 21 | } 22 | *value = Email(input) 23 | return nil 24 | } 25 | 26 | func (value Email) MarshalGQL(w io.Writer) { 27 | output, _ := json.Marshal(string(value)) 28 | w.Write(output) 29 | } 30 | -------------------------------------------------------------------------------- /docs/static/external-link-alt.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /_examples/config/todo.resolvers.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | // Code generated by github.com/99designs/gqlgen version v0.17.27-dev 6 | 7 | import ( 8 | "context" 9 | "fmt" 10 | ) 11 | 12 | // ID is the resolver for the id field. 13 | func (r *todoResolver) ID(ctx context.Context, obj *Todo) (string, error) { 14 | if obj.ID != "" { 15 | return obj.ID, nil 16 | } 17 | 18 | obj.ID = fmt.Sprintf("TODO:%d", obj.DatabaseID) 19 | 20 | return obj.ID, nil 21 | } 22 | 23 | // Todo returns TodoResolver implementation. 24 | func (r *Resolver) Todo() TodoResolver { return &todoResolver{r} } 25 | 26 | type todoResolver struct{ *Resolver } 27 | -------------------------------------------------------------------------------- /graphql/handler/extension/introspection.go: -------------------------------------------------------------------------------- 1 | package extension 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/99designs/gqlgen/graphql" 7 | "github.com/vektah/gqlparser/v2/gqlerror" 8 | ) 9 | 10 | // EnableIntrospection enables clients to reflect all of the types available on the graph. 11 | type Introspection struct{} 12 | 13 | var _ interface { 14 | graphql.OperationContextMutator 15 | graphql.HandlerExtension 16 | } = Introspection{} 17 | 18 | func (c Introspection) ExtensionName() string { 19 | return "Introspection" 20 | } 21 | 22 | func (c Introspection) Validate(schema graphql.ExecutableSchema) error { 23 | return nil 24 | } 25 | 26 | func (c Introspection) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error { 27 | rc.DisableIntrospection = false 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /graphql/handler/lru/lru.go: -------------------------------------------------------------------------------- 1 | package lru 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/99designs/gqlgen/graphql" 7 | lru "github.com/hashicorp/golang-lru/v2" 8 | ) 9 | 10 | type LRU struct { 11 | lru *lru.Cache[string, any] 12 | } 13 | 14 | var _ graphql.Cache = &LRU{} 15 | 16 | func New(size int) *LRU { 17 | cache, err := lru.New[string, any](size) 18 | if err != nil { 19 | // An error is only returned for non-positive cache size 20 | // and we already checked for that. 21 | panic("unexpected error creating cache: " + err.Error()) 22 | } 23 | return &LRU{cache} 24 | } 25 | 26 | func (l LRU) Get(ctx context.Context, key string) (value interface{}, ok bool) { 27 | return l.lru.Get(key) 28 | } 29 | 30 | func (l LRU) Add(ctx context.Context, key string, value interface{}) { 31 | l.lru.Add(key, value) 32 | } 33 | -------------------------------------------------------------------------------- /_examples/federation/products/graph/schema.resolvers.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | // Code generated by github.com/99designs/gqlgen version v0.17.27-dev 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/99designs/gqlgen/_examples/federation/products/graph/model" 11 | ) 12 | 13 | // TopProducts is the resolver for the topProducts field. 14 | func (r *queryResolver) TopProducts(ctx context.Context, first *int) ([]*model.Product, error) { 15 | return hats, nil 16 | } 17 | 18 | // Query returns QueryResolver implementation. 19 | func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } 20 | 21 | type queryResolver struct{ *Resolver } 22 | -------------------------------------------------------------------------------- /.github/workflows/check-integration: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | cd integration 6 | 7 | date 8 | go run ./server/server.go & 9 | 10 | sleep 5 11 | curl -s --connect-timeout 5 \ 12 | --max-time 10 \ 13 | --retry 5 \ 14 | --retry-delay 5 \ 15 | --retry-max-time 40 \ 16 | --retry-connrefused \ 17 | localhost:8080 > /dev/null 18 | npm install -g typescript 19 | npm link typescript 20 | echo "### running jest integration spec" 21 | ./node_modules/.bin/jest --color 22 | 23 | 24 | echo "### validating introspected schema" 25 | ./node_modules/.bin/graphql-codegen 26 | 27 | if ! diff <(tail -n +3 schema-expected.graphql) <(tail -n +3 schema-fetched.graphql) ; then 28 | echo "The expected schema has changed, you need to update schema-expected.graphql with any expected changes" 29 | exit 1 30 | fi 31 | -------------------------------------------------------------------------------- /docs/layouts/index.html: -------------------------------------------------------------------------------- 1 | {{ define "main" }} 2 | {{ range where .Site.Pages "Type" "homepage" }} 3 |
4 | {{ .Description }} 5 |
6 |

{{ .LinkTitle }}

7 |
{{ .Title }}
8 |
9 |
10 | 11 |
12 |
13 | {{partial "version-banner"}} 14 | {{ .Content }} 15 | {{.Scratch.Set "intro" (readFile "content/_introduction.md")}} 16 | {{.Scratch.Set "intro" (split (.Scratch.Get "intro") "\n")}} 17 | {{.Scratch.Set "intro" (after 2 (.Scratch.Get "intro"))}} 18 | {{.Scratch.Set "intro" (delimit (.Scratch.Get "intro") "\n")}} 19 | {{.Scratch.Get "intro"|markdownify}} 20 |
21 |
22 | {{ end }} 23 | {{ end }} 24 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/typefallback_test.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestTypeFallback(t *testing.T) { 13 | resolvers := &Stub{} 14 | 15 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 16 | 17 | resolvers.QueryResolver.Fallback = func(ctx context.Context, arg FallbackToStringEncoding) (FallbackToStringEncoding, error) { 18 | return arg, nil 19 | } 20 | 21 | t.Run("fallback to string passthrough", func(t *testing.T) { 22 | var resp struct { 23 | Fallback string 24 | } 25 | c.MustPost(`query { fallback(arg: A) }`, &resp) 26 | require.Equal(t, "A", resp.Fallback) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /graphql/handler/transport/util.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/99designs/gqlgen/graphql" 9 | "github.com/vektah/gqlparser/v2/gqlerror" 10 | ) 11 | 12 | func writeJson(w io.Writer, response *graphql.Response) { 13 | b, err := json.Marshal(response) 14 | if err != nil { 15 | panic(err) 16 | } 17 | w.Write(b) 18 | } 19 | 20 | func writeJsonError(w io.Writer, msg string) { 21 | writeJson(w, &graphql.Response{Errors: gqlerror.List{{Message: msg}}}) 22 | } 23 | 24 | func writeJsonErrorf(w io.Writer, format string, args ...interface{}) { 25 | writeJson(w, &graphql.Response{Errors: gqlerror.List{{Message: fmt.Sprintf(format, args...)}}}) 26 | } 27 | 28 | func writeJsonGraphqlError(w io.Writer, err ...*gqlerror.Error) { 29 | writeJson(w, &graphql.Response{Errors: err}) 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: [push, pull_request] 3 | # When a new revision is pushed to a PR, cancel all in-progress CI runs for that 4 | # PR. See https://docs.github.com/en/actions/using-jobs/using-concurrency 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 7 | cancel-in-progress: true 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest, windows-latest] 13 | go: ["1.18", "1.20"] 14 | runs-on: ${{ matrix.os }} 15 | continue-on-error: true 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions/setup-go@v3 19 | with: 20 | go-version: ${{ matrix.go }} 21 | - run: go mod download && go test -race ./... 22 | - run: cd _examples && go mod download && go test -race ./... 23 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/typefallback_test.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestTypeFallback(t *testing.T) { 13 | resolvers := &Stub{} 14 | 15 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 16 | 17 | resolvers.QueryResolver.Fallback = func(ctx context.Context, arg FallbackToStringEncoding) (FallbackToStringEncoding, error) { 18 | return arg, nil 19 | } 20 | 21 | t.Run("fallback to string passthrough", func(t *testing.T) { 22 | var resp struct { 23 | Fallback string 24 | } 25 | c.MustPost(`query { fallback(arg: A) }`, &resp) 26 | require.Equal(t, "A", resp.Fallback) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /graphql/handler/transport/error.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | 8 | "github.com/99designs/gqlgen/graphql" 9 | "github.com/vektah/gqlparser/v2/gqlerror" 10 | ) 11 | 12 | // SendError sends a best effort error to a raw response writer. It assumes the client can understand the standard 13 | // json error response 14 | func SendError(w http.ResponseWriter, code int, errors ...*gqlerror.Error) { 15 | w.WriteHeader(code) 16 | b, err := json.Marshal(&graphql.Response{Errors: errors}) 17 | if err != nil { 18 | panic(err) 19 | } 20 | w.Write(b) 21 | } 22 | 23 | // SendErrorf wraps SendError to add formatted messages 24 | func SendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { 25 | SendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) 26 | } 27 | -------------------------------------------------------------------------------- /graphql/response.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "github.com/vektah/gqlparser/v2/gqlerror" 9 | ) 10 | 11 | // Errors are intentionally serialized first based on the advice in 12 | // https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107 13 | // and https://github.com/facebook/graphql/pull/384 14 | type Response struct { 15 | Errors gqlerror.List `json:"errors,omitempty"` 16 | Data json.RawMessage `json:"data"` 17 | Extensions map[string]interface{} `json:"extensions,omitempty"` 18 | } 19 | 20 | func ErrorResponse(ctx context.Context, messagef string, args ...interface{}) *Response { 21 | return &Response{ 22 | Errors: gqlerror.List{{Message: fmt.Sprintf(messagef, args...)}}, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /RELEASE-CHECKLIST.md: -------------------------------------------------------------------------------- 1 | # When gqlgen gets released, the following things need to happen 2 | Assuming the next version is $NEW_VERSION=v0.16.0 or something like that. 3 | 4 | 1. Run the https://github.com/99designs/gqlgen/blob/master/bin/release: 5 | ``` 6 | ./bin/release $NEW_VERSION 7 | ``` 8 | 2. git-chglog -o CHANGELOG.md 9 | 3. go generate ./...; cd _examples; go generate ./...; cd .. 10 | 4. git commit and push the CHANGELOG.md 11 | 5. Go to https://github.com/99designs/gqlgen/releases and draft new release, autogenerate the release notes, and Create a discussion for this release 12 | 6. Comment on the release discussion with any really important notes (breaking changes) 13 | 14 | I used https://github.com/git-chglog/git-chglog to automate the changelog maintenance process for now. We could just as easily use go releaser to make the whole thing automated. 15 | 16 | -------------------------------------------------------------------------------- /_examples/federation/reviews/graph/reviews.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | import "github.com/99designs/gqlgen/_examples/federation/reviews/graph/model" 4 | 5 | var reviews = []*model.Review{ 6 | { 7 | Body: "A highly effective form of birth control.", 8 | Product: &model.Product{ID: "111", Manufacturer: &model.Manufacturer{ID: "1234"}}, 9 | Author: &model.User{ID: "1234"}, 10 | }, 11 | { 12 | Body: "Fedoras are one of the most fashionable hats around and can look great with a variety of outfits.", 13 | Product: &model.Product{ID: "222", Manufacturer: &model.Manufacturer{ID: "2345"}}, 14 | Author: &model.User{ID: "1234"}, 15 | }, 16 | { 17 | Body: "This is the last straw. Hat you will wear. 11/10", 18 | Product: &model.Product{ID: "333", Manufacturer: &model.Manufacturer{ID: "2345"}}, 19 | Author: &model.User{ID: "7777"}, 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /graphql/error.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/vektah/gqlparser/v2/gqlerror" 8 | ) 9 | 10 | type ErrorPresenterFunc func(ctx context.Context, err error) *gqlerror.Error 11 | 12 | func DefaultErrorPresenter(ctx context.Context, err error) *gqlerror.Error { 13 | var gqlErr *gqlerror.Error 14 | if errors.As(err, &gqlErr) { 15 | return gqlErr 16 | } 17 | return gqlerror.WrapPath(GetPath(ctx), err) 18 | } 19 | 20 | func ErrorOnPath(ctx context.Context, err error) error { 21 | if err == nil { 22 | return nil 23 | } 24 | var gqlErr *gqlerror.Error 25 | if errors.As(err, &gqlErr) { 26 | if gqlErr.Path == nil { 27 | gqlErr.Path = GetPath(ctx) 28 | } 29 | // Return the original error to avoid losing any attached annotation 30 | return err 31 | } 32 | return gqlerror.WrapPath(GetPath(ctx), err) 33 | } 34 | -------------------------------------------------------------------------------- /plugin/modelgen/testdata/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/schema.graphql" 3 | 4 | exec: 5 | filename: out/ignored.go 6 | model: 7 | filename: out/generated.go 8 | 9 | models: 10 | ExistingModel: 11 | model: github.com/99designs/gqlgen/plugin/modelgen/out.ExistingModel 12 | ExistingInput: 13 | model: github.com/99designs/gqlgen/plugin/modelgen/out.ExistingInput 14 | ExistingEnum: 15 | model: github.com/99designs/gqlgen/plugin/modelgen/out.ExistingEnum 16 | ExistingInterface: 17 | model: github.com/99designs/gqlgen/plugin/modelgen/out.ExistingInterface 18 | ExistingUnion: 19 | model: github.com/99designs/gqlgen/plugin/modelgen/out.ExistingUnion 20 | ExistingType: 21 | model: github.com/99designs/gqlgen/plugin/modelgen/out.ExistingType 22 | RenameFieldTest: 23 | fields: 24 | badName: 25 | fieldName: GOODnaME 26 | 27 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/federation_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package subdir 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "strings" 9 | 10 | "github.com/99designs/gqlgen/plugin/federation/fedruntime" 11 | ) 12 | 13 | var ( 14 | ErrUnknownType = errors.New("unknown type") 15 | ErrTypeNotFound = errors.New("type not found") 16 | ) 17 | 18 | func (ec *executionContext) __resolve__service(ctx context.Context) (fedruntime.Service, error) { 19 | if ec.DisableIntrospection { 20 | return fedruntime.Service{}, errors.New("federated introspection disabled") 21 | } 22 | 23 | var sdl []string 24 | 25 | for _, src := range sources { 26 | if src.BuiltIn { 27 | continue 28 | } 29 | sdl = append(sdl, src.Input) 30 | } 31 | 32 | return fedruntime.Service{ 33 | SDL: strings.Join(sdl, "\n"), 34 | }, nil 35 | } 36 | -------------------------------------------------------------------------------- /api/testdata/federation2/graph/federation.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package graph 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "strings" 9 | 10 | "github.com/99designs/gqlgen/plugin/federation/fedruntime" 11 | ) 12 | 13 | var ( 14 | ErrUnknownType = errors.New("unknown type") 15 | ErrTypeNotFound = errors.New("type not found") 16 | ) 17 | 18 | func (ec *executionContext) __resolve__service(ctx context.Context) (fedruntime.Service, error) { 19 | if ec.DisableIntrospection { 20 | return fedruntime.Service{}, errors.New("federated introspection disabled") 21 | } 22 | 23 | var sdl []string 24 | 25 | for _, src := range sources { 26 | if src.BuiltIn { 27 | continue 28 | } 29 | sdl = append(sdl, src.Input) 30 | } 31 | 32 | return fedruntime.Service{ 33 | SDL: strings.Join(sdl, "\n"), 34 | }, nil 35 | } 36 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "*.graphql" 3 | skip_validation: true 4 | exec: 5 | filename: generated.go 6 | package: singlefile 7 | model: 8 | filename: models-gen.go 9 | package: singlefile 10 | resolver: 11 | filename: resolver.go 12 | package: singlefile 13 | type: Resolver 14 | 15 | autobind: 16 | - "github.com/99designs/gqlgen/codegen/testserver" 17 | - "github.com/99designs/gqlgen/codegen/testserver/singlefile" 18 | - "github.com/99designs/gqlgen/codegen/testserver/singlefile/introspection" 19 | - "github.com/99designs/gqlgen/codegen/testserver/singlefile/invalid-packagename" 20 | 21 | models: 22 | Email: 23 | model: "github.com/99designs/gqlgen/codegen/testserver/singlefile.Email" 24 | StringFromContextFunction: 25 | model: "github.com/99designs/gqlgen/codegen/testserver/singlefile.StringFromContextFunction" 26 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/gendir/federation_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package gendir 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "strings" 9 | 10 | "github.com/99designs/gqlgen/plugin/federation/fedruntime" 11 | ) 12 | 13 | var ( 14 | ErrUnknownType = errors.New("unknown type") 15 | ErrTypeNotFound = errors.New("type not found") 16 | ) 17 | 18 | func (ec *executionContext) __resolve__service(ctx context.Context) (fedruntime.Service, error) { 19 | if ec.DisableIntrospection { 20 | return fedruntime.Service{}, errors.New("federated introspection disabled") 21 | } 22 | 23 | var sdl []string 24 | 25 | for _, src := range sources { 26 | if src.BuiltIn { 27 | continue 28 | } 29 | sdl = append(sdl, src.Input) 30 | } 31 | 32 | return fedruntime.Service{ 33 | SDL: strings.Join(sdl, "\n"), 34 | }, nil 35 | } 36 | -------------------------------------------------------------------------------- /plugin/servergen/server.gotpl: -------------------------------------------------------------------------------- 1 | {{ reserveImport "context" }} 2 | {{ reserveImport "log" }} 3 | {{ reserveImport "net/http" }} 4 | {{ reserveImport "os" }} 5 | {{ reserveImport "github.com/99designs/gqlgen/graphql/playground" }} 6 | {{ reserveImport "github.com/99designs/gqlgen/graphql/handler" }} 7 | 8 | const defaultPort = "8080" 9 | 10 | func main() { 11 | port := os.Getenv("PORT") 12 | if port == "" { 13 | port = defaultPort 14 | } 15 | 16 | srv := handler.NewDefaultServer({{ lookupImport .ExecPackageName }}.NewExecutableSchema({{ lookupImport .ExecPackageName}}.Config{Resolvers: &{{ lookupImport .ResolverPackageName}}.Resolver{}})) 17 | 18 | http.Handle("/", playground.Handler("GraphQL playground", "/query")) 19 | http.Handle("/query", srv) 20 | 21 | log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) 22 | log.Fatal(http.ListenAndServe(":" + port, nil)) 23 | } 24 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/gqlgen.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "*.graphql" 3 | skip_validation: true 4 | exec: 5 | layout: follow-schema 6 | dir: . 7 | package: followschema 8 | model: 9 | filename: models-gen.go 10 | package: followschema 11 | resolver: 12 | filename: resolver.go 13 | package: followschema 14 | type: Resolver 15 | 16 | autobind: 17 | - "github.com/99designs/gqlgen/codegen/testserver" 18 | - "github.com/99designs/gqlgen/codegen/testserver/followschema" 19 | - "github.com/99designs/gqlgen/codegen/testserver/followschema/introspection" 20 | - "github.com/99designs/gqlgen/codegen/testserver/followschema/invalid-packagename" 21 | 22 | models: 23 | Email: 24 | model: "github.com/99designs/gqlgen/codegen/testserver/followschema.Email" 25 | StringFromContextFunction: 26 | model: "github.com/99designs/gqlgen/codegen/testserver/followschema.StringFromContextFunction" 27 | -------------------------------------------------------------------------------- /plugin/federation/test_data/model/federation.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type _FieldSet string //nolint:deadcode,unused 4 | 5 | type Hello struct { 6 | Name string 7 | Secondary string 8 | } 9 | 10 | func (Hello) IsEntity() {} 11 | 12 | type World struct { 13 | Foo string 14 | Bar int 15 | } 16 | 17 | func (World) IsEntity() {} 18 | 19 | type ExternalExtension struct { 20 | UPC string 21 | Reviews []*World 22 | } 23 | 24 | func (ExternalExtension) IsEntity() {} 25 | 26 | type NestedKey struct { 27 | ID string 28 | Hello *Hello 29 | } 30 | 31 | func (NestedKey) IsEntity() {} 32 | 33 | type MoreNesting struct { 34 | ID string 35 | World *World 36 | } 37 | 38 | func (MoreNesting) IsEntity() {} 39 | 40 | type VeryNestedKey struct { 41 | ID string 42 | Hello *Hello 43 | World *World 44 | Nested *NestedKey 45 | More *MoreNesting 46 | } 47 | 48 | func (VeryNestedKey) IsEntity() {} 49 | -------------------------------------------------------------------------------- /_examples/embedding/subdir/resolvers.go: -------------------------------------------------------------------------------- 1 | //go:generate go run ../../../testdata/gqlgen.go -config cfgdir/generate_in_subdir.yml 2 | //go:generate go run ../../../testdata/gqlgen.go -config cfgdir/generate_in_gendir.yml 3 | 4 | package subdir 5 | 6 | import ( 7 | "context" 8 | 9 | "github.com/99designs/gqlgen/_examples/embedding/subdir/gendir" 10 | ) 11 | 12 | type Resolver struct{ *Resolver } 13 | 14 | func (q *Resolver) Query() QueryResolver { 15 | return q 16 | } 17 | func (q *Resolver) InSchemadir(ctx context.Context) (string, error) { 18 | return "example", nil 19 | } 20 | func (q *Resolver) Parentdir(ctx context.Context) (string, error) { 21 | return "example", nil 22 | } 23 | func (q *Resolver) Subdir(ctx context.Context) (string, error) { 24 | return "example", nil 25 | } 26 | 27 | type GendirResolver struct{ *Resolver } 28 | 29 | func (q *GendirResolver) Query() gendir.QueryResolver { 30 | return &Resolver{} 31 | } 32 | -------------------------------------------------------------------------------- /plugin/federation/testdata/allthethings/model/federation.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type _FieldSet string //nolint:deadcode,unused 4 | 5 | type Hello struct { 6 | Name string 7 | Secondary string 8 | } 9 | 10 | func (Hello) IsEntity() {} 11 | 12 | type World struct { 13 | Foo string 14 | Bar int 15 | } 16 | 17 | func (World) IsEntity() {} 18 | 19 | type ExternalExtension struct { 20 | UPC string 21 | Reviews []*World 22 | } 23 | 24 | func (ExternalExtension) IsEntity() {} 25 | 26 | type NestedKey struct { 27 | ID string 28 | Hello *Hello 29 | } 30 | 31 | func (NestedKey) IsEntity() {} 32 | 33 | type MoreNesting struct { 34 | ID string 35 | World *World 36 | } 37 | 38 | func (MoreNesting) IsEntity() {} 39 | 40 | type VeryNestedKey struct { 41 | ID string 42 | Hello *Hello 43 | World *World 44 | Nested *NestedKey 45 | More *MoreNesting 46 | } 47 | 48 | func (VeryNestedKey) IsEntity() {} 49 | -------------------------------------------------------------------------------- /_examples/chat/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import styled from 'styled-components'; 3 | import { Room } from './Room'; 4 | 5 | const Input = styled.div` 6 | padding: 4px; 7 | margin: 0 0 4px; 8 | 9 | input { 10 | border: 1px solid #ccc; 11 | padding: 2px; 12 | font-size: 14px; 13 | } 14 | `; 15 | 16 | export const App = () => { 17 | const [name, setName] = useState('tester'); 18 | const [channel, setChannel] = useState('#gophers'); 19 | 20 | return ( 21 | <> 22 | 23 | name: setName(e.target.value)} /> 24 | 25 | 26 | channel: setChannel(e.target.value)} /> 27 | 28 | 29 | 30 | 31 | ); 32 | 33 | }; 34 | -------------------------------------------------------------------------------- /_examples/fileupload/schema.graphql: -------------------------------------------------------------------------------- 1 | "The `Upload` scalar type represents a multipart file upload." 2 | scalar Upload 3 | 4 | "The `File` type, represents the response of uploading a file." 5 | type File { 6 | id: Int! 7 | name: String! 8 | content: String! 9 | contentType: String! 10 | } 11 | 12 | "The `UploadFile` type, represents the request for uploading a file with certain payload." 13 | input UploadFile { 14 | id: Int! 15 | file: Upload! 16 | } 17 | 18 | "The `Query` type, represents all of the entry points into our object graph." 19 | type Query { 20 | empty: String! 21 | } 22 | 23 | "The `Mutation` type, represents all updates we can make to our data." 24 | type Mutation { 25 | singleUpload(file: Upload!): File! 26 | singleUploadWithPayload(req: UploadFile!): File! 27 | multipleUpload(files: [Upload!]!): [File!]! 28 | multipleUploadWithPayload(req: [UploadFile!]!): [File!]! 29 | } 30 | 31 | -------------------------------------------------------------------------------- /_examples/todo/schema.graphql: -------------------------------------------------------------------------------- 1 | schema { 2 | query: MyQuery 3 | mutation: MyMutation 4 | } 5 | 6 | type MyQuery { 7 | todo(id: ID!): Todo 8 | lastTodo: Todo 9 | todos: [Todo!]! 10 | } 11 | 12 | type MyMutation { 13 | createTodo(todo: TodoInput!): Todo! 14 | updateTodo(id: ID!, changes: Map!): Todo 15 | } 16 | 17 | type Todo { 18 | id: ID! 19 | text: String! 20 | done: Boolean! @hasRole(role: OWNER) # only the owner can see if a todo is done 21 | } 22 | 23 | "Passed to createTodo to create a new todo" 24 | input TodoInput { 25 | "The body text" 26 | text: String! 27 | "Is it done already?" 28 | done: Boolean 29 | } 30 | 31 | scalar Map 32 | 33 | "Prevents access to a field if the user doesnt have the matching role" 34 | directive @hasRole(role: Role!) on FIELD_DEFINITION 35 | directive @user(id: ID!) on MUTATION | QUERY | FIELD 36 | 37 | enum Role { 38 | ADMIN 39 | OWNER 40 | } 41 | -------------------------------------------------------------------------------- /_examples/federation/reviews/server.go: -------------------------------------------------------------------------------- 1 | //go:generate go run ../../../testdata/gqlgen.go 2 | package main 3 | 4 | import ( 5 | "log" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/99designs/gqlgen/_examples/federation/reviews/graph" 10 | "github.com/99designs/gqlgen/graphql/handler" 11 | "github.com/99designs/gqlgen/graphql/handler/debug" 12 | "github.com/99designs/gqlgen/graphql/playground" 13 | ) 14 | 15 | const defaultPort = "4003" 16 | 17 | func main() { 18 | port := os.Getenv("PORT") 19 | if port == "" { 20 | port = defaultPort 21 | } 22 | 23 | srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) 24 | srv.Use(&debug.Tracer{}) 25 | 26 | http.Handle("/", playground.Handler("GraphQL playground", "/query")) 27 | http.Handle("/query", srv) 28 | 29 | log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) 30 | log.Fatal(http.ListenAndServe(":"+port, nil)) 31 | } 32 | -------------------------------------------------------------------------------- /_examples/federation/accounts/server.go: -------------------------------------------------------------------------------- 1 | //go:generate go run ../../../testdata/gqlgen.go 2 | package main 3 | 4 | import ( 5 | "log" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/99designs/gqlgen/_examples/federation/accounts/graph" 10 | "github.com/99designs/gqlgen/graphql/handler" 11 | "github.com/99designs/gqlgen/graphql/handler/debug" 12 | "github.com/99designs/gqlgen/graphql/playground" 13 | ) 14 | 15 | const defaultPort = "4001" 16 | 17 | func main() { 18 | port := os.Getenv("PORT") 19 | if port == "" { 20 | port = defaultPort 21 | } 22 | 23 | srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) 24 | srv.Use(&debug.Tracer{}) 25 | 26 | http.Handle("/", playground.Handler("GraphQL playground", "/query")) 27 | http.Handle("/query", srv) 28 | 29 | log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) 30 | log.Fatal(http.ListenAndServe(":"+port, nil)) 31 | } 32 | -------------------------------------------------------------------------------- /_examples/federation/products/server.go: -------------------------------------------------------------------------------- 1 | //go:generate go run ../../../testdata/gqlgen.go 2 | package main 3 | 4 | import ( 5 | "log" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/99designs/gqlgen/_examples/federation/products/graph" 10 | "github.com/99designs/gqlgen/graphql/handler" 11 | "github.com/99designs/gqlgen/graphql/handler/debug" 12 | "github.com/99designs/gqlgen/graphql/playground" 13 | ) 14 | 15 | const defaultPort = "4002" 16 | 17 | func main() { 18 | port := os.Getenv("PORT") 19 | if port == "" { 20 | port = defaultPort 21 | } 22 | 23 | srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}})) 24 | srv.Use(&debug.Tracer{}) 25 | 26 | http.Handle("/", playground.Handler("GraphQL playground", "/query")) 27 | http.Handle("/query", srv) 28 | 29 | log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) 30 | log.Fatal(http.ListenAndServe(":"+port, nil)) 31 | } 32 | -------------------------------------------------------------------------------- /_examples/scalars/schema.graphql: -------------------------------------------------------------------------------- 1 | type Query { 2 | user(id: ID!): User 3 | search(input: SearchArgs = {location: "37,144", isBanned: false}): [User!]! 4 | userByTier(tier: Tier!, darkMode: DarkMode!): [User!]! 5 | } 6 | 7 | type User { 8 | id: ID! 9 | name: String! 10 | created: Timestamp 11 | modified: Timestamp 12 | valPrefs: DarkMode 13 | ptrPrefs: DarkMode 14 | isBanned: Banned! 15 | primitiveResolver: String! 16 | customResolver: Point! 17 | address: Address 18 | tier: Tier 19 | } 20 | 21 | type Address { 22 | id: ID! 23 | location: Point 24 | } 25 | 26 | input SearchArgs { 27 | location: Point 28 | createdAfter: Timestamp 29 | isBanned: Banned # TODO: This can be a Boolean again once multiple backing types are allowed 30 | } 31 | 32 | enum Tier { 33 | A 34 | B 35 | C 36 | } 37 | 38 | scalar Timestamp 39 | scalar Point 40 | scalar Banned 41 | scalar DarkMode 42 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/response_extension_test.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql" 9 | "github.com/99designs/gqlgen/graphql/handler" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestResponseExtension(t *testing.T) { 14 | resolvers := &Stub{} 15 | resolvers.QueryResolver.Valid = func(ctx context.Context) (s string, e error) { 16 | return "Ok", nil 17 | } 18 | 19 | srv := handler.NewDefaultServer( 20 | NewExecutableSchema(Config{Resolvers: resolvers}), 21 | ) 22 | 23 | srv.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 24 | graphql.RegisterExtension(ctx, "example", "value") 25 | 26 | return next(ctx) 27 | }) 28 | 29 | c := client.New(srv) 30 | 31 | raw, _ := c.RawPost(`query { valid }`) 32 | require.Equal(t, raw.Extensions["example"], "value") 33 | } 34 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/response_extension_test.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql" 9 | "github.com/99designs/gqlgen/graphql/handler" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestResponseExtension(t *testing.T) { 14 | resolvers := &Stub{} 15 | resolvers.QueryResolver.Valid = func(ctx context.Context) (s string, e error) { 16 | return "Ok", nil 17 | } 18 | 19 | srv := handler.NewDefaultServer( 20 | NewExecutableSchema(Config{Resolvers: resolvers}), 21 | ) 22 | 23 | srv.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 24 | graphql.RegisterExtension(ctx, "example", "value") 25 | 26 | return next(ctx) 27 | }) 28 | 29 | c := client.New(srv) 30 | 31 | raw, _ := c.RawPost(`query { valid }`) 32 | require.Equal(t, raw.Extensions["example"], "value") 33 | } 34 | -------------------------------------------------------------------------------- /_examples/federation/accounts/graph/schema.resolvers.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | // Code generated by github.com/99designs/gqlgen version v0.17.27-dev 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/99designs/gqlgen/_examples/federation/accounts/graph/model" 11 | ) 12 | 13 | // Me is the resolver for the me field. 14 | func (r *queryResolver) Me(ctx context.Context) (*model.User, error) { 15 | return &model.User{ 16 | ID: "1234", 17 | Host: &model.EmailHost{ 18 | ID: "4567", 19 | Name: "Email Host 4567", 20 | }, 21 | Email: "me@example.com", 22 | Username: "Me", 23 | }, nil 24 | } 25 | 26 | // Query returns QueryResolver implementation. 27 | func (r *Resolver) Query() QueryResolver { return &queryResolver{r} } 28 | 29 | type queryResolver struct{ *Resolver } 30 | -------------------------------------------------------------------------------- /codegen/type.go: -------------------------------------------------------------------------------- 1 | package codegen 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/99designs/gqlgen/codegen/config" 7 | ) 8 | 9 | func (b *builder) buildTypes() map[string]*config.TypeReference { 10 | ret := map[string]*config.TypeReference{} 11 | for _, ref := range b.Binder.References { 12 | processType(ret, ref) 13 | } 14 | return ret 15 | } 16 | 17 | func processType(ret map[string]*config.TypeReference, ref *config.TypeReference) { 18 | key := ref.UniquenessKey() 19 | if existing, found := ret[key]; found { 20 | // Simplistic check of content which is obviously different. 21 | existingGQL := fmt.Sprintf("%v", existing.GQL) 22 | newGQL := fmt.Sprintf("%v", ref.GQL) 23 | if existingGQL != newGQL { 24 | panic(fmt.Sprintf("non-unique key \"%s\", trying to replace %s with %s", key, existingGQL, newGQL)) 25 | } 26 | } 27 | ret[key] = ref 28 | 29 | if ref.IsSlice() || ref.IsPtrToSlice() || ref.IsPtrToPtr() { 30 | processType(ret, ref.Elem()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /_examples/starwars/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | 9 | "github.com/99designs/gqlgen/_examples/starwars" 10 | "github.com/99designs/gqlgen/_examples/starwars/generated" 11 | "github.com/99designs/gqlgen/graphql" 12 | "github.com/99designs/gqlgen/graphql/handler" 13 | "github.com/99designs/gqlgen/graphql/playground" 14 | ) 15 | 16 | func main() { 17 | srv := handler.NewDefaultServer(generated.NewExecutableSchema(starwars.NewResolver())) 18 | srv.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 19 | rc := graphql.GetFieldContext(ctx) 20 | fmt.Println("Entered", rc.Object, rc.Field.Name) 21 | res, err = next(ctx) 22 | fmt.Println("Left", rc.Object, rc.Field.Name, "=>", res, err) 23 | return res, err 24 | }) 25 | 26 | http.Handle("/", playground.Handler("Starwars", "/query")) 27 | http.Handle("/query", srv) 28 | 29 | log.Fatal(http.ListenAndServe(":8080", nil)) 30 | } 31 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/variadic_test.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/99designs/gqlgen/client" 10 | "github.com/99designs/gqlgen/graphql/handler" 11 | ) 12 | 13 | func TestVariadic(t *testing.T) { 14 | resolver := &Stub{} 15 | resolver.QueryResolver.VariadicModel = func(ctx context.Context) (*VariadicModel, error) { 16 | return &VariadicModel{}, nil 17 | } 18 | c := client.New(handler.NewDefaultServer( 19 | NewExecutableSchema(Config{Resolvers: resolver}), 20 | )) 21 | 22 | var resp struct { 23 | VariadicModel struct { 24 | Value string 25 | } 26 | } 27 | err := c.Post(`query { variadicModel { value(rank: 1) } }`, &resp) 28 | require.NoError(t, err) 29 | require.Equal(t, resp.VariadicModel.Value, "1") 30 | 31 | err = c.Post(`query { variadicModel { value(rank: 2) } }`, &resp) 32 | require.NoError(t, err) 33 | require.Equal(t, resp.VariadicModel.Value, "2") 34 | } 35 | -------------------------------------------------------------------------------- /plugin/modelgen/testdata/gqlgen_struct_field_pointers.yml: -------------------------------------------------------------------------------- 1 | schema: 2 | - "testdata/schema.graphql" 3 | 4 | exec: 5 | filename: out_struct_pointers/ignored.go 6 | model: 7 | filename: out_struct_pointers/generated.go 8 | 9 | struct_fields_always_pointers: false 10 | omit_getters: true 11 | 12 | models: 13 | ExistingModel: 14 | model: github.com/99designs/gqlgen/plugin/modelgen/out_struct_pointers.ExistingModel 15 | ExistingInput: 16 | model: github.com/99designs/gqlgen/plugin/modelgen/out_struct_pointers.ExistingInput 17 | ExistingEnum: 18 | model: github.com/99designs/gqlgen/plugin/modelgen/out_struct_pointers.ExistingEnum 19 | ExistingInterface: 20 | model: github.com/99designs/gqlgen/plugin/modelgen/out_struct_pointers.ExistingInterface 21 | ExistingUnion: 22 | model: github.com/99designs/gqlgen/plugin/modelgen/out_struct_pointers.ExistingUnion 23 | ExistingType: 24 | model: github.com/99designs/gqlgen/plugin/modelgen/out_struct_pointers.ExistingType 25 | 26 | -------------------------------------------------------------------------------- /plugin/resolvergen/testdata/singlefile/out/resolver.go: -------------------------------------------------------------------------------- 1 | package customresolver 2 | 3 | // THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES. 4 | 5 | import ( 6 | "context" 7 | ) 8 | 9 | type CustomResolverType struct{} 10 | 11 | // // foo 12 | func (r *queryCustomResolverType) Resolver(ctx context.Context) (*Resolver, error) { 13 | panic("not implemented") 14 | } 15 | 16 | // // foo 17 | func (r *resolverCustomResolverType) Name(ctx context.Context, obj *Resolver) (string, error) { 18 | panic("not implemented") 19 | } 20 | 21 | // Query returns QueryResolver implementation. 22 | func (r *CustomResolverType) Query() QueryResolver { return &queryCustomResolverType{r} } 23 | 24 | // Resolver returns ResolverResolver implementation. 25 | func (r *CustomResolverType) Resolver() ResolverResolver { return &resolverCustomResolverType{r} } 26 | 27 | type queryCustomResolverType struct{ *CustomResolverType } 28 | type resolverCustomResolverType struct{ *CustomResolverType } 29 | -------------------------------------------------------------------------------- /_examples/chat/src/graphql-sse.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ApolloLink, 3 | Operation, 4 | FetchResult, 5 | Observable, 6 | } from '@apollo/client/core'; 7 | import { print } from 'graphql'; 8 | import { createClient, ClientOptions, Client } from 'graphql-sse'; 9 | 10 | export class SSELink extends ApolloLink { 11 | private client: Client; 12 | 13 | constructor(options: ClientOptions) { 14 | super(); 15 | this.client = createClient(options); 16 | } 17 | 18 | public request(operation: Operation): Observable { 19 | return new Observable((sink) => { 20 | return this.client.subscribe( 21 | { ...operation, query: print(operation.query) }, 22 | { 23 | next: sink.next.bind(sink), 24 | complete: sink.complete.bind(sink), 25 | error: sink.error.bind(sink), 26 | }, 27 | ); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /_examples/federation/reviews/graph/schema.resolvers.go: -------------------------------------------------------------------------------- 1 | package graph 2 | 3 | // This file will be automatically regenerated based on the schema, any resolver implementations 4 | // will be copied through when generating and any unknown code will be moved to the end. 5 | // Code generated by github.com/99designs/gqlgen version v0.17.27-dev 6 | 7 | import ( 8 | "context" 9 | 10 | "github.com/99designs/gqlgen/_examples/federation/reviews/graph/model" 11 | ) 12 | 13 | // Reviews is the resolver for the reviews field. 14 | func (r *userResolver) Reviews(ctx context.Context, obj *model.User) ([]*model.Review, error) { 15 | var productReviews []*model.Review 16 | for _, review := range reviews { 17 | if review.Author.ID == obj.ID { 18 | productReviews = append(productReviews, review) 19 | } 20 | } 21 | return productReviews, nil 22 | } 23 | 24 | // User returns UserResolver implementation. 25 | func (r *Resolver) User() UserResolver { return &userResolver{r} } 26 | 27 | type userResolver struct{ *Resolver } 28 | -------------------------------------------------------------------------------- /internal/rewrite/rewriter_test.go: -------------------------------------------------------------------------------- 1 | package rewrite 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestRewriter(t *testing.T) { 12 | t.Run("default", func(t *testing.T) { 13 | r, err := New("testdata") 14 | require.NoError(t, err) 15 | 16 | body := r.GetMethodBody("Foo", "Method") 17 | require.Equal(t, ` 18 | // leading comment 19 | 20 | // field comment 21 | m.Field++ 22 | 23 | // trailing comment 24 | `, strings.ReplaceAll(body, "\r\n", "\n")) 25 | 26 | imps := r.ExistingImports("testdata/example.go") 27 | require.Len(t, imps, 2) 28 | assert.Equal(t, []Import{ 29 | { 30 | Alias: "", 31 | ImportPath: "fmt", 32 | }, 33 | { 34 | Alias: "lol", 35 | ImportPath: "bytes", 36 | }, 37 | }, imps) 38 | }) 39 | 40 | t.Run("out of scope dir", func(t *testing.T) { 41 | _, err := New("../../../out-of-gomod/package") 42 | require.Error(t, err) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /graphql/handler/transport/options.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | 7 | "github.com/99designs/gqlgen/graphql" 8 | ) 9 | 10 | // Options responds to http OPTIONS and HEAD requests 11 | type Options struct { 12 | // AllowedMethods is a list of allowed HTTP methods. 13 | AllowedMethods []string 14 | } 15 | 16 | var _ graphql.Transport = Options{} 17 | 18 | func (o Options) Supports(r *http.Request) bool { 19 | return r.Method == "HEAD" || r.Method == "OPTIONS" 20 | } 21 | 22 | func (o Options) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) { 23 | switch r.Method { 24 | case http.MethodOptions: 25 | w.Header().Set("Allow", o.allowedMethods()) 26 | w.WriteHeader(http.StatusOK) 27 | case http.MethodHead: 28 | w.WriteHeader(http.StatusMethodNotAllowed) 29 | } 30 | } 31 | 32 | func (o Options) allowedMethods() string { 33 | if len(o.AllowedMethods) == 0 { 34 | return "OPTIONS, GET, POST" 35 | } 36 | return strings.Join(o.AllowedMethods, ", ") 37 | } 38 | -------------------------------------------------------------------------------- /graphql/handler/transport/reader.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | ) 7 | 8 | type bytesReader struct { 9 | s *[]byte 10 | i int64 // current reading index 11 | } 12 | 13 | func (r *bytesReader) Read(b []byte) (n int, err error) { 14 | if r.s == nil { 15 | return 0, errors.New("byte slice pointer is nil") 16 | } 17 | if r.i >= int64(len(*r.s)) { 18 | return 0, io.EOF 19 | } 20 | n = copy(b, (*r.s)[r.i:]) 21 | r.i += int64(n) 22 | return 23 | } 24 | 25 | func (r *bytesReader) Seek(offset int64, whence int) (int64, error) { 26 | if r.s == nil { 27 | return 0, errors.New("byte slice pointer is nil") 28 | } 29 | var abs int64 30 | switch whence { 31 | case io.SeekStart: 32 | abs = offset 33 | case io.SeekCurrent: 34 | abs = r.i + offset 35 | case io.SeekEnd: 36 | abs = int64(len(*r.s)) + offset 37 | default: 38 | return 0, errors.New("invalid whence") 39 | } 40 | if abs < 0 { 41 | return 0, errors.New("negative position") 42 | } 43 | r.i = abs 44 | return abs, nil 45 | } 46 | -------------------------------------------------------------------------------- /plugin/federation/testdata/allthethings/schema.graphql: -------------------------------------------------------------------------------- 1 | type Hello @key(fields: "name") { 2 | name: String! 3 | secondary: String! 4 | } 5 | 6 | type World @key(fields: " foo ") @key(fields: "bar") { 7 | foo: String! 8 | bar: Int! 9 | } 10 | 11 | extend type ExternalExtension @key(fields: " upc ") { 12 | upc: String! @external 13 | reviews: [World] 14 | } 15 | 16 | extend type NestedKey @key(fields: "id hello { name}") { 17 | id: String! @external 18 | hello: Hello 19 | } 20 | 21 | extend type MoreNesting @key(fields: "id") { 22 | id: String! @external 23 | world: World! @external 24 | } 25 | 26 | extend type VeryNestedKey 27 | @key( 28 | fields: "id hello { name} world {foo } world{bar} more { world { foo }}" 29 | ) { 30 | id: String! @external 31 | hello: Hello 32 | world: World 33 | nested: NestedKey @requires(fields: "id hello {secondary }") 34 | more: MoreNesting 35 | } 36 | 37 | type Query { 38 | hello: Hello! 39 | world: World! 40 | } 41 | -------------------------------------------------------------------------------- /_examples/starwars/benchmarks_test.go: -------------------------------------------------------------------------------- 1 | package starwars 2 | 3 | import ( 4 | "net/http/httptest" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/99designs/gqlgen/_examples/starwars/generated" 9 | "github.com/99designs/gqlgen/graphql/handler" 10 | ) 11 | 12 | func BenchmarkSimpleQueryNoArgs(b *testing.B) { 13 | server := handler.NewDefaultServer(generated.NewExecutableSchema(NewResolver())) 14 | 15 | q := `{"query":"{ search(text:\"Luke\") { ... on Human { starships { name } } } }"}` 16 | 17 | var body strings.Reader 18 | r := httptest.NewRequest("POST", "/graphql", &body) 19 | r.Header.Set("Content-Type", "application/json") 20 | 21 | b.ReportAllocs() 22 | b.ResetTimer() 23 | 24 | rec := httptest.NewRecorder() 25 | for i := 0; i < b.N; i++ { 26 | body.Reset(q) 27 | rec.Body.Reset() 28 | server.ServeHTTP(rec, r) 29 | if rec.Body.String() != `{"data":{"search":[{"starships":[{"name":"X-Wing"},{"name":"Imperial shuttle"}]}]}}` { 30 | b.Fatalf("Unexpected response: %s", rec.Body.String()) 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /plugin/plugin.go: -------------------------------------------------------------------------------- 1 | // plugin package interfaces are EXPERIMENTAL. 2 | 3 | package plugin 4 | 5 | import ( 6 | "github.com/99designs/gqlgen/codegen" 7 | "github.com/99designs/gqlgen/codegen/config" 8 | "github.com/vektah/gqlparser/v2/ast" 9 | ) 10 | 11 | type Plugin interface { 12 | Name() string 13 | } 14 | 15 | type ConfigMutator interface { 16 | MutateConfig(cfg *config.Config) error 17 | } 18 | 19 | type CodeGenerator interface { 20 | GenerateCode(cfg *codegen.Data) error 21 | } 22 | 23 | // EarlySourceInjector is used to inject things that are required for user schema files to compile. 24 | type EarlySourceInjector interface { 25 | InjectSourceEarly() *ast.Source 26 | } 27 | 28 | // LateSourceInjector is used to inject more sources, after we have loaded the users schema. 29 | type LateSourceInjector interface { 30 | InjectSourceLate(schema *ast.Schema) *ast.Source 31 | } 32 | 33 | // Implementer is used to generate code inside resolvers 34 | type ResolverImplementer interface { 35 | Implement(field *codegen.Field) string 36 | } 37 | -------------------------------------------------------------------------------- /_examples/chat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chat", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@apollo/client": "^3.2.3", 7 | "apollo-utilities": "^1.0.26", 8 | "graphql": "^14.0.2", 9 | "graphql-sse": "^2.0.0", 10 | "graphql-tag": "^2.10.0", 11 | "graphql-ws": "^5.8.1", 12 | "react": "^16.6.3", 13 | "react-dom": "^16.6.3", 14 | "react-scripts": "^2.1.1", 15 | "styled-components": "^5.2.0", 16 | "subscriptions-transport-ws": "^0.9.5", 17 | "typescript": "^4.9.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "start:graphql-transport-ws": "REACT_APP_WS_PROTOCOL=graphql-transport-ws npm run start", 22 | "start:graphql-sse": "REACT_APP_SSE_PROTOCOL=true npm run start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test --env=jsdom", 25 | "eject": "react-scripts eject" 26 | }, 27 | "browserslist": [ 28 | ">0.2%", 29 | "not dead", 30 | "not ie <= 11", 31 | "not op_mini all" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/scalar_default_test.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestDefaultScalarImplementation(t *testing.T) { 13 | resolvers := &Stub{} 14 | 15 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 16 | 17 | resolvers.QueryResolver.DefaultScalar = func(ctx context.Context, arg string) (i string, e error) { 18 | return arg, nil 19 | } 20 | 21 | t.Run("with arg value", func(t *testing.T) { 22 | var resp struct{ DefaultScalar string } 23 | c.MustPost(`query { defaultScalar(arg: "fff") }`, &resp) 24 | require.Equal(t, "fff", resp.DefaultScalar) 25 | }) 26 | 27 | t.Run("with default value", func(t *testing.T) { 28 | var resp struct{ DefaultScalar string } 29 | c.MustPost(`query { defaultScalar }`, &resp) 30 | require.Equal(t, "default", resp.DefaultScalar) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/scalar_default_test.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestDefaultScalarImplementation(t *testing.T) { 13 | resolvers := &Stub{} 14 | 15 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 16 | 17 | resolvers.QueryResolver.DefaultScalar = func(ctx context.Context, arg string) (i string, e error) { 18 | return arg, nil 19 | } 20 | 21 | t.Run("with arg value", func(t *testing.T) { 22 | var resp struct{ DefaultScalar string } 23 | c.MustPost(`query { defaultScalar(arg: "fff") }`, &resp) 24 | require.Equal(t, "fff", resp.DefaultScalar) 25 | }) 26 | 27 | t.Run("with default value", func(t *testing.T) { 28 | var resp struct{ DefaultScalar string } 29 | c.MustPost(`query { defaultScalar }`, &resp) 30 | require.Equal(t, "default", resp.DefaultScalar) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /_examples/chat/readme.md: -------------------------------------------------------------------------------- 1 | # Chat App 2 | 3 | Example app using subscriptions to build a chat room. 4 | 5 | ### Server 6 | 7 | ```bash 8 | go run ./server/server.go 9 | ``` 10 | 11 | ### Client 12 | 13 | The react app uses two different implementation for the websocket link 14 | 15 | - [apollo-link-ws](https://www.apollographql.com/docs/react/api/link/apollo-link-ws) which uses the deprecated [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws) library 16 | - [graphql-ws](https://github.com/enisdenjo/graphql-ws) 17 | 18 | First you need to install the dependencies 19 | 20 | ```bash 21 | npm install 22 | ``` 23 | 24 | Then to run the app with the `apollo-link-ws` implementation do 25 | 26 | ```bash 27 | npm run start 28 | ``` 29 | 30 | or to run the app with the `graphql-ws` implementation (and the newer `graphql-transport-ws` protocol) do 31 | 32 | ```bash 33 | npm run start:graphql-transport-ws 34 | ``` 35 | 36 | or to run the app with the `graphql-sse` implementation do 37 | 38 | ```bash 39 | npm run start:graphql-sse 40 | ``` 41 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/ptr_to_slice_test.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestPtrToSlice(t *testing.T) { 13 | resolvers := &Stub{} 14 | 15 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 16 | 17 | resolvers.QueryResolver.PtrToSliceContainer = func(ctx context.Context) (wrappedStruct *PtrToSliceContainer, e error) { 18 | ptrToSliceContainer := PtrToSliceContainer{ 19 | PtrToSlice: &[]string{"hello"}, 20 | } 21 | return &ptrToSliceContainer, nil 22 | } 23 | 24 | t.Run("pointer to slice", func(t *testing.T) { 25 | var resp struct { 26 | PtrToSliceContainer struct { 27 | PtrToSlice []string 28 | } 29 | } 30 | 31 | err := c.Post(`query { ptrToSliceContainer { ptrToSlice }}`, &resp) 32 | require.NoError(t, err) 33 | 34 | require.Equal(t, []string{"hello"}, resp.PtrToSliceContainer.PtrToSlice) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/ptr_to_slice_test.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestPtrToSlice(t *testing.T) { 13 | resolvers := &Stub{} 14 | 15 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 16 | 17 | resolvers.QueryResolver.PtrToSliceContainer = func(ctx context.Context) (wrappedStruct *PtrToSliceContainer, e error) { 18 | ptrToSliceContainer := PtrToSliceContainer{ 19 | PtrToSlice: &[]string{"hello"}, 20 | } 21 | return &ptrToSliceContainer, nil 22 | } 23 | 24 | t.Run("pointer to slice", func(t *testing.T) { 25 | var resp struct { 26 | PtrToSliceContainer struct { 27 | PtrToSlice []string 28 | } 29 | } 30 | 31 | err := c.Post(`query { ptrToSliceContainer { ptrToSlice }}`, &resp) 32 | require.NoError(t, err) 33 | 34 | require.Equal(t, []string{"hello"}, resp.PtrToSliceContainer.PtrToSlice) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /graphql/cache.go: -------------------------------------------------------------------------------- 1 | package graphql 2 | 3 | import "context" 4 | 5 | // Cache is a shared store for APQ and query AST caching 6 | type Cache interface { 7 | // Get looks up a key's value from the cache. 8 | Get(ctx context.Context, key string) (value interface{}, ok bool) 9 | 10 | // Add adds a value to the cache. 11 | Add(ctx context.Context, key string, value interface{}) 12 | } 13 | 14 | // MapCache is the simplest implementation of a cache, because it can not evict it should only be used in tests 15 | type MapCache map[string]interface{} 16 | 17 | // Get looks up a key's value from the cache. 18 | func (m MapCache) Get(ctx context.Context, key string) (value interface{}, ok bool) { 19 | v, ok := m[key] 20 | return v, ok 21 | } 22 | 23 | // Add adds a value to the cache. 24 | func (m MapCache) Add(ctx context.Context, key string, value interface{}) { m[key] = value } 25 | 26 | type NoCache struct{} 27 | 28 | func (n NoCache) Get(ctx context.Context, key string) (value interface{}, ok bool) { return nil, false } 29 | func (n NoCache) Add(ctx context.Context, key string, value interface{}) {} 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 gqlgen authors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /codegen/testserver/singlefile/validtypes_test.go: -------------------------------------------------------------------------------- 1 | package singlefile 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestValidType(t *testing.T) { 13 | resolvers := &Stub{} 14 | resolvers.QueryResolver.ValidType = func(ctx context.Context) (validType *ValidType, e error) { 15 | return &ValidType{ 16 | DifferentCase: "new", 17 | DifferentCaseOld: "old", 18 | }, nil 19 | } 20 | 21 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 22 | 23 | t.Run("fields with differing cases can be distinguished", func(t *testing.T) { 24 | var resp struct { 25 | ValidType struct { 26 | New string `json:"differentCase"` 27 | Old string `json:"different_case"` 28 | } 29 | } 30 | err := c.Post(`query { validType { differentCase, different_case } }`, &resp) 31 | require.NoError(t, err) 32 | 33 | require.Equal(t, "new", resp.ValidType.New) 34 | require.Equal(t, "old", resp.ValidType.Old) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /codegen/testserver/followschema/validtypes_test.go: -------------------------------------------------------------------------------- 1 | package followschema 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/99designs/gqlgen/client" 8 | "github.com/99designs/gqlgen/graphql/handler" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestValidType(t *testing.T) { 13 | resolvers := &Stub{} 14 | resolvers.QueryResolver.ValidType = func(ctx context.Context) (validType *ValidType, e error) { 15 | return &ValidType{ 16 | DifferentCase: "new", 17 | DifferentCaseOld: "old", 18 | }, nil 19 | } 20 | 21 | c := client.New(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolvers}))) 22 | 23 | t.Run("fields with differing cases can be distinguished", func(t *testing.T) { 24 | var resp struct { 25 | ValidType struct { 26 | New string `json:"differentCase"` 27 | Old string `json:"different_case"` 28 | } 29 | } 30 | err := c.Post(`query { validType { differentCase, different_case } }`, &resp) 31 | require.NoError(t, err) 32 | 33 | require.Equal(t, "new", resp.ValidType.New) 34 | require.Equal(t, "old", resp.ValidType.Old) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /integration/schema.graphql: -------------------------------------------------------------------------------- 1 | "This directive does magical things" 2 | directive @magic(kind: Int) on FIELD_DEFINITION 3 | 4 | scalar Map 5 | 6 | type Element { 7 | child: Element! 8 | error: Boolean! 9 | mismatched: [Boolean!] 10 | } 11 | 12 | enum DATE_FILTER_OP { 13 | # multi 14 | # line 15 | # comment 16 | EQ 17 | NEQ 18 | GT 19 | GTE 20 | LT 21 | LTE 22 | } 23 | 24 | input DateFilter { 25 | value: String! 26 | timezone: String = "UTC" 27 | op: DATE_FILTER_OP = EQ 28 | } 29 | 30 | type Viewer { 31 | user: User 32 | } 33 | 34 | input ListCoercion { 35 | enumVal: [ErrorType] 36 | strVal: [String] 37 | intVal: [Int] 38 | scalarVal: [Map] 39 | } 40 | 41 | type Query { 42 | path: [Element] 43 | date(filter: DateFilter!): Boolean! 44 | viewer: Viewer 45 | jsonEncoding: String! 46 | error(type: ErrorType = NORMAL): Boolean! 47 | complexity(value: Int!): Boolean! 48 | coercion(value: [ListCoercion!]): Boolean! 49 | } 50 | 51 | enum ErrorType { 52 | CUSTOM 53 | NORMAL 54 | } 55 | 56 | # this is a comment with a `backtick` 57 | -------------------------------------------------------------------------------- /integration/schema-expected.graphql: -------------------------------------------------------------------------------- 1 | """This directive does magical things""" 2 | directive @magic(kind: Int) on FIELD_DEFINITION 3 | 4 | enum DATE_FILTER_OP { 5 | EQ 6 | GT 7 | GTE 8 | LT 9 | LTE 10 | NEQ 11 | } 12 | 13 | input DateFilter { 14 | op: DATE_FILTER_OP = EQ 15 | timezone: String = "UTC" 16 | value: String! 17 | } 18 | 19 | type Element { 20 | child: Element! 21 | error: Boolean! 22 | mismatched: [Boolean!] 23 | } 24 | 25 | enum ErrorType { 26 | CUSTOM 27 | NORMAL 28 | } 29 | 30 | input ListCoercion { 31 | enumVal: [ErrorType] 32 | intVal: [Int] 33 | scalarVal: [Map] 34 | strVal: [String] 35 | } 36 | 37 | scalar Map 38 | 39 | type Query { 40 | coercion(value: [ListCoercion!]): Boolean! 41 | complexity(value: Int!): Boolean! 42 | date(filter: DateFilter!): Boolean! 43 | error(type: ErrorType = NORMAL): Boolean! 44 | jsonEncoding: String! 45 | path: [Element] 46 | viewer: Viewer 47 | } 48 | 49 | type RemoteModelWithOmitempty { 50 | newDesc: String 51 | } 52 | 53 | type User { 54 | likes: [String!]! 55 | name: String! 56 | } 57 | 58 | type Viewer { 59 | user: User 60 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/99designs/gqlgen 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/gorilla/websocket v1.5.0 7 | github.com/hashicorp/golang-lru/v2 v2.0.1 8 | github.com/kevinmbeaulieu/eq-go v1.0.0 9 | github.com/logrusorgru/aurora/v3 v3.0.0 10 | github.com/matryer/moq v0.2.7 11 | github.com/mattn/go-colorable v0.1.13 12 | github.com/mattn/go-isatty v0.0.17 13 | github.com/mitchellh/mapstructure v1.5.0 14 | github.com/stretchr/testify v1.8.2 15 | github.com/urfave/cli/v2 v2.24.4 16 | github.com/vektah/gqlparser/v2 v2.5.1 17 | golang.org/x/text v0.7.0 18 | golang.org/x/tools v0.6.0 19 | google.golang.org/protobuf v1.28.1 20 | gopkg.in/yaml.v3 v3.0.1 21 | ) 22 | 23 | require ( 24 | github.com/agnivade/levenshtein v1.1.1 // indirect 25 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 26 | github.com/davecgh/go-spew v1.1.1 // indirect 27 | github.com/pmezard/go-difflib v1.0.0 // indirect 28 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 29 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect 30 | golang.org/x/mod v0.8.0 // indirect 31 | golang.org/x/sys v0.5.0 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /_examples/todo/models_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by github.com/99designs/gqlgen, DO NOT EDIT. 2 | 3 | package todo 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "strconv" 9 | ) 10 | 11 | // Passed to createTodo to create a new todo 12 | type TodoInput struct { 13 | // The body text 14 | Text string `json:"text"` 15 | // Is it done already? 16 | Done *bool `json:"done,omitempty"` 17 | } 18 | 19 | type Role string 20 | 21 | const ( 22 | RoleAdmin Role = "ADMIN" 23 | RoleOwner Role = "OWNER" 24 | ) 25 | 26 | var AllRole = []Role{ 27 | RoleAdmin, 28 | RoleOwner, 29 | } 30 | 31 | func (e Role) IsValid() bool { 32 | switch e { 33 | case RoleAdmin, RoleOwner: 34 | return true 35 | } 36 | return false 37 | } 38 | 39 | func (e Role) String() string { 40 | return string(e) 41 | } 42 | 43 | func (e *Role) UnmarshalGQL(v interface{}) error { 44 | str, ok := v.(string) 45 | if !ok { 46 | return fmt.Errorf("enums must be strings") 47 | } 48 | 49 | *e = Role(str) 50 | if !e.IsValid() { 51 | return fmt.Errorf("%s is not a valid Role", str) 52 | } 53 | return nil 54 | } 55 | 56 | func (e Role) MarshalGQL(w io.Writer) { 57 | fmt.Fprint(w, strconv.Quote(e.String())) 58 | } 59 | -------------------------------------------------------------------------------- /bin/release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | if ! [ $# -eq 1 ] ; then 6 | echo "usage: ./bin/release [version]" 7 | exit 1 8 | fi 9 | 10 | VERSION=$1 11 | 12 | if ! git diff-index --quiet HEAD -- ; then 13 | echo "uncommited changes on HEAD, aborting" 14 | exit 1 15 | fi 16 | 17 | if [[ ${VERSION:0:1} != "v" ]] ; then 18 | echo "version strings must start with v" 19 | exit 1 20 | fi 21 | 22 | git fetch origin 23 | git checkout origin/master 24 | 25 | cat > graphql/version.go < graphql/version.go <