diff --git a/README.md b/README.md index d40c9bee0fc..8b4fcd51447 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ [gqlgen](https://github.com/99designs/gqlgen) is a Go library for building GraphQL servers without any fuss. gqlgen is: - - **Schema first** — Define your API using the GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). - - **Type safe** — You should never see `map[string]interface{}` here. - - **Codegen** — Let us generate the boring bits, so you can build your app quickly. +- **Schema first** — Define your API using the GraphQL [Schema Definition Language](http://graphql.org/learn/schema/). +- **Type safe** — You should never see `map[string]interface{}` here. +- **Codegen** — Let us generate the boring bits, so you can build your app quickly. [Feature Comparison](https://gqlgen.com/feature-comparison/) @@ -29,24 +29,30 @@ Read our [Contribution Guidelines](https://github.com/99designs/gqlgen/blob/mast ### How do I prevent fetching child objects that might not be used? When you have nested or recursive schema like this: + ```graphql type User { - id: ID! - name: String! - friends: [User!]! + id: ID! + name: String! + friends: [User!]! } ``` + You need to tell gqlgen that we should only fetch friends if the user requested it. There are two ways to do this. -1. Write the model yourself and leave off friends. +#### Custom Models + +Write a custom model that omits the Friends model: ```go type User struct { - Id int - Name string + ID int + Name string } ``` +And reference the model in `gqlgen.yml`: + ```yaml # gqlgen.yml models: @@ -54,7 +60,9 @@ models: model: github.com/you/pkg/model.User # go import path to the User struct above ``` -2. Keep using the generated model, and mark the field as requiring a resolver explicitly +#### Explicit Resolvers + +If you want to Keep using the generated model: mark the field as requiring a resolver explicitly in `gqlgen.yml`: ```yaml # gqlgen.yml @@ -66,33 +74,36 @@ models: ``` After doing either of the above and running generate we will need to provide a resolver for friends: + ```go func (r *userResolver) Friends(ctx context.Context, obj *User) ([]*User, error) { - // select * from user where friendid = obj.ID - return friends, nil + // select * from user where friendid = obj.ID + return friends, nil } ``` ### IDs are strings but I like ints, why cant I have ints? You can by remapping it in config: + ```yaml models: ID: # The GraphQL type ID is backed by model: - - github.com/99designs/gqlgen/graphql.IntID # An go integer - - github.com/99designs/gqlgen/graphql.ID # or a go string + - github.com/99designs/gqlgen/graphql.IntID # An go integer + - github.com/99designs/gqlgen/graphql.ID # or a go string ``` This means gqlgen will be able to automatically bind to strings or ints for models you have written yourself, but the first model in this list is used as the default type and it will always be used when: - - generating models based on schema - - as arguments in resolvers + +- Generating models based on schema +- As arguments in resolvers There isnt any way around this, gqlgen has no way to know what you want in a given context. ## Other Resources - - [Christopher Biscardi @ Gophercon UK 2018](https://youtu.be/FdURVezcdcw) - - [Introducing gqlgen: a GraphQL Server Generator for Go](https://99designs.com.au/blog/engineering/gqlgen-a-graphql-server-generator-for-go/) - - [Dive into GraphQL by Iván Corrales Solera](https://medium.com/@ivan.corrales.solera/dive-into-graphql-9bfedf22e1a) +- [Christopher Biscardi @ Gophercon UK 2018](https://youtu.be/FdURVezcdcw) +- [Introducing gqlgen: a GraphQL Server Generator for Go](https://99designs.com.au/blog/engineering/gqlgen-a-graphql-server-generator-for-go/) +- [Dive into GraphQL by Iván Corrales Solera](https://medium.com/@ivan.corrales.solera/dive-into-graphql-9bfedf22e1a) diff --git a/docs/content/config.md b/docs/content/config.md index 09713cc31bd..00902352dad 100644 --- a/docs/content/config.md +++ b/docs/content/config.md @@ -9,6 +9,7 @@ weight: -5 gqlgen can be configured using a `gqlgen.yml` file, by default it will be loaded from the current directory, or any parent directory. Example: + ```yml # You can pass a single schema file schema: schema.graphql @@ -44,7 +45,7 @@ resolver: # Optional, turns on binding to field names by tag provided struct_tag: json -# Optional, set to true if you prefer []*Thing over []Thing +# Optional, set to true if you prefer []Thing over []*Thing omit_slice_element_pointers: false # Instead of listing out every model like below, you can automatically bind to any matching types @@ -79,6 +80,7 @@ Everything has defaults, so add things as you need. gqlgen ships with some builtin directives that make it a little easier to manage wiring. To start using them you first need to define them: + ```graphql directive @goModel(model: String, models: [String!]) on OBJECT | INPUT_OBJECT @@ -90,7 +92,7 @@ directive @goModel(model: String, models: [String!]) on OBJECT directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITION | FIELD_DEFINITION ``` - + > Here be dragons > > gqlgen doesnt currently support user-configurable directives for SCALAR, ENUM, INTERFACE or UNION. This only works @@ -99,8 +101,8 @@ directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITI Now you can use these directives when defining types in your schema: ```graphql -type User @goModel(model:"github.com/my/app/models.User") { - id: ID! @goField(name:"todoId") - name: String! @goField(forceResolver: true) +type User @goModel(model: "github.com/my/app/models.User") { + id: ID! @goField(name: "todoId") + name: String! @goField(forceResolver: true) } ``` diff --git a/docs/content/reference/dataloaders.md b/docs/content/reference/dataloaders.md index c478947bc94..f1423511b2a 100644 --- a/docs/content/reference/dataloaders.md +++ b/docs/content/reference/dataloaders.md @@ -155,4 +155,4 @@ The generated UserLoader has a few other useful methods on it: - `LoadAll(keys)`: If you know up front you want a bunch users - `Prime(key, user)`: Used to sync state between similar loaders (usersById, usersByNote) -You can see the full working example [here](https://github.com/vektah/gqlgen-tutorials/tree/master/dataloader) +You can see the full working example [here](https://github.com/vektah/gqlgen-tutorials/tree/master/dataloader). diff --git a/docs/content/reference/file-upload.md b/docs/content/reference/file-upload.md index 116bc4f7a4e..4bf50422206 100644 --- a/docs/content/reference/file-upload.md +++ b/docs/content/reference/file-upload.md @@ -1,29 +1,32 @@ --- -title: 'File Upload' +title: "File Upload" description: How to upload files. linkTitle: File Upload -menu: { main: { parent: 'reference' } } +menu: { main: { parent: "reference" } } --- Graphql server has an already built-in Upload scalar to upload files using a multipart request. \ -It implements the following spec https://github.com/jaydenseric/graphql-multipart-request-spec, -that defines an interoperable multipart form field structure for GraphQL requests, used by -various file upload client implementations. +It implements the following spec https://github.com/jaydenseric/graphql-multipart-request-spec, +that defines an interoperable multipart form field structure for GraphQL requests, used by +various file upload client implementations. To use it you need to add the Upload scalar in your schema, and it will automatically add the marshalling behaviour to Go types. # Configuration + There are two specific options that can be configured for uploading files: + - uploadMaxSize \ -This option specifies the maximum number of bytes used to parse a request body as multipart/form-data. + This option specifies the maximum number of bytes used to parse a request body as multipart/form-data. - uploadMaxMemory \ -This option specifies the maximum number of bytes used to parse a request body as -multipart/form-data in memory, with the remainder stored on disk in temporary files. + This option specifies the maximum number of bytes used to parse a request body as + multipart/form-data in memory, with the remainder stored on disk in temporary files. # Examples ## Single file upload + For this use case, the schema could look like this. ```graphql @@ -41,7 +44,8 @@ type Mutation { } ``` -cURL can be used the make a query as follows: +cURL can be used the make a query as follows: + ``` curl localhost:4000/graphql \ -F operations='{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }' \ @@ -50,7 +54,8 @@ curl localhost:4000/graphql \ ``` That invokes the following operation: -``` + +```javascript { query: ` mutation($file: Upload!) { @@ -66,8 +71,9 @@ That invokes the following operation: ``` ## Multiple file upload + For this use case, the schema could look like this. - + ```graphql "The `Upload` scalar type represents a multipart file upload." scalar Upload @@ -97,9 +103,9 @@ type Mutation { ``` -cURL can be used the make a query as follows: +cURL can be used the make a query as follows: -``` +```bash curl localhost:4000/query \ -F operations='{ "query": "mutation($req: [UploadFile!]!) { multipleUpload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }' \ -F map='{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }' \ @@ -108,7 +114,8 @@ curl localhost:4000/query \ ``` That invokes the following operation: -``` + +```javascript { query: ` mutation($req: [UploadFile!]!) @@ -129,9 +136,9 @@ That invokes the following operation: id: 2, File, // c.txt } - ] + ] } } ``` -see the [example/fileupload](https://github.com/99designs/gqlgen/tree/master/example/fileupload) package for more examples. +See the [example/fileupload](https://github.com/99designs/gqlgen/tree/master/example/fileupload) package for more examples. diff --git a/docs/content/reference/plugins.md b/docs/content/reference/plugins.md index 7f8cfce0b0b..047da75c884 100644 --- a/docs/content/reference/plugins.md +++ b/docs/content/reference/plugins.md @@ -2,14 +2,14 @@ linkTitle: Plugins title: How to write plugins for gqlgen description: Use plugins to customize code generation and integrate with other libraries -menu: { main: { parent: 'reference' } } +menu: { main: { parent: "reference" } } --- Plugins provide a way to hook into the gqlgen code generation lifecycle. In order to use anything other than the default plugins you will need to create your own entrypoint: - ## Using a plugin + ```go // +build ignore @@ -36,7 +36,7 @@ func main() { } - err = api.Generate(cfg, + err = api.Generate(cfg, api.AddPlugin(yourplugin.New()), // This is the magic line ) if err != nil { @@ -45,18 +45,17 @@ func main() { } } -``` +``` ## Writing a plugin There are currently only two hooks: - - MutateConfig: Allows a plugin to mutate the config before codegen starts. This allows plugins to add - custom directives, define types, and implement resolvers. see - [modelgen](https://github.com/99designs/gqlgen/tree/master/plugin/modelgen) for an example - - GenerateCode: Allows a plugin to generate a new output file, see - [stubgen](https://github.com/99designs/gqlgen/tree/master/plugin/stubgen) for an example - -Take a look at [plugin.go](https://github.com/99designs/gqlgen/blob/master/plugin/plugin.go) for the full list of -available hooks. These are likely to change with each release. +- MutateConfig: Allows a plugin to mutate the config before codegen starts. This allows plugins to add + custom directives, define types, and implement resolvers. see + [modelgen](https://github.com/99designs/gqlgen/tree/master/plugin/modelgen) for an example +- GenerateCode: Allows a plugin to generate a new output file, see + [stubgen](https://github.com/99designs/gqlgen/tree/master/plugin/stubgen) for an example +Take a look at [plugin.go](https://github.com/99designs/gqlgen/blob/master/plugin/plugin.go) for the full list of +available hooks. These are likely to change with each release. diff --git a/docs/content/reference/scalars.md b/docs/content/reference/scalars.md index 02ea1fb7aa0..99d25d32304 100644 --- a/docs/content/reference/scalars.md +++ b/docs/content/reference/scalars.md @@ -2,12 +2,12 @@ linkTitle: Scalars title: Mapping GraphQL scalar types to Go types description: Mapping GraphQL scalar types to Go types -menu: { main: { parent: 'reference' } } +menu: { main: { parent: "reference" } } --- ## Built-in helpers -gqlgen ships with three built-in helpers for common custom scalar use-cases, `Time`, `Any`, `Upload` and `Map`. Adding any of these to a schema will automatically add the marshalling behaviour to Go types. +gqlgen ships with three built-in helpers for common custom scalar use-cases, `Time`, `Any`, `Upload` and `Map`. Adding any of these to a schema will automatically add the marshalling behaviour to Go types. ### Time @@ -32,13 +32,15 @@ scalar Upload ``` Maps a `Upload` GraphQL scalar to a `graphql.Upload` struct, defined as follows: -``` + +```go type Upload struct { File io.Reader Filename string Size int64 } ``` + ### Any ```graphql @@ -47,7 +49,7 @@ scalar Any Maps an arbitrary GraphQL value to a `interface{}` Go type. -## Custom scalars with user defined types +## Custom scalars with user defined types For user defined types you can implement the graphql.Marshaler and graphql.Unmarshaler interfaces and they will be called. @@ -88,13 +90,13 @@ func (y YesNo) MarshalGQL(w io.Writer) { ``` and then in .gqlgen.yml point to the name without the Marshal|Unmarshal in front: + ```yaml models: YesNo: model: github.com/me/mypkg.YesNo ``` - ## Custom scalars with third party types Sometimes you cant add methods to a type because its in another repo, part of the standard @@ -136,11 +138,12 @@ func UnmarshalMyCustomBooleanScalar(v interface{}) (bool, error) { } ``` -and then in .gqlgen.yml point to the name without the Marshal|Unmarshal in front: +Then in .gqlgen.yml point to the name without the Marshal|Unmarshal in front: + ```yaml models: MyCustomBooleanScalar: model: github.com/me/mypkg.MyCustomBooleanScalar ``` -see the [example/scalars](https://github.com/99designs/gqlgen/tree/master/example/scalars) package for more examples. +See the [example/scalars](https://github.com/99designs/gqlgen/tree/master/example/scalars) package for more examples.