Skip to content

Commit

Permalink
Merge branch 'master' into small-interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
mastercactapus committed Jun 23, 2018
2 parents a21f327 + a39c63a commit 2bbbe05
Show file tree
Hide file tree
Showing 14 changed files with 115 additions and 79 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ See the [docs](https://gqlgen.com/) for a getting started guide.
| --------: | :-------- | :-------- | :-------- | :-------- |
| Kind | schema first | schema first | run time types | struct first |
| Boilerplate | less | more | more | some |
| Docs | [docs](https://gqlgen.com) & [examples](https://github.com/vektah/gqlgen/tree/master/example) | [example](https://github.com/vektah/gqlgen/tree/master/example) | [examples](https://github.com/graph-gophers/graphql-go/tree/master/example/starwars) | [examples](https://github.com/graphql-go/graphql/tree/master/examples) | [example](https://github.com/samsarahq/thunder/tree/master/example)|
| Docs | [docs](https://gqlgen.com) & [examples](https://github.com/vektah/gqlgen/tree/master/example) | [examples](https://github.com/graph-gophers/graphql-go/tree/master/example/starwars) | [examples](https://github.com/graphql-go/graphql/tree/master/examples) | [examples](https://github.com/samsarahq/thunder/tree/master/example)|
| Query | :+1: | :+1: | :+1: | :+1: |
| Mutation | :+1: | :construction: [pr](https://github.com/graph-gophers/graphql-go/pull/182) | :+1: | :+1: |
| Subscription | :+1: | :construction: [pr](https://github.com/graph-gophers/graphql-go/pull/132) | :no_entry: [is](https://github.com/graphql-go/graphql/issues/207) | :+1: |
Expand Down
4 changes: 3 additions & 1 deletion codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ func fullPackageName(dir string, pkgName string) string {

for _, gopath := range filepath.SplitList(build.Default.GOPATH) {
gopath = filepath.Join(gopath, "src") + string(os.PathSeparator)
fullPkgName = strings.TrimPrefix(fullPkgName, gopath)
if strings.EqualFold(gopath, fullPkgName[0:len(gopath)]) {
fullPkgName = fullPkgName[len(gopath):]
}
}
return filepath.ToSlash(fullPkgName)
}
Expand Down
2 changes: 1 addition & 1 deletion codegen/templates/data.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion codegen/templates/models.gotpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file was generated by github.com/vektah/gqlgen, DO NOT EDIT
// Code generated by github.com/vektah/gqlgen, DO NOT EDIT.

package {{ .PackageName }}

Expand Down
5 changes: 4 additions & 1 deletion codegen/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ func bindObject(t types.Type, object *Object, imports Imports) error {
}

if structField := findField(underlying, field.GQLName); structField != nil {
prevModifiers := field.Type.Modifiers
field.Type.Modifiers = modifiersFromGoType(structField.Type())
field.GoVarName = structField.Name()

Expand All @@ -190,7 +191,9 @@ func bindObject(t types.Type, object *Object, imports Imports) error {
}

default:
return errors.Errorf("type mismatch on %s.%s, expected %s got %s\n", object.GQLType, field.GQLName, field.Type.FullSignature(), structField.Type())
// type mismatch, require custom resolver for field
field.GoVarName = ""
field.Type.Modifiers = prevModifiers
}
continue
}
Expand Down
163 changes: 97 additions & 66 deletions docs/content/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ You can find the finished code for this tutorial [here](https://github.com/vekta
Assuming you already have a working [go environment](https://golang.org/doc/install) you can simply go get:

```sh
go get github.com/vektah/gqlgen
go get -u github.com/vektah/gqlgen
```


Expand All @@ -39,26 +39,72 @@ type Todo {
}

type User {
id: ID!
name: String!
id: ID!
name: String!
}

type Query {
todos: [Todo!]!
}

input NewTodo {
text: String!
userId: String!
}

type Mutation {
createTodo(text: String!): Todo!
createTodo(input: NewTodo!): Todo!
}
```

Now lets write out our models:
graph/graph.go
`graph/graph.go`
```go
package graph


type User struct {
ID string
Name string
}

type Todo struct {
ID string
Text string
Done bool
UserID string
}

```

So we have our schema and our models. now we need to link them up:

`graph/types.json`
```json
{
"User": "github.com/vektah/gqlgen-tutorials/gettingstarted/graph.User",
"Todo": "github.com/vektah/gqlgen-tutorials/gettingstarted/graph.Todo"
}
```
This simply says, `User` in schema is backed by `graph.User` in go.


gqlgen then follows some pretty simple rules to match up all the fields:

1. If there is a property with that name and type, use it
2. If there is a method with that name and type, use it
3. Otherwise, add it to the Resolvers interface. This is the magic.

## Generate the bindings

Now that we have defined the shape of our data, and what actions can be taken we can ask gqlgen to convert the schema into code:

Lets generate the server now:

```bash
mkdir graph
cd graph
gqlgen -schema ../schema.graphql
gqlgen -types types.json -schema ../schema.graphql
```

gqlgen should have created two new files `generated.go` and `models_gen.go`. If we take a peek in both we can see what the server has generated:
Expand All @@ -70,37 +116,29 @@ func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema {
}

type Resolvers interface {
Mutation_createTodo(ctx context.Context, text string) (Todo, error)
Mutation_createTodo(ctx context.Context, input NewTodo) (Todo, error)
Query_todos(ctx context.Context) ([]Todo, error)
Todo_user(ctx context.Context, it *Todo) (User, error)
}

// graph/models_gen.go
type Todo struct {
ID string
Text string
Done bool
UserID string
Todo_user(ctx context.Context, obj *Todo) (User, error)
}

type User struct {
ID string
Name string
// graph/models_gen.go
type NewTodo struct {
Text string `json:"text"`
User int `json:"user"`
}

```

**Note**: ctx here is the golang context.Context, its used to pass per-request context like url params, tracing
information, cancellation, and also the current selection set. This makes it more like the `info` argument in
`graphql-js`. Because the caller will create an object to satisfy the interface, they can inject any dependencies in
directly.
Notice the `Todo_user` resolver? Thats gqlgen saying "I dont know how to get a User from a Todo, you tell me.". Its worked out everything else, for us.

For any missing models (like NewTodo) gqlgen will generate a go struct. This is usually only used for input types and one-off return values. Most of the time your types will be coming from the database, or an API client so binding is better than generating.

## Write the resolvers

Finally, we get to write some code!
All thats left for us to do now is fill in the blanks in that interface:

`graph/graph.go`
```go
// graph/graph.go
package graph

import (
Expand All @@ -109,6 +147,18 @@ import (
"math/rand"
)

type User struct {
ID string
Name string
}

type Todo struct {
ID string
Text string
Done bool
UserID string
}

type MyApp struct {
todos []Todo
}
Expand All @@ -132,8 +182,8 @@ func (a *MyApp) Todo_user(ctx context.Context, it *Todo) (User, error) {
}
```

`main.go`
```go
// main.go
package main

import (
Expand All @@ -158,13 +208,13 @@ func main() {

We now have a working server, to start it:
```bash
go run *.go
go run main.go
```

then open http://localhost:8080 in a browser. here are some queries to try:
```graphql
mutation createTodo {
createTodo(text:"test") {
createTodo(input:{text:"todo", user:"1"}) {
user {
id
}
Expand All @@ -174,70 +224,51 @@ mutation createTodo {
}

query findTodos {
todos {
text
done
user {
name
todos {
text
done
user {
name
}
}
}
}
```

## Customizing the models

Generated models are nice to get moving quickly, but you probably want control over them at some point. To do that
create a `graph/types.json`:
```json
{
"User": "github.com/vektah/gqlgen-tutorials/gettingstarted/graph.User"
}
```

and create the model yourself:
```go
// graph/graph.go
type User struct {
ID string
Name string
}
```

then regenerate, this time specifying the type map:

```bash
gqlgen -typemap types.json -schema ../schema.graphql
```

gqlgen will look at the user defined types and match the fields up finding fields and functions by matching names.


## Finishing touches

gqlgen is still unstable, and the APIs may change at any time. To prevent changes from ruining your day make sure
to lock your dependencies:

*Note*: If you dont have dep installed yet, you can get it [here](https://github.com/golang/dep)

First uninstall the global version we grabbed earlier. This is a good way to prevent version mismatch footguns.

```bash
rm ~/go/bin/gqlgen
rm -rf ~/go/src/github.com/vektah/gqlgen
```

Next install gorunpkg, its kind of like npx but only searches vendor.

```bash
dep init
dep ensure
go get github.com/vektah/gorunpkg
```

at the top of our main.go:
Now at the top of our graph.go:
```go
//go:generate gorunpkg github.com/vektah/gqlgen -typemap types.json -out generated.go -package main
//go:generate gorunpkg gqlgen -typemap types.json -schema ../schema.graphql

package main
package graph
```
**Note:** be careful formatting this, there must no space between the `//` and `go:generate`, and one empty line
between it and the `package main`.


This magic comment tells `go generate` what command to run when we want to regenerate our code. to do so run:
```go
go generate ./..
go generate ./...
```

*gorunpkg* will build and run the version of gqlgen we just installed into vendor with dep. This makes sure
Expand Down
Loading

0 comments on commit 2bbbe05

Please sign in to comment.