Creating GraphQL servers in Go

So far, we've seen how to create a REST API. But how can we create a GraphQL API in Go or any other programming language? We can't do this directly. We need the help of a few packages to build GraphQL servers that can handle requests from clients. Clients can be web-based or mobile. We need two vital things to build a GraphQL server:

The Schema is what we discussed in the early stage of this chapter. Resolvers, on the other hand, are solid entities that generate HTTP responses. The Schema only validates and routes the request to the corresponding resources; revolvers do the actual logic of computing the result, a database query, or any other backend operation.

In this section, we'll create a simple server that responds to queries for player data in a multiplayer game. Let's get started:

  1. Let's say the schema looks like this:
query {
players {
highScore
id
isOnline
levelsUnlocked
}
}

Let's say the server should return this information. Let's start implementing the service. We are going to mock data into our multiplayer game API. The same data can be queried from the database or can be fetched from a file. First, install the necessary packages. We need two packages:

  1. Let's create the project repository:
mkdir -p $GOPATH/src/github.com/git-user/chapter10/graphqlServer
touch $GOPATH/src/github.com/git-user/chapter10/graphqlServer/
main.go
  1. We can install both packages using the dep tool:
dep init
dep ensure -add "github.com/graphql-go/graphql"
dep ensure -add "github.com/graphql-go/handler"
  1. Now, let's write our main.go file. It should contain all the necessary imports. The main imports are from the packages we installed along with net/http:
import (
"net/http"

"github.com/graphql-go/graphql"
"github.com/graphql-go/handler"
)
  1. Now, let's define our dummy data, which we serve through resolvers. Define a struct that returns the response for the preceding client query:
// Player holds player response
type Player struct {
ID int `json:"int"`
Name string `json:"name"`
HighScore int `json:"highScore"`
IsOnline bool `json:"isOnline"`
Location string `json:"location"`
LevelsUnlocked []string `json:"levelsUnlocked"`
}

var players = []Player{
Player{ID: 123, Name: "Pablo", HighScore: 1100, IsOnline: true,
Location: "Italy"},
Player{ID: 230, Name: "Dora", HighScore: 2100, IsOnline: false,
Location: "Germany"},
}

The previously defined constructs are a simple Go struct and a list. We'll use this information later.

  1. Now, define a player object using the graphql.NewObject function. This takes a graphql.ObjectConfig instance that defines the fields and their types for the object.
  2. The graphql package provides scalar types and composite types such as lists. The following is the definition of the player object:
var playerObject = graphql.NewObject(
graphql.ObjectConfig{
Name: "Player",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.Int,
},
"name": &graphql.Field{
Type: graphql.String,
},
"highScore": &graphql.Field{
Type: graphql.String,
},
"isOnline": &graphql.Field{
Type: graphql.Boolean,
},
"location": &graphql.Field{
Type: graphql.String,
},
"levelsUnlocked": &graphql.Field{
Type: graphql.NewList(graphql.String),
},
},
},
)

These object fields will be mapped to the struct Player fields we defined previously.

  1. Next comes our main function. Here, we have to define three things:

A root query defines the root object while querying. A schema defines the structure of the GraphQL response. A new schema can be created from the schema config. Our main function does all these things.

  1. Then, we create a fields section and attach it to the root query. These fields have a resolver that gets called whenever a client makes a query. Let's say we return all the players when someone queries the root object:
func main() {
// Schema
fields := graphql.Fields{
"players": &graphql.Field{
Type: graphql.NewList(playerObject),
Description: "All players",
Resolve: func(p graphql.ResolveParams) (interface{},
error) {
return players, nil
},
},
}
rootQuery := graphql.ObjectConfig{Name: "RootQuery",
Fields: fields}
schemaConfig := graphql.SchemaConfig{Query:
graphql.NewObject(rootQuery)}
schema, _ := graphql.NewSchema(schemaConfig)
...
}
  1. Now, we have a schema. But in order to serve it via HTTP, we should pass this schema to the graphql-go package's handler.New function. We can also create an interactive GraphQL browser called GraphiQL. Let's see how to do this in code:
    h := handler.New(&handler.Config{
Schema: &schema,
Pretty: true,
GraphiQL: true,
})

http.Handle("/graphql", h)
http.ListenAndServe(":8000", nil)

handler.New takes a schema as well as the option to prettify the GraphQL response. The GraphiQL option is used to enable the documentation and the interactive browser editor for the exposed API.

  1. Now, run the program, like this:
go run $GOPATH/src/github.com/git-user/chapter10/graphqlServer/
main.go

This starts a GraphQL server on localhost:8000.

  1. Open a browser and visit http://localhost:8000. You should see the interactive GraphQL editor. Now, paste the following client query into the left-hand pane:
query {
players {
highScore
id
isOnline
levelsUnlocked
}
}

You'll see the response that's served by our GraphQL server in the right-hand pane:

{
"data": {
"players": [
{
"highScore": "1100",
"id": 123,
"isOnline": true,
"levelsUnlocked": []
},
{
"highScore": "2100",
"id": 230,
"isOnline": false,
"levelsUnlocked": []
}
]
}
}

The response contains mock data, but this data should be dynamically generated in a real-world API. For more details about creating advanced queries and mutations within the server, refer to the graphql-go documentation (https://github.com/graphql-go/graphql).

You can use GraphiQL (an interactive GraphQL editor) as an API documentation service for your GraphQL schema. It is similar to the Swagger API specification. See the GitHub interactive API editor for inspiration: https://developer.github.com/v4/explorer/.