Developing GraphQL servers in Go

In this post, we will create a simple GraphQL server using Jaal. Jaal is GraphQL server library developed in Go. Go is an open-source, compiled and statically typed programming language developed by Google. Refer to this blog or official Go Tour for more information about Go. GraphQL is a query language for APIs, developed and open-sourced by Facebook. Please refer to official documentation or this blog for more information.

Jaal provides various APIs to build a GraphQL server. We will explore how inputs, objects( AKA payloads) and enums can be registered on the schema & how to create queries and mutations. We will also see how to add introspection with just one line!

The GraphQL Schema

Let’s create a GraphQL server with the following schema:

The GraphQL schema defined above exposes two queries and one mutation. The query character fetches a Character by its ID while query characters list all the characters. We have defined one mutation createCharacter which creates a new Character.

Enough chit-chat, let’s dive into the implementation!

Step 1: Defining the required types

As you can see, we have defined all the required types. We have defined our Character object, the Type enum and CreateCharacterRequest.

We then implemented the server with all the required APIs, i.e. GetCharacter()ListCharacters() and CreateCharacter().To keep this simple, we have stored the data in memory. If you want, you can use a database to store and fetch the data.

Step 2: Exposing the APIs

Now that we have defined all our required types and APIs, we will register them on a schema.

2.1: Registering the types on the schema

2.1.1: Registering Character object

To register an object on the schema, we use method Object()

func (s *Schema) Object(name string, typ interface{}) *Object

It takes the name with which the object is to be registered on the schema and the type of the object.

To register a field on the object, we use the method FieldFunc()

func (s *Object) FieldFunc(name string, f interface{})

It takes the name with which the field is to be registered on the object and a function to expose the field. Thus, we can pre-process the data if we want. For instance, we have registered field Id of Character using schemabuilder.IDJaal uses schemabuilder.ID to register a field as ID datatype of GraphQL.

2.1.2 : Registering CreateCharacterRequest input

To register an input on the schema, we use method InputObject()

func (s *Schema) InputObject(name string, typ interface{}) *InputObject

It takes the name with which the input is to be registered on the schema and the type of the struct.

To register a field on the input, we use the method FieldFunc()

func (io *InputObject) FieldFunc(name string, function interface{})

It takes the name with which the field is to be registered on the input and a function. The number of arguments in function should be two. The first argument should be the struct registered as input while the second argument should be the field which is to be exposed. In the function, we set the field of struct in the way we want.

Thus, we can pre-process the data if we want. For example, if we want the name to be stored in uppercase, then we can do so in the following way:

input.FieldFunc("name", func(target *CreateCharacterRequest, source string) {
target.Name = strings.ToUpper(source)
})

2.1.3: Registering Type Enum

To register an enum, we use schema.Enum() method. The first argument takes the datatype of the enum, while the second argument is a map which registers enum members on the schema using the key of the map.

2.2: Registering APIs as query and mutation

As you know, queries are represented as fields on the root Query object in the GraphQL schema while mutations are represented as fields on the root Mutation object. We can create queries and mutations in the following way:

We use FieldFunc() method of schema.Query() to register a query on the schema. While registering a query, the second argument of the function passed to FieldFunc() must be a struct. The exported fields of this struct (in our case anonymous args struct{} are exposed as the arguments of the query. Same goes for mutation.

Step 3: Running the server

Now that we have defined all the required functions, it is time to run the server and see our code in action.

To be able to use our schema on a server, we need to build it. The method Build() on schemabuilder.Schema does that. It also checks whether there is an irregularity on the schema and reports it.

To add introspection to our server, we use AddIntrospectionToSchema() API provided by Jaal.

Step 4: Seeing it in action

Let’s piece together our code and run it.

To run queries and mutations, we can use GraphQL Playground. To run the example, run the command go run example.go . The server will start at http://localhost:9000/graphql. The following video shows how to create and fetch a Character.

Next Steps / Conclusion

We created a simple GraphQL server. But Jaal does so much more than this. It has inbuilt features such as include & skip directives, and maptimestamp & duration scalars. It also provides APIs to register custom scalar types on the schema, create union and interface types and schema-stitching or connections.

To create this simple server, we had to write a lot of boilerplate code. First of all, we had to define the types, register them on the schema and then we were able to define queries and mutation. To generate this boilerplate code, we use Jaal along with protocol buffers and gRPC. We developed protoc-gen-jaal plugin, which registers all the messages and services of a proto on the schema. You can explore more about it here.

Product Developer

1 comments On Developing GraphQL servers in Go

Leave a reply:

Your email address will not be published.

Site Footer