github.com/animeshon/gqlgen@v0.13.1-0.20210304133704-3a770431bb6d/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 ## Setup Project 18 19 Create a directory for your project, and initialise it as a Go Module: 20 21 ```sh 22 $ mkdir gqlgen-todos 23 $ cd gqlgen-todos 24 $ go mod init github.com/[username]/gqlgen-todos 25 $ go get github.com/99designs/gqlgen 26 ``` 27 28 ## Building the server 29 30 ### Create the project skeleton 31 32 ```bash 33 $ go run github.com/99designs/gqlgen init 34 ``` 35 36 This will create our suggested package layout. You can modify these paths in gqlgen.yml if you need to. 37 ``` 38 ├── go.mod 39 ├── go.sum 40 ├── gqlgen.yml - The gqlgen config file, knobs for controlling the generated code. 41 ├── graph 42 │ ├── generated - A package that only contains the generated runtime 43 │ │ └── generated.go 44 │ ├── model - A package for all your graph models, generated or otherwise 45 │ │ └── models_gen.go 46 │ ├── resolver.go - The root graph resolver type. This file wont get regenerated 47 │ ├── schema.graphqls - Some schema. You can split the schema into as many graphql files as you like 48 │ └── schema.resolvers.go - the resolver implementation for schema.graphql 49 └── server.go - The entry point to your app. Customize it however you see fit 50 ``` 51 52 ### Define your schema 53 54 gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL 55 [Schema Definition Language](http://graphql.org/learn/schema/). By default this goes into a file called 56 `schema.graphql` but you can break it up into as many different files as you want. 57 58 The schema that was generated for us was: 59 ```graphql 60 type Todo { 61 id: ID! 62 text: String! 63 done: Boolean! 64 user: User! 65 } 66 67 type User { 68 id: ID! 69 name: String! 70 } 71 72 type Query { 73 todos: [Todo!]! 74 } 75 76 input NewTodo { 77 text: String! 78 userId: String! 79 } 80 81 type Mutation { 82 createTodo(input: NewTodo!): Todo! 83 } 84 ``` 85 86 ### Implement the resolvers 87 88 `gqlgen generate` compares the schema file (`graph/schema.graphqls`) with the models `graph/model/*` and wherever it 89 can it will bind directly to the model. 90 91 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 92 it was twice: 93 94 ```go 95 func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) { 96 panic(fmt.Errorf("not implemented")) 97 } 98 99 func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) { 100 panic(fmt.Errorf("not implemented")) 101 } 102 ``` 103 104 We just need to implement these two methods to get our server working: 105 106 First we need somewhere to track our state, lets put it in `graph/resolver.go`: 107 ```go 108 type Resolver struct{ 109 todos []*model.Todo 110 } 111 ``` 112 This is where we declare any dependencies for our app like our database, it gets initialized once in `server.go` when 113 we create the graph. 114 115 ```go 116 func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) { 117 todo := &model.Todo{ 118 Text: input.Text, 119 ID: fmt.Sprintf("T%d", rand.Int()), 120 User: &model.User{ID: input.UserID, Name: "user " + input.UserID}, 121 } 122 r.todos = append(r.todos, todo) 123 return todo, nil 124 } 125 126 func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) { 127 return r.todos, nil 128 } 129 ``` 130 131 We now have a working server, to start it: 132 ```bash 133 go run server.go 134 ``` 135 136 then open http://localhost:8080 in a browser. here are some queries to try: 137 ```graphql 138 mutation createTodo { 139 createTodo(input:{text:"todo", userId:"1"}) { 140 user { 141 id 142 } 143 text 144 done 145 } 146 } 147 148 query findTodos { 149 todos { 150 text 151 done 152 user { 153 name 154 } 155 } 156 } 157 ``` 158 159 ### Don't eagerly fetch the user 160 161 This example is great, but in the real world fetching most objects is expensive. We dont want to load the User on the 162 todo unless the user actually asked for it. So lets replace the generated `Todo` model with something slightly more 163 realistic. 164 165 Create a new file called `graph/model/todo.go` 166 ```go 167 package model 168 169 type Todo struct { 170 ID string `json:"id"` 171 Text string `json:"text"` 172 Done bool `json:"done"` 173 UserID string `json:"user"` 174 } 175 ``` 176 177 > Note 178 > 179 > By default gqlgen will use any models in the model directory that match on name, this can be configured in `gqlgen.yml`. 180 181 And run `go run github.com/99designs/gqlgen generate`. 182 183 Now if we look in `graph/schema.resolvers.go` we can see a new resolver, lets implement it and fix `CreateTodo`. 184 ```go 185 func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) { 186 todo := &model.Todo{ 187 Text: input.Text, 188 ID: fmt.Sprintf("T%d", rand.Int()), 189 UserID: input.UserID, // fix this line 190 } 191 r.todos = append(r.todos, todo) 192 return todo, nil 193 } 194 195 func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User, error) { 196 return &model.User{ID: obj.UserID, Name: "user " + obj.UserID}, nil 197 } 198 ``` 199 200 ## Finishing touches 201 202 At the top of our `resolver.go`, between `package` and `import`, add the following line: 203 204 ```go 205 //go:generate go run github.com/99designs/gqlgen 206 ``` 207 208 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: 209 210 ```go 211 go generate ./... 212 ```