github.com/shippio/gqlgen@v0.0.0-20220912092219-633ea699ef07/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: -7 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 ## Set up Project 18 19 Create a directory for your project, and [initialise it as a Go Module](https://golang.org/doc/tutorial/create-module): 20 21 ```shell 22 mkdir gqlgen-todos 23 cd gqlgen-todos 24 go mod init github.com/[username]/gqlgen-todos 25 ``` 26 27 Next, create a `tools.go` file and add gqlgen as a [tool dependency for your module](https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module). 28 29 ```go 30 //go:build tools 31 // +build tools 32 33 package tools 34 35 import ( 36 _ "github.com/99designs/gqlgen" 37 ) 38 ``` 39 40 To automatically add the dependency to your `go.mod` run 41 ```shell 42 go mod tidy 43 ``` 44 45 By default you'll be using the latest version of gqlgen, but if you want to specify a particular version you can use `go get` (replacing `VERSION` with the particular version desired) 46 ```shell 47 go get -d github.com/99designs/gqlgen@VERSION 48 ``` 49 50 51 52 ## Building the server 53 54 ### Create the project skeleton 55 56 ```shell 57 go run github.com/99designs/gqlgen init 58 ``` 59 60 This will create our suggested package layout. You can modify these paths in gqlgen.yml if you need to. 61 ``` 62 ├── go.mod 63 ├── go.sum 64 ├── gqlgen.yml - The gqlgen config file, knobs for controlling the generated code. 65 ├── graph 66 │ ├── generated - A package that only contains the generated runtime 67 │ │ └── generated.go 68 │ ├── model - A package for all your graph models, generated or otherwise 69 │ │ └── models_gen.go 70 │ ├── resolver.go - The root graph resolver type. This file wont get regenerated 71 │ ├── schema.graphqls - Some schema. You can split the schema into as many graphql files as you like 72 │ └── schema.resolvers.go - the resolver implementation for schema.graphql 73 └── server.go - The entry point to your app. Customize it however you see fit 74 ``` 75 76 ### Define your schema 77 78 gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL 79 [Schema Definition Language](http://graphql.org/learn/schema/). By default this goes into a file called 80 `schema.graphqls` but you can break it up into as many different files as you want. 81 82 The schema that was generated for us was: 83 ```graphql 84 type Todo { 85 id: ID! 86 text: String! 87 done: Boolean! 88 user: User! 89 } 90 91 type User { 92 id: ID! 93 name: String! 94 } 95 96 type Query { 97 todos: [Todo!]! 98 } 99 100 input NewTodo { 101 text: String! 102 userId: String! 103 } 104 105 type Mutation { 106 createTodo(input: NewTodo!): Todo! 107 } 108 ``` 109 110 ### Implement the resolvers 111 112 When executed, gqlgen's `generate` command compares the schema file (`graph/schema.graphqls`) with the models `graph/model/*`, and, wherever it 113 can, it will bind directly to the model. That was done already when `init` was run. We'll edit the schema later in the tutorial, but for now, let's look at what was generated already. 114 115 If we take a look in `graph/schema.resolvers.go` we will see all the times that gqlgen couldn't match them up. For us 116 it was twice: 117 118 ```go 119 func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) { 120 panic(fmt.Errorf("not implemented")) 121 } 122 123 func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) { 124 panic(fmt.Errorf("not implemented")) 125 } 126 ``` 127 128 We just need to implement these two methods to get our server working: 129 130 First we need somewhere to track our state, lets put it in `graph/resolver.go`. The `graph/resolver.go` file is where we declare our app's dependencies, like our database. It gets initialized once in `server.go` when we create the graph. 131 132 ```go 133 type Resolver struct{ 134 todos []*model.Todo 135 } 136 ``` 137 138 Returning to `graph/schema.resolvers.go`, let's implement the bodies of those automatically generated resolver functions. For `CreateTodo`, we'll use the [`math.rand` package](https://pkg.go.dev/math/rand#Rand.Int) to simply return a todo with a randomly generated ID and store that in the in-memory todos list --- in a real app, you're likely to use a database or some other backend service. 139 140 ```go 141 func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) { 142 todo := &model.Todo{ 143 Text: input.Text, 144 ID: fmt.Sprintf("T%d", rand.Int()), 145 User: &model.User{ID: input.UserID, Name: "user " + input.UserID}, 146 } 147 r.todos = append(r.todos, todo) 148 return todo, nil 149 } 150 151 func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) { 152 return r.todos, nil 153 } 154 ``` 155 156 ### Run the server 157 158 We now have a working server, to start it: 159 ```bash 160 go run server.go 161 ``` 162 163 Open http://localhost:8080 in a browser. Here are some queries to try, starting with creating a todo: 164 ```graphql 165 mutation createTodo { 166 createTodo(input: { text: "todo", userId: "1" }) { 167 user { 168 id 169 } 170 text 171 done 172 } 173 } 174 ``` 175 176 And then querying for it: 177 178 ```graphql 179 query findTodos { 180 todos { 181 text 182 done 183 user { 184 name 185 } 186 } 187 } 188 ``` 189 190 ### Don't eagerly fetch the user 191 192 This example is great, but in the real world fetching most objects is expensive. We dont want to load the User on the 193 todo unless the user actually asked for it. So lets replace the generated `Todo` model with something slightly more 194 realistic. 195 196 First let's enable `autobind`, allowing gqlgen to use your custom models if it can find them rather than generating them. We do this by uncommenting the `autobind` config line in `gqlgen.yml`: 197 198 ```yml 199 # gqlgen will search for any type names in the schema in these go packages 200 # if they match it will use them, otherwise it will generate them. 201 autobind: 202 - "github.com/[username]/gqlgen-todos/graph/model" 203 ``` 204 205 And add `Todo` fields resolver config in `gqlgen.yml` to generate resolver for `user` field 206 ```yml 207 # This section declares type mapping between the GraphQL and go type systems 208 # 209 # The first line in each type will be used as defaults for resolver arguments and 210 # modelgen, the others will be allowed when binding to fields. Configure them to 211 # your liking 212 models: 213 ID: 214 model: 215 - github.com/99designs/gqlgen/graphql.ID 216 - github.com/99designs/gqlgen/graphql.Int 217 - github.com/99designs/gqlgen/graphql.Int64 218 - github.com/99designs/gqlgen/graphql.Int32 219 Int: 220 model: 221 - github.com/99designs/gqlgen/graphql.Int 222 - github.com/99designs/gqlgen/graphql.Int64 223 - github.com/99designs/gqlgen/graphql.Int32 224 Todo: 225 fields: 226 user: 227 resolver: true 228 ``` 229 230 Next, create a new file called `graph/model/todo.go` 231 232 ```go 233 package model 234 235 type Todo struct { 236 ID string `json:"id"` 237 Text string `json:"text"` 238 Done bool `json:"done"` 239 UserID string `json:"userId"` 240 User *User `json:"user"` 241 } 242 ``` 243 244 And run `go run github.com/99designs/gqlgen generate`. 245 246 > 247 > If you run into this error `package github.com/99designs/gqlgen: no Go files` while executing the `generate` command above, follow the instructions in [this](https://github.com/99designs/gqlgen/issues/800#issuecomment-888908950) comment for a possible solution. 248 249 Now if we look in `graph/schema.resolvers.go` we can see a new resolver, lets implement it and fix `CreateTodo`. 250 ```go 251 func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) { 252 todo := &model.Todo{ 253 Text: input.Text, 254 ID: fmt.Sprintf("T%d", rand.Int()), 255 User: &model.User{ID: input.UserID, Name: "user " + input.UserID}, 256 UserID: input.UserID, 257 } 258 r.todos = append(r.todos, todo) 259 return todo, nil 260 } 261 262 func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User, error) { 263 return &model.User{ID: obj.UserID, Name: "user " + obj.UserID}, nil 264 } 265 ``` 266 267 ## Finishing touches 268 269 At the top of our `resolver.go`, between `package` and `import`, add the following line: 270 271 ```go 272 //go:generate go run github.com/99designs/gqlgen generate 273 ``` 274 275 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: 276 277 ```go 278 go generate ./... 279 ```