more README stuff, example README, other misc cleanup
This commit is contained in:
@@ -17,12 +17,16 @@ client.Query(context.Background(), &query, nil)
|
|||||||
fmt.Println(query.Me.Name)
|
fmt.Println(query.Me.Name)
|
||||||
// Output: Luke Skywalker
|
// Output: Luke Skywalker
|
||||||
```
|
```
|
||||||
While your code is type-safe, there's nothing to check that the schema looks like you intend it to. In fact, perhaps here we're querying the GitHub API, in which the field is called `viewer`, not `me`, so this query will fail. Even more common than misusing the name of the field is mis-capitalizing it: the GraphQL convention is `myUrl` whereas the Go convention is `MyURL`, and it's easy to forget to use the right struct tag. (And if you remember, it adds up to a lot of boilerplate!) Other clients, such as [machinebox/graphql](https://github.com/machinebox/graphql), have even fewer guardrails to help you make the right query.
|
While your code is type-safe, there's nothing to check that the schema looks like you intend it to. In fact, perhaps here we're querying the GitHub API, in which the field is called `viewer`, not `me`, so this query will fail. Even more common than misusing the name of the field is mis-capitalizing it: the GraphQL convention is `myUrl` whereas the Go convention is `MyURL`, and it's easy to forget to use the right struct tag. (And if you remember, it adds up to a lot of boilerplate!) Other clients, such as [machinebox/graphql](https://github.com/machinebox/graphql), have even fewer guardrails to help you make the right query and use the result correctly.
|
||||||
|
|
||||||
These problems should be entirely avoidable: GraphQL and Go are both typed languages; and GraphQL servers expose their schema in a standard, machine-readable format. We should be able to simply write a query `{ viewer { name } }`, have that automatically validated against the schema and turned into a Go struct which we can use in our code. In fact, there's already good prior art to do this sort of thing: [99designs/gqlgen](https://github.com/99designs/gqlgen) is a popular server library that does exactly this: it generates type-safe GraphQL resolvers from a schema.
|
These problems should be entirely avoidable: GraphQL and Go are both typed languages; and GraphQL servers expose their schema in a standard, machine-readable format. We should be able to simply write a query `{ viewer { name } }`, have that automatically validated against the schema and turned into a Go struct which we can use in our code. In fact, there's already good prior art to do this sort of thing: [99designs/gqlgen](https://github.com/99designs/gqlgen) is a popular server library that does exactly this: it generates type-safe GraphQL resolvers from a schema.
|
||||||
|
|
||||||
This is a proof-of-concept of a GraphQL client that does the same sort of thing: you specify the query, and it generates type-safe helpers that make your query.
|
This is a proof-of-concept of a GraphQL client that does the same sort of thing: you specify the query, and it generates type-safe helpers that make your query.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
See `example/`.
|
||||||
|
|
||||||
## Major TODOs
|
## Major TODOs
|
||||||
|
|
||||||
Query structures to support:
|
Query structures to support:
|
||||||
@@ -33,3 +37,6 @@ Config options:
|
|||||||
- file locations (queries, generated, schema (or get via HTTP))
|
- file locations (queries, generated, schema (or get via HTTP))
|
||||||
- use ctx or not, incl. complexities of how Khan uses context
|
- use ctx or not, incl. complexities of how Khan uses context
|
||||||
- HTTP calling convention (is there enough variation to matter?)
|
- HTTP calling convention (is there enough variation to matter?)
|
||||||
|
|
||||||
|
Other:
|
||||||
|
- figure out and document go generate syntax
|
||||||
|
|||||||
+28
-1
@@ -1,7 +1,34 @@
|
|||||||
Generate the schemas by getting a token from [GitHub](https://github.com/settings/tokens/new) (no scopes needed), then:
|
# example of genql
|
||||||
|
|
||||||
|
## Getting a token
|
||||||
|
Get a token from [GitHub](https://github.com/settings/tokens/new) (no scopes needed).
|
||||||
|
|
||||||
|
## Invoking the example
|
||||||
|
|
||||||
|
To run the example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ KEY=<your token> go run ./cmd/example/main.go <username>
|
||||||
|
you are Ben Kraft
|
||||||
|
csilvers is Craig Silverstein
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Running genql
|
||||||
|
|
||||||
|
It's already checked in to github, but to generate `generated.go`:
|
||||||
|
```sh
|
||||||
|
rm example/generated.go
|
||||||
|
go run ./cmd/genql/main.go example/genql.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Generating the schema files
|
||||||
|
|
||||||
|
These are also checked in, but to update them:
|
||||||
|
|
||||||
|
```sh
|
||||||
npm install -g graphql-introspection-json-to-sdl
|
npm install -g graphql-introspection-json-to-sdl
|
||||||
curl -H "Authorization: bearer <your token>" https://api.github.com/graphql >example/schema.json
|
curl -H "Authorization: bearer <your token>" https://api.github.com/graphql >example/schema.json
|
||||||
graphql-introspection-json-to-sdl example/schema.json >example/schema.graphql
|
graphql-introspection-json-to-sdl example/schema.json >example/schema.graphql
|
||||||
```
|
```
|
||||||
|
|
||||||
TODO: something better
|
TODO: something better
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Code generated by github.com/Khan/genql, DO NOT EDIT.
|
||||||
package example
|
package example
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# These are the defaults.
|
||||||
|
package: example
|
||||||
|
schema: schema.graphql
|
||||||
|
queries: queries.graphql
|
||||||
|
generated: generated.go
|
||||||
+8
-1
@@ -62,7 +62,7 @@ func ReadAndValidateConfig(filename string) (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var config Config
|
var config Config
|
||||||
err = yaml.Unmarshal(text, config)
|
err = yaml.Unmarshal(text, &config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid config file %v: %v", filename, err)
|
return nil, fmt.Errorf("invalid config file %v: %v", filename, err)
|
||||||
}
|
}
|
||||||
@@ -72,5 +72,12 @@ func ReadAndValidateConfig(filename string) (*Config, error) {
|
|||||||
return nil, fmt.Errorf("invalid config file %v: %v", filename, err)
|
return nil, fmt.Errorf("invalid config file %v: %v", filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make paths relative to config dir
|
||||||
|
// TODO: more principled typing here?
|
||||||
|
basename := filepath.Dir(filename)
|
||||||
|
config.Schema = filepath.Join(basename, config.Schema)
|
||||||
|
config.Queries = filepath.Join(basename, config.Queries)
|
||||||
|
config.Generated = filepath.Join(basename, config.Generated)
|
||||||
|
|
||||||
return &config, nil
|
return &config, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ func Main() {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if len(os.Args) != 2 {
|
if len(os.Args) != 2 {
|
||||||
|
// TODO: omit config to get it from genql.yaml, or to use the defaults.
|
||||||
err = fmt.Errorf("usage: %s genql.yaml", os.Args[0])
|
err = fmt.Errorf("usage: %s genql.yaml", os.Args[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Code generated by github.com/Khan/genql, DO NOT EDIT.
|
||||||
package {{.PackageName}}
|
package {{.PackageName}}
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in New Issue
Block a user