Skip to content

Commit

Permalink
Merge pull request 99designs#56 from vektah/getting-started-fixes
Browse files Browse the repository at this point in the history
Getting started fixes
  • Loading branch information
vektah authored Mar 18, 2018
2 parents 5985c41 + 0612395 commit 0dfb2cf
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 56 deletions.
20 changes: 16 additions & 4 deletions codegen/models_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ func buildModels(types NamedTypes, s *schema.Schema, prog *loader.Program) []Mod
if obj.Root || obj.GoType != "" {
continue
}
model = obj2Model(obj)
model = obj2Model(s, obj)
case *schema.InputObject:
obj := buildInput(types, typ)
if obj.GoType != "" {
continue
}
model = obj2Model(obj)
model = obj2Model(s, obj)
case *schema.Interface, *schema.Union:
intf := buildInterface(types, typ, prog)
if intf.GoType != "" {
Expand All @@ -46,7 +46,7 @@ func buildModels(types NamedTypes, s *schema.Schema, prog *loader.Program) []Mod
return models
}

func obj2Model(obj *Object) Model {
func obj2Model(s *schema.Schema, obj *Object) Model {
model := Model{
NamedType: obj.NamedType,
Fields: []ModelField{},
Expand All @@ -66,9 +66,21 @@ func obj2Model(obj *Object) Model {
}
} else if mf.IsInput {
mf.GoVarName = ucFirst(field.GQLName)
} else if mf.IsSlice() {
// one to many, we don't need a prop, we need a resolver
} else {
mf.GoFKName = ucFirst(field.GQLName) + "ID"
mf.GoFKType = "int" // todo: use schema to determine type of id?
mf.GoFKType = "string"

if obj, ok := s.Types[field.GQLType].(*schema.Object); ok {
for _, f := range obj.Fields {
if strings.EqualFold(f.Name, "id") {
if strings.Contains(f.Type.String(), "Int") {
mf.GoFKType = "int"
}
}
}
}
}

model.Fields = append(model.Fields, mf)
Expand Down
3 changes: 2 additions & 1 deletion codegen/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ func (t *Type) StripPtr() {
}

func (t Type) IsSlice() bool {
return len(t.Modifiers) > 0 && t.Modifiers[0] == modList
return len(t.Modifiers) > 0 && t.Modifiers[0] == modList ||
len(t.Modifiers) > 1 && t.Modifiers[0] == modPtr && t.Modifiers[1] == modList
}

func (t NamedType) IsMarshaled() bool {
Expand Down
62 changes: 33 additions & 29 deletions docs/content/tutorial/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The aim for this tutorial is to build a "todo" graphql server that can:
- create new todos
- mark off todos as they are completed

You can find the finished code for this tutorial [here](https://github.com/vektah/gqlgen-tutorials/tree/master/gettingstarted)

## Install gqlgen

Expand Down Expand Up @@ -54,13 +55,15 @@ type Mutation {

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:
```bash
gqlgen -out generated.go -package main
mkdir graph
cd graph
gqlgen -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:

```go
// generated.go
// graph/generated.go
func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema {
return &executableSchema{resolvers}
}
Expand All @@ -71,7 +74,7 @@ type Resolvers interface {
Todo_user(ctx context.Context, it *Todo) (User, error)
}

// models_gen.go
// graph/models_gen.go
type Todo struct {
ID string
Text string
Expand All @@ -96,23 +99,19 @@ directly.
Finally, we get to write some code!

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

import (
"context"
"fmt"
"log"
"math/rand"
"net/http"

"github.com/vektah/gqlgen/handler"
)

type MyApp struct {
todos []Todo
}

func (a *MyApp) Query_todos(ctx context.Context) ([]Todo, error) {
return a.todos, nil
}
Expand All @@ -127,21 +126,33 @@ func (a *MyApp) Mutation_createTodo(ctx context.Context, text string) (Todo, err
return todo, nil
}


func (a *MyApp) Todo_user(ctx context.Context, it *Todo) (User, error) {
return User{ID: it.UserID, Name: "user " + it.UserID}, nil
}
```

```go
// main.go
package main

import (
"fmt"
"log"
"net/http"

"github.com/vektah/gqlgen-tutorials/gettingstarted/graph"
"github.com/vektah/gqlgen/handler"
)

func main() {
app := &MyApp{
todos: []Todo{}, // this would normally be a reference to the db
}
app := &graph.MyApp{}
http.Handle("/", handler.Playground("Todo", "/query"))
http.Handle("/query", handler.GraphQL(MakeExecutableSchema(app)))
http.Handle("/query", handler.GraphQL(graph.MakeExecutableSchema(app)))

fmt.Println("Listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}

```

We now have a working server, to start it:
Expand Down Expand Up @@ -175,33 +186,26 @@ query findTodos {
## 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 types.json, eg:
create a `graph/types.json`:
```json
{
"Todo": "github.com/vektah/gettingstarted.Todo"
"User": "github.com/vektah/gqlgen-tutorials/gettingstarted/graph.User"
}
```

and create the model yourself:
```go
type Todo struct {
ID string
Text string
done bool
userID string // I've made userID private now.
// graph/graph.go
type User struct {
ID string
Name string
}

// lets define a getter too. it could also return an error if we needed.
func (t Todo) Done() bool {
return t.done
}

```

then regenerate, this time specifying the type map:

```bash
gqlgen -out generated.go -package main -typemap types.json
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.
Expand Down
8 changes: 3 additions & 5 deletions example/dataloader/models_gen.go

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

33 changes: 16 additions & 17 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"golang.org/x/tools/imports"
)

var output = flag.String("out", "-", "the file to write to, - for stdout")
var output = flag.String("out", "generated.go", "the file to write to")
var models = flag.String("models", "models_gen.go", "the file to write the models to")
var schemaFilename = flag.String("schema", "schema.graphql", "the graphql schema to generate types from")
var typemap = flag.String("typemap", "", "a json map going from graphql to golang types")
Expand Down Expand Up @@ -49,15 +49,17 @@ func main() {
os.Exit(1)
}

if *output != "-" {
_ = syscall.Unlink(*output)
}
_ = syscall.Unlink(*output)
_ = syscall.Unlink(*models)

types := loadTypeMap()

modelsBuild := codegen.Models(schema, types, dirName())
if len(modelsBuild.Models) > 0 {
if *packageName != "" {
modelsBuild.PackageName = *packageName
}

buf, err := templates.Run("models.gotpl", modelsBuild)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to generate code: "+err.Error())
Expand Down Expand Up @@ -102,20 +104,17 @@ func gofmt(filename string, b []byte) []byte {
}

func write(filename string, b []byte) {
if filename == "-" {
fmt.Println(string(gofmt(filename, b)))
} else {
err := os.MkdirAll(filepath.Dir(filename), 0755)
if err != nil {
fmt.Fprintln(os.Stderr, "failed to create directory: ", err.Error())
os.Exit(1)
}
fmt.Println(filename)
err := os.MkdirAll(filepath.Dir(filename), 0755)
if err != nil {
fmt.Fprintln(os.Stderr, "failed to create directory: ", err.Error())
os.Exit(1)
}

err = ioutil.WriteFile(filename, gofmt(filename, b), 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to write %s: %s", filename, err.Error())
os.Exit(1)
}
err = ioutil.WriteFile(filename, gofmt(filename, b), 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to write %s: %s", filename, err.Error())
os.Exit(1)
}
}

Expand Down

0 comments on commit 0dfb2cf

Please sign in to comment.