github.com/vektah/gqlgen@v0.7.2/docs/content/getting-started.md (about) 1 --- 2 linkTitle: Getting Started 3 title: Building GraphQL servers in golang 4 description: Get started building type-safe GraphQL servers in Golang using gqlgen 5 menu: main 6 weight: -5 7 --- 8 9 This tutorial will take you through the process of building a GraphQL server with gqlgen that can: 10 11 - Return a list of todos 12 - Create new todos 13 - Mark off todos as they are completed 14 15 You can find the finished code for this tutorial [here](https://github.com/vektah/gqlgen-tutorials/tree/master/gettingstarted) 16 17 ## Install gqlgen 18 19 This article uses [`dep`](https://github.com/golang/dep) to install gqlgen. [Follow the instructions for your environment](https://github.com/golang/dep) to install. 20 21 Assuming you already have a working [Go environment](https://golang.org/doc/install), create a directory for the project in your `$GOPATH`: 22 23 ```sh 24 $ mkdir -p $GOPATH/src/github.com/[username]/gqlgen-todos 25 ``` 26 27 > Go Modules 28 > 29 > Currently `gqlgen` does not support Go Modules. This is due to the [`loader`](https://godoc.org/golang.org/x/tools/go/loader) package, that also does not yet support Go Modules. We are looking at solutions to this and the issue is tracked in Github. 30 31 Add the following file to your project under `scripts/gqlgen.go`: 32 33 ```go 34 // +build ignore 35 36 package main 37 38 import "github.com/99designs/gqlgen/cmd" 39 40 func main() { 41 cmd.Execute() 42 } 43 ``` 44 45 Lastly, initialise dep. This will inspect any imports you have in your project, and pull down the latest tagged release. 46 47 ```sh 48 $ dep init 49 ``` 50 51 ## Building the server 52 53 ### Define the schema 54 55 gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL 56 [Schema Definition Language](http://graphql.org/learn/schema/). This usually goes into a file called `schema.graphql`: 57 58 ```graphql 59 type Todo { 60 id: ID! 61 text: String! 62 done: Boolean! 63 user: User! 64 } 65 66 type User { 67 id: ID! 68 name: String! 69 } 70 71 type Query { 72 todos: [Todo!]! 73 } 74 75 input NewTodo { 76 text: String! 77 userId: String! 78 } 79 80 type Mutation { 81 createTodo(input: NewTodo!): Todo! 82 } 83 ``` 84 85 ### Create the project skeleton 86 87 ```bash 88 $ go run scripts/gqlgen.go init 89 ``` 90 91 This has created an empty skeleton with all files you need: 92 93 - `gqlgen.yml` — The gqlgen config file, knobs for controlling the generated code. 94 - `generated.go` — The GraphQL execution runtime, the bulk of the generated code. 95 - `models_gen.go` — Generated models required to build the graph. Often you will override these with your own models. Still very useful for input types. 96 - `resolver.go` — This is where your application code lives. `generated.go` will call into this to get the data the user has requested. 97 - `server/server.go` — This is a minimal entry point that sets up an `http.Handler` to the generated GraphQL server. 98 99 Now run dep ensure, so that we can ensure that the newly generated code's dependencies are all present: 100 101 ```sh 102 $ dep ensure 103 ``` 104 105 ### Create the database models 106 107 The generated model for Todo isn't right, it has a user embeded in it but we only want to fetch it if the user actually requested it. So instead lets make a new model in `todo.go`: 108 109 ```go 110 package gettingstarted 111 112 type Todo struct { 113 ID string 114 Text string 115 Done bool 116 UserID string 117 } 118 ``` 119 120 Next tell gqlgen to use this new struct by adding it to `gqlgen.yml`: 121 122 ```yaml 123 models: 124 Todo: 125 model: github.com/[username]/gqlgen-todos/gettingstarted.Todo 126 ``` 127 128 Regenerate by running: 129 130 ```bash 131 $ go run scripts/gqlgen.go -v 132 Unable to bind Todo.user to github.com/[username]/gqlgen-todos/gettingstarted.Todo 133 no method named user 134 no field named user 135 Adding resolver method 136 ``` 137 138 > Note 139 > 140 > The verbose flag `-v` is here to show what gqlgen is doing. It has looked at all the fields on the model and found matching methods for all of them, except user. For user it has added a resolver to the interface you need to implement. *This is the magic that makes gqlgen work so well!* 141 142 ### Implement the resolvers 143 144 The generated runtime has defined an interface for all the missing resolvers that we need to provide. Lets take a look in `generated.go` 145 146 ```go 147 // NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. 148 func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { 149 return &executableSchema{ 150 resolvers: cfg.Resolvers, 151 directives: cfg.Directives, 152 } 153 } 154 155 type Config struct { 156 Resolvers ResolverRoot 157 Directives DirectiveRoot 158 } 159 160 type ResolverRoot interface { 161 Mutation() MutationResolver 162 Query() QueryResolver 163 Todo() TodoResolver 164 } 165 166 type DirectiveRoot struct { 167 } 168 type MutationResolver interface { 169 CreateTodo(ctx context.Context, input NewTodo) (Todo, error) 170 } 171 type QueryResolver interface { 172 Todos(ctx context.Context) ([]Todo, error) 173 } 174 type TodoResolver interface { 175 User(ctx context.Context, obj *Todo) (User, error) 176 } 177 ``` 178 179 Notice the `TodoResolver.User` method? Thats gqlgen saying "I dont know how to get a User from a Todo, you tell me.". 180 Its worked out how to build everything else for us. 181 182 For any missing models (like NewTodo) gqlgen will generate a go struct. This is usually only used for input types and 183 one-off return values. Most of the time your types will be coming from the database, or an API client so binding is 184 better than generating. 185 186 ### Write the resolvers 187 188 This is a work in progress, we have a way to generate resolver stubs, but it cannot currently update existing code. We can force it to run again by deleting `resolver.go` and re-running gqlgen: 189 190 ```bash 191 $ rm resolver.go 192 $ go run scripts/gqlgen.go 193 ``` 194 195 Now we just need to fill in the `not implemented` parts. Update `resolver.go` 196 197 ```go 198 //go:generate go run ./scripts/gqlgen.go 199 200 package gettingstarted 201 202 import ( 203 context "context" 204 "fmt" 205 "math/rand" 206 ) 207 208 type Resolver struct{ 209 todos []Todo 210 } 211 212 func (r *Resolver) Mutation() MutationResolver { 213 return &mutationResolver{r} 214 } 215 func (r *Resolver) Query() QueryResolver { 216 return &queryResolver{r} 217 } 218 func (r *Resolver) Todo() TodoResolver { 219 return &todoResolver{r} 220 } 221 222 type mutationResolver struct{ *Resolver } 223 224 func (r *mutationResolver) CreateTodo(ctx context.Context, input NewTodo) (Todo, error) { 225 todo := Todo{ 226 Text: input.Text, 227 ID: fmt.Sprintf("T%d", rand.Int()), 228 UserID: input.UserID, 229 } 230 r.todos = append(r.todos, todo) 231 return todo, nil 232 } 233 234 type queryResolver struct{ *Resolver } 235 236 func (r *queryResolver) Todos(ctx context.Context) ([]Todo, error) { 237 return r.todos, nil 238 } 239 240 type todoResolver struct{ *Resolver } 241 242 func (r *todoResolver) User(ctx context.Context, obj *Todo) (User, error) { 243 return User{ID: obj.UserID, Name: "user " + obj.UserID}, nil 244 } 245 246 ``` 247 248 We now have a working server, to start it: 249 ```bash 250 go run server/server.go 251 ``` 252 253 then open http://localhost:8080 in a browser. here are some queries to try: 254 ```graphql 255 mutation createTodo { 256 createTodo(input:{text:"todo", userId:"1"}) { 257 user { 258 id 259 } 260 text 261 done 262 } 263 } 264 265 query findTodos { 266 todos { 267 text 268 done 269 user { 270 name 271 } 272 } 273 } 274 ``` 275 276 ## Finishing touches 277 278 At the top of our `resolver.go` add the following line: 279 280 ```go 281 //go:generate go run scripts/gqlgen.go -v 282 ``` 283 284 This magic comment tells `go generate` what command to run when we want to regenerate our code. To run go generate recursively over your entire project, use this command: 285 286 ```go 287 go generate ./... 288 ``` 289 290 > Note 291 > 292 > Ensure that the path to your `gqlgen` binary is relative to the file the generate command is added to.