Skip to content

Commit

Permalink
Update getting started docs
Browse files Browse the repository at this point in the history
  • Loading branch information
vektah committed Feb 3, 2020
1 parent c233876 commit 59caad6
Show file tree
Hide file tree
Showing 17 changed files with 319 additions and 605 deletions.
145 changes: 93 additions & 52 deletions cmd/init.go
Expand Up @@ -3,28 +3,74 @@ package cmd
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/99designs/gqlgen/api"
"github.com/99designs/gqlgen/plugin/servergen"

"github.com/99designs/gqlgen/codegen/config"
"github.com/pkg/errors"
"github.com/99designs/gqlgen/internal/code"
"github.com/99designs/gqlgen/plugin/servergen"
"github.com/urfave/cli"
yaml "gopkg.in/yaml.v2"
)

var configComment = `
# .gqlgen.yml example
#
# Refer to https://gqlgen.com/config/
# for detailed .gqlgen.yml documentation.
`
var configTemplate = template.Must(template.New("name").Parse(
`# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- graph/*.graphqls
# Where should the generated server code go?
exec:
filename: graph/generated/generated.go
package: generated
# Where should any generated models go?
model:
filename: graph/model/models_gen.go
package: model
# Where should the resolver implementations go?
resolver:
layout: follow-schema
dir: graph
package: graph
# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
# struct_tag: json
var schemaDefault = `
# GraphQL schema example
# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: false
# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true
# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
- "{{.}}/graph/model"
# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
ID:
model:
- github.com/99designs/gqlgen/graphql.ID
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
Int:
model:
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
`))

var schemaDefault = `# GraphQL schema example
#
# https://gqlgen.com/getting-started/
Expand Down Expand Up @@ -60,14 +106,25 @@ var initCmd = cli.Command{
Flags: []cli.Flag{
cli.BoolFlag{Name: "verbose, v", Usage: "show logs"},
cli.StringFlag{Name: "config, c", Usage: "the config filename"},
cli.StringFlag{Name: "server", Usage: "where to write the server stub to", Value: "server/server.go"},
cli.StringFlag{Name: "schema", Usage: "where to write the schema stub to", Value: "schema.graphql"},
cli.StringFlag{Name: "server", Usage: "where to write the server stub to", Value: "server.go"},
cli.StringFlag{Name: "schema", Usage: "where to write the schema stub to", Value: "graph/schema.graphqls"},
},
Action: func(ctx *cli.Context) {
configFilename := ctx.String("config")
serverFilename := ctx.String("server")

pkgName := code.ImportPathForDir(".")
if pkgName == "" {
fmt.Fprintln(os.Stderr, "unable to determine import path for current directory, you probably need to run go mod init first")
os.Exit(1)
}

initSchema(ctx.String("schema"))
initConfig(ctx)
if !configExists(configFilename) {
initConfig(configFilename, pkgName)
}

GenerateGraphServer(ctx.String("server"))
GenerateGraphServer(serverFilename)
},
}

Expand All @@ -76,59 +133,39 @@ func GenerateGraphServer(serverFilename string) {
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
}
err = api.Generate(cfg, api.AddPlugin(servergen.New(serverFilename)))
if err != nil {

if err := api.Generate(cfg, api.AddPlugin(servergen.New(serverFilename))); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
}

fmt.Fprintf(os.Stdout, "Exec \"go run ./%s\" to start GraphQL server\n", serverFilename)
}

func initConfig(ctx *cli.Context) {
func configExists(configFilename string) bool {
var cfg *config.Config
var err error
configFilename := ctx.String("config")

if configFilename != "" {
cfg, err = config.LoadConfig(configFilename)
cfg, _ = config.LoadConfig(configFilename)
} else {
cfg, err = config.LoadConfigFromDefaultLocations()
}

if cfg != nil {
fmt.Fprintf(os.Stderr, "init failed: a configuration file already exists\n")
os.Exit(1)
}

if !os.IsNotExist(errors.Cause(err)) {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
cfg, _ = config.LoadConfigFromDefaultLocations()
}
return cfg != nil
}

func initConfig(configFilename string, pkgName string) {
if configFilename == "" {
configFilename = "gqlgen.yml"
}
cfg = config.DefaultConfig()

cfg.Resolver = config.ResolverConfig{
Type: "Resolver",
Layout: config.LayoutFollowSchema,
DirName: ".",
if err := os.MkdirAll(filepath.Dir(configFilename), 0755); err != nil {
fmt.Fprintln(os.Stderr, "unable to create config dir: "+err.Error())
os.Exit(1)
}
cfg.SchemaFilename = config.StringList{ctx.String("schema")}

var buf bytes.Buffer
buf.WriteString(strings.TrimSpace(configComment))
buf.WriteString("\n\n")
var b []byte
b, err = yaml.Marshal(cfg)
if err != nil {
fmt.Fprintln(os.Stderr, "unable to marshal yaml: "+err.Error())
os.Exit(1)
}
buf.Write(b)
configTemplate.Execute(&buf, pkgName)

err = ioutil.WriteFile(configFilename, buf.Bytes(), 0644)
if err != nil {
if err := ioutil.WriteFile(configFilename, buf.Bytes(), 0644); err != nil {
fmt.Fprintln(os.Stderr, "unable to write cfg file: "+err.Error())
os.Exit(1)
}
Expand All @@ -140,8 +177,12 @@ func initSchema(schemaFilename string) {
return
}

err = ioutil.WriteFile(schemaFilename, []byte(strings.TrimSpace(schemaDefault)), 0644)
if err != nil {
if err := os.MkdirAll(filepath.Dir(schemaFilename), 0755); err != nil {
fmt.Fprintln(os.Stderr, "unable to create schema dir: "+err.Error())
os.Exit(1)
}

if err = ioutil.WriteFile(schemaFilename, []byte(strings.TrimSpace(schemaDefault)), 0644); err != nil {
fmt.Fprintln(os.Stderr, "unable to write schema file: "+err.Error())
os.Exit(1)
}
Expand Down
1 change: 0 additions & 1 deletion codegen/config/package.go
Expand Up @@ -12,7 +12,6 @@ import (
type PackageConfig struct {
Filename string `yaml:"filename,omitempty"`
Package string `yaml:"package,omitempty"`
Type string `yaml:"type,omitempty"`
}

func (c *PackageConfig) ImportPath() string {
Expand Down
3 changes: 3 additions & 0 deletions codegen/config/resolver.go
Expand Up @@ -28,6 +28,9 @@ func (r *ResolverConfig) Check() error {
if r.Layout == "" {
r.Layout = LayoutSingleFile
}
if r.Type == "" {
r.Type = "Resolver"
}

switch r.Layout {
case LayoutSingleFile:
Expand Down
7 changes: 6 additions & 1 deletion codegen/config/resolver_test.go
Expand Up @@ -13,6 +13,7 @@ func TestResolverConfig(t *testing.T) {
p := ResolverConfig{Filename: "testdata/example.go"}
require.True(t, p.IsDefined())

require.NoError(t, p.Check())
require.NoError(t, p.Check())

require.Equal(t, p.Package, "config_test_data")
Expand All @@ -29,6 +30,7 @@ func TestResolverConfig(t *testing.T) {
p := ResolverConfig{Filename: "testdata/example.go", Package: "wololo"}
require.True(t, p.IsDefined())

require.NoError(t, p.Check())
require.NoError(t, p.Check())

require.Equal(t, p.Package, "wololo")
Expand Down Expand Up @@ -76,6 +78,7 @@ func TestResolverConfig(t *testing.T) {
p := ResolverConfig{Layout: LayoutFollowSchema, DirName: "testdata"}
require.True(t, p.IsDefined())

require.NoError(t, p.Check())
require.NoError(t, p.Check())

require.Equal(t, p.Package, "config_test_data")
Expand All @@ -92,6 +95,7 @@ func TestResolverConfig(t *testing.T) {
p := ResolverConfig{Layout: LayoutFollowSchema, DirName: "testdata", Package: "wololo"}
require.True(t, p.IsDefined())

require.NoError(t, p.Check())
require.NoError(t, p.Check())

require.Equal(t, p.Package, "wololo")
Expand All @@ -105,9 +109,10 @@ func TestResolverConfig(t *testing.T) {
})

t.Run("when given a filename", func(t *testing.T) {
p := ResolverConfig{Layout: LayoutFollowSchema, DirName: "testdata", Filename: "asdf.go"}
p := ResolverConfig{Layout: LayoutFollowSchema, DirName: "testdata", Filename: "testdata/asdf.go"}
require.True(t, p.IsDefined())

require.NoError(t, p.Check())
require.NoError(t, p.Check())

require.Equal(t, p.Package, "config_test_data")
Expand Down
90 changes: 39 additions & 51 deletions docs/content/config.md
Expand Up @@ -11,70 +11,58 @@ gqlgen can be configured using a `gqlgen.yml` file, by default it will be loaded
Example:

```yml
# You can pass a single schema file
schema: schema.graphql

# Or multiple files
schema:
- schema.graphql
- user.graphql

# Or you can use globs
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- "*.graphql"
- graph/*.graphqls

# Or globs from a root directory
schema:
- "schema/**/*.graphql"

# Let gqlgen know where to put the generated server
# Where should the generated server code go?
exec:
filename: graph/generated/generated.go
package: generated

# Let gqlgen know where to put the generated models (if any)
# Set to null to disable
# Where should any generated models go?
model:
filename: models/generated.go
package: models
filename: graph/model/models_gen.go
package: model

# Optional, turns on resolver stub generation
# Where should the resolver implementations go?
resolver:
filename: resolver.go # where to write them
type: Resolver # what's the resolver root implementation type called?
layout: follow-schema
dir: graph
package: graph

# Optional, turns on binding to field names by tag provided
struct_tag: json
# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
# struct_tag: json

# Optional, set to true if you prefer []Thing over []*Thing
omit_slice_element_pointers: false
# Optional: turn on to use []Thing instead of []*Thing
# omit_slice_element_pointers: false

# Optional, set to speed up generation time by not performing a final validation pass
skip_validation: true
# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true

# Instead of listing out every model like below, you can automatically bind to any matching types
# within the given path by using `model: User` or `model: models.User`. EXPERIMENTAL in v0.9.1
# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
- github.com/my/app/models
- "{{.}}/graph/model"

# Tell gqlgen about any existing models you want to reuse for
# graphql. These normally come from the db or a remote api.
# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
User:
model: models.User # can use short paths when the package is listed in autobind
Todo:
model: github.com/my/app/models.Todo # or full paths if you need to go elsewhere
fields:
id:
resolver: true # force a resolver to be generated
fieldName: todoId # bind to a different go field name
# model also accepts multiple backing go types. When mapping onto structs
# any of these types can be used, the first one is used as the default for
# resolver args.
ID:
model:
- github.com/99designs/gqlgen/graphql.IntID
- github.com/99designs/gqlgen/graphql.ID
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
Int:
model:
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32

```

Everything has defaults, so add things as you need.
Expand All @@ -86,14 +74,14 @@ gqlgen ships with some builtin directives that make it a little easier to manage
To start using them you first need to define them:

```graphql
directive @goModel(model: String, models: [String!]) on OBJECT
| INPUT_OBJECT
| SCALAR
| ENUM
| INTERFACE
directive @goModel(model: String, models: [String!]) on OBJECT
| INPUT_OBJECT
| SCALAR
| ENUM
| INTERFACE
| UNION

directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITION
directive @goField(forceResolver: Boolean, name: String) on INPUT_FIELD_DEFINITION
| FIELD_DEFINITION
```

Expand Down

0 comments on commit 59caad6

Please sign in to comment.