github.com/mstephano/gqlgen-schemagen@v0.0.0-20230113041936-dd2cd4ea46aa/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/mstephano/gqlgen-schemagen"
    37  )
    38  ```
    39  
    40  To automatically add the dependency to your `go.mod` run
    41  
    42  ```shell
    43  go mod tidy
    44  ```
    45  
    46  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)
    47  
    48  ```shell
    49  go get -d github.com/mstephano/gqlgen@VERSION
    50  ```
    51  
    52  ## Building the server
    53  
    54  ### Create the project skeleton
    55  
    56  ```shell
    57  go run github.com/mstephano/gqlgen-schemagen 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  ```
    63  ├── go.mod
    64  ├── go.sum
    65  ├── gqlgen.yml               - The gqlgen config file, knobs for controlling the generated code.
    66  ├── graph
    67  │   ├── generated            - A package that only contains the generated runtime
    68  │   │   └── generated.go
    69  │   ├── model                - A package for all your graph models, generated or otherwise
    70  │   │   └── models_gen.go
    71  │   ├── resolver.go          - The root graph resolver type. This file wont get regenerated
    72  │   ├── schema.graphqls      - Some schema. You can split the schema into as many graphql files as you like
    73  │   └── schema.resolvers.go  - the resolver implementation for schema.graphql
    74  └── server.go                - The entry point to your app. Customize it however you see fit
    75  ```
    76  
    77  ### Define your schema
    78  
    79  gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL
    80  [Schema Definition Language](http://graphql.org/learn/schema/). By default this goes into a file called
    81  `schema.graphqls` but you can break it up into as many different files as you want.
    82  
    83  The schema that was generated for us was:
    84  
    85  ```graphql
    86  type Todo {
    87  	id: ID!
    88  	text: String!
    89  	done: Boolean!
    90  	user: User!
    91  }
    92  
    93  type User {
    94  	id: ID!
    95  	name: String!
    96  }
    97  
    98  type Query {
    99  	todos: [Todo!]!
   100  }
   101  
   102  input NewTodo {
   103  	text: String!
   104  	userId: String!
   105  }
   106  
   107  type Mutation {
   108  	createTodo(input: NewTodo!): Todo!
   109  }
   110  ```
   111  
   112  ### Implement the resolvers
   113  
   114  When executed, gqlgen's `generate` command compares the schema file (`graph/schema.graphqls`) with the models `graph/model/*`, and, wherever it
   115  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.
   116  
   117  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
   118  it was twice:
   119  
   120  ```go
   121  func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
   122  	panic(fmt.Errorf("not implemented"))
   123  }
   124  
   125  func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
   126  	panic(fmt.Errorf("not implemented"))
   127  }
   128  ```
   129  
   130  We just need to implement these two methods to get our server working:
   131  
   132  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.
   133  
   134  ```go
   135  type Resolver struct{
   136  	todos []*model.Todo
   137  }
   138  ```
   139  
   140  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.
   141  
   142  ```go
   143  func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
   144    rand, _ := rand.Int(rand.Reader, big.NewInt(100))
   145  	todo := &model.Todo{
   146  		Text:   input.Text,
   147  		ID:     fmt.Sprintf("T%d", rand),
   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  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`:
   202  
   203  ```yml
   204  # gqlgen will search for any type names in the schema in these go packages
   205  # if they match it will use them, otherwise it will generate them.
   206  autobind:
   207    - "github.com/[username]/gqlgen-todos/graph/model"
   208  ```
   209  
   210  And add `Todo` fields resolver config in `gqlgen.yml` to generate resolver for `user` field
   211  
   212  ```yml
   213  # This section declares type mapping between the GraphQL and go type systems
   214  #
   215  # The first line in each type will be used as defaults for resolver arguments and
   216  # modelgen, the others will be allowed when binding to fields. Configure them to
   217  # your liking
   218  models:
   219    ID:
   220      model:
   221        - github.com/mstephano/gqlgen-schemagen/graphql.ID
   222        - github.com/mstephano/gqlgen-schemagen/graphql.Int
   223        - github.com/mstephano/gqlgen-schemagen/graphql.Int64
   224        - github.com/mstephano/gqlgen-schemagen/graphql.Int32
   225    Int:
   226      model:
   227        - github.com/mstephano/gqlgen-schemagen/graphql.Int
   228        - github.com/mstephano/gqlgen-schemagen/graphql.Int64
   229        - github.com/mstephano/gqlgen-schemagen/graphql.Int32
   230    Todo:
   231      fields:
   232        user:
   233          resolver: true
   234  ```
   235  
   236  Next, create a new file called `graph/model/todo.go`
   237  
   238  ```go
   239  package model
   240  
   241  type Todo struct {
   242  	ID     string `json:"id"`
   243  	Text   string `json:"text"`
   244  	Done   bool   `json:"done"`
   245  	UserID string `json:"userId"`
   246  	User   *User  `json:"user"`
   247  }
   248  ```
   249  
   250  And run `go run github.com/mstephano/gqlgen-schemagen generate`.
   251  
   252  > If you run into this error `package github.com/mstephano/gqlgen: no Go files` while executing the `generate` command above, follow the instructions in [this](https://github.com/mstephano/gqlgen-schemagen/issues/800#issuecomment-888908950) comment for a possible solution.
   253  
   254  Now if we look in `graph/schema.resolvers.go` we can see a new resolver, lets implement it and fix `CreateTodo`.
   255  
   256  ```go
   257  func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
   258  	todo := &model.Todo{
   259  		Text:   input.Text,
   260  		ID:     fmt.Sprintf("T%d", rand.Int()),
   261  		User:   &model.User{ID: input.UserID, Name: "user " + input.UserID},
   262  		UserID: input.UserID,
   263  	}
   264  	r.todos = append(r.todos, todo)
   265  	return todo, nil
   266  }
   267  
   268  func (r *todoResolver) User(ctx context.Context, obj *model.Todo) (*model.User, error) {
   269  	return &model.User{ID: obj.UserID, Name: "user " + obj.UserID}, nil
   270  }
   271  ```
   272  
   273  ## Finishing touches
   274  
   275  At the top of our `resolver.go`, between `package` and `import`, add the following line:
   276  
   277  ```go
   278  //go:generate go run github.com/mstephano/gqlgen-schemagen generate
   279  ```
   280  
   281  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:
   282  
   283  ```go
   284  go generate ./...
   285  ```