github.com/operandinc/gqlgen@v0.16.1/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/operandinc/gqlgen"
    37  )
    38  ```
    39  
    40  To automatically add the dependency to your `go.mod` run
    41  
    42  ```shell
    43  go mod tidy
    44  ```
    45  
    46  If you want to specify a particular version of gqlgen, you can use `go get`. For example
    47  
    48  ```shell
    49  go get -d github.com/operandinc/gqlgen
    50  ```
    51  
    52  ## Building the server
    53  
    54  ### Create the project skeleton
    55  
    56  ```shell
    57  go run github.com/operandinc/gqlgen init
    58  printf 'package model' | gofmt > graph/model/doc.go
    59  ```
    60  
    61  This will create our suggested package layout. You can modify these paths in gqlgen.yml if you need to.
    62  
    63  ```
    64  ├── go.mod
    65  ├── go.sum
    66  ├── gqlgen.yml               - The gqlgen config file, knobs for controlling the generated code.
    67  ├── graph
    68  │   ├── generated            - A package that only contains the generated runtime
    69  │   │   └── generated.go
    70  │   ├── model                - A package for all your graph models, generated or otherwise
    71  │   │   └── models_gen.go
    72  │   ├── resolver.go          - The root graph resolver type. This file wont get regenerated
    73  │   ├── schema.graphqls      - Some schema. You can split the schema into as many graphql files as you like
    74  │   └── schema.resolvers.go  - the resolver implementation for schema.graphql
    75  └── server.go                - The entry point to your app. Customize it however you see fit
    76  ```
    77  
    78  ### Define your schema
    79  
    80  gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL
    81  [Schema Definition Language](http://graphql.org/learn/schema/). By default this goes into a file called
    82  `schema.graphqls` but you can break it up into as many different files as you want.
    83  
    84  The schema that was generated for us was:
    85  
    86  ```graphql
    87  type Todo {
    88  	id: ID!
    89  	text: String!
    90  	done: Boolean!
    91  	user: User!
    92  }
    93  
    94  type User {
    95  	id: ID!
    96  	name: String!
    97  }
    98  
    99  type Query {
   100  	todos: [Todo!]!
   101  }
   102  
   103  input NewTodo {
   104  	text: String!
   105  	userId: String!
   106  }
   107  
   108  type Mutation {
   109  	createTodo(input: NewTodo!): Todo!
   110  }
   111  ```
   112  
   113  ### Implement the resolvers
   114  
   115  When executed, gqlgen's `generate` command compares the schema file (`graph/schema.graphqls`) with the models `graph/model/*`, and, wherever it
   116  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.
   117  
   118  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
   119  it was twice:
   120  
   121  ```go
   122  func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
   123  	panic(fmt.Errorf("not implemented"))
   124  }
   125  
   126  func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
   127  	panic(fmt.Errorf("not implemented"))
   128  }
   129  ```
   130  
   131  We just need to implement these two methods to get our server working:
   132  
   133  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.
   134  
   135  ```go
   136  type Resolver struct{
   137  	todos []*model.Todo
   138  }
   139  ```
   140  
   141  Returning to `graph/schema.resolvers.go`, let's implement the bodies of those automatically generated resolver functions. For `CreateTodo`, we'll use `math.rand` 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.
   142  
   143  ```go
   144  func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
   145  	todo := &model.Todo{
   146  		Text:   input.Text,
   147  		ID:     fmt.Sprintf("T%d", rand.Int()),
   148  		User: &model.User{ID: input.UserID, Name: "user " + input.UserID},
   149  	}
   150  	r.todos = append(r.todos, todo)
   151  	return todo, nil
   152  }
   153  
   154  func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
   155  	return r.todos, nil
   156  }
   157  ```
   158  
   159  ### Run the server
   160  
   161  We now have a working server, to start it:
   162  
   163  ```bash
   164  go run server.go
   165  ```
   166  
   167  Open http://localhost:8080 in a browser. Here are some queries to try, starting with creating a todo:
   168  
   169  ```graphql
   170  mutation createTodo {
   171  	createTodo(input: { text: "todo", userId: "1" }) {
   172  		user {
   173  			id
   174  		}
   175  		text
   176  		done
   177  	}
   178  }
   179  ```
   180  
   181  And then querying for it:
   182  
   183  ```graphql
   184  query findTodos {
   185  	todos {
   186  		text
   187  		done
   188  		user {
   189  			name
   190  		}
   191  	}
   192  }
   193  ```
   194  
   195  ### Don't eagerly fetch the user
   196  
   197  This example is great, but in the real world fetching most objects is expensive. We dont want to load the User on the
   198  todo unless the user actually asked for it. So lets replace the generated `Todo` model with something slightly more
   199  realistic.
   200  
   201  Create a new file called `graph/model/todo.go`
   202  
   203  ```go
   204  package model
   205  
   206  type Todo struct {
   207  	ID     string `json:"id"`
   208  	Text   string `json:"text"`
   209  	Done   bool   `json:"done"`
   210  	User   *User  `json:"user"`
   211  }
   212  ```
   213  
   214  > Note
   215  >
   216  > By default gqlgen will use any models in the model directory that match on name, this can be configured in `gqlgen.yml`.
   217  
   218  And run `go run github.com/operandinc/gqlgen generate`.
   219  
   220  > If you run into this error `package github.com/operandinc/gqlgen: no Go files` while executing the `generate` command above, follow the instructions in [this](https://github.com/operandinc/gqlgen/issues/800#issuecomment-888908950) comment for a possible solution.
   221  
   222  Now if we look in `graph/schema.resolvers.go` we can see a new resolver, lets implement it and fix `CreateTodo`.
   223  
   224  ```go
   225  func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
   226  	todo := &model.Todo{
   227  		Text:   input.Text,
   228  		ID:     fmt.Sprintf("T%d", rand.Int()),
   229  		User:   &model.User{ID: input.UserID, Name: "user " + input.UserID},
   230  	}
   231  	r.todos = append(r.todos, todo)
   232  	return todo, nil
   233  }
   234  
   235  func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User, error) {
   236  	return &model.User{ID: obj.UserID, Name: "user " + obj.UserID}, nil
   237  }
   238  ```
   239  
   240  ## Finishing touches
   241  
   242  At the top of our `resolver.go`, between `package` and `import`, add the following line:
   243  
   244  ```go
   245  //go:generate go run github.com/operandinc/gqlgen generate
   246  ```
   247  
   248  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:
   249  
   250  ```go
   251  go generate ./...
   252  ```