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  ```