Golang: How To Guide

Build Efficient APIs in Go with GraphQL and gqlgen

A step-by-step guide to building robust, flexible APIs in Go

Maina Wycliffe
The GoDev Corner

--

Gopher credit art: @egonelbre | Source: github.com/egonelbre/gophers

Introduction: Why gqlgen and GraphQL?

Ahhh REST APIs — good ol’ faithful. Simple, straightforward, and easy to use.

They’ve been the “go-to” option for developers for quite a while. After all, who doesn’t want predictable and consistent APIs?

However, use cases are becoming more complex, more applications are requiring sophisticated data relationships, and efficient data retrieval is becoming paramount.

So some of the major drawbacks of using REST API’s are slowly being exposed (e.g., issues with documentation and over/under-fetching of data).

Funny meme expressing interest to be more efficient and not waste time underfetching data

As a result, GraphQL has emerged as a promising alternative by conveniently addressing these concerns.

In fact, one of the main advantages of GraphQL is that it offers a query language similar to SQL, allowing you to retrieve specific data through a single query, rather than relying on multiple requests to various endpoints. This improves efficiency and reduces network traffic, among other benefits.

Additionally, GraphQL provides more flexibility for other developers to consume your API, even with deeply nested data.

There are several libraries available for building GraphQL APIs in Go, each with its own unique set of strengths and weaknesses. But in this article, we’ll be focusing on one of my favorites — gqlgen.

Gqlgen is a schema-first GraphQL library that allows you to quickly build and deploy GraphQL APIs in Go, all while maintaining reliability and consistency.

Overall, gqlgen is an excellent tool to leverage when building GraphQL APIs in Go. My goal here is to show you how you can use it to your advantage.

Ready? Let’s dive in.

The GoDev Corner unique separator

❗️TL;DR

While REST APIs have been a popular choice for developers due to their simplicity and predictability, they also have their limitations.

With the need for more complex use cases and efficient data retrieval, GraphQL has emerged as a promising alternative, offering a more flexible and efficient way to consume APIs.

Gqlgen, a schema-first GraphQL library, can help you quickly build and deploy high-quality GraphQL APIs in Go, all while ensuring reliability and consistency.

This is a tutorial on how to use gqlgen to develop GraphQL APIs.

Gqlgen: Configuration & Setup

How gqlgen works

To build our GraphQL API using gqlgen, we’ll start by creating a GraphQL schema. From this, gqlgen will automatically generate all the required Go code for resolvers (i.e., type and fields resolvers) and data structures.

👉 Note: After you make changes to the schema, you’ll need to regenerate the code to ensure our resolvers and models remain in sync with it

Getting started with gqlgen

Assuming you already have an initialized Go project, let’s create a tools.go file with the following content:

//go:build tools
// +build tools

package tools

import (
_ "github.com/99designs/gqlgen"
)

Next, we’ll want to install our dependencies within our source code by running the go mod tidy command:

go mod tidy

…and scaffold all the necessary configs needed for our gqlgen workspace:

go run github.com/99designs/gqlgen init

The end result should look like this:

visual representation of scaffolding all the configs

Next, let’s modify our resolver.go file and add the comment below after the Go imports, which tells the go generate command how to generate our schema when we run the go generate ./… command:

//go:generate go run github.com/99designs/gqlgen generate

Now, our resolver.go should look something like this:

package graph

// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you
// require here.

//go:generate go run github.com/99designs/gqlgen generate

type Resolver struct{}

🎯 Pro Tip: Our Resolver struct can be used to inject dependencies such as DB Clients, and we can access them inside our resolvers. For instance, let’s say we build a To-Do application; we then can store our list of tasks in a variable and pass it into the struct:

type Resolver struct {
TodoList []*model.Todo
}

Then, inside one of our resolvers, we can access our list of tasks:

func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
return r.TodoList, nil
}

Configuring Gqlgen for flexibility

The flexibility of gqlgen is virtually limitless, and it allows you to configure nearly everything from file directories for generated files to more complex tasks, such as linking GraphQL schemas types to existing Go structures.

This can help you bypass struct generation and link to pre-existing ones, which can provide greater versatility in the development process. You can also configure other items as well, such as:

  • Turning on gqlgen tags —i.e., instead of using JSON tags in Go structs, you can use the gqlgen:fieldName tag
  • Disabling and enabling slice pointers
  • Automatically binding existing Go structures within a package to corresponding GraphQL types
    [This means that if a struct within a “models” directory shares the same name as a GraphQL type, gqlgen will prioritize the existing struct rather than create a new one]
  • And so much more (see here for more information)
meme featuring joey from Friends saying “who likes automation?” this guy

🎯 Pro Tip: You can break up your schemas into multiple files with gqlgen to manage your resolvers easier. So, instead of generating a single Go file with all resolvers, let’s say, the resolver code can be spread across multiple files to match the schema layout structure.
Note: you can always modify this for your specific needs.

The GoDev Corner unique separator

GraphQL Schema

Let’s build a simple To-Do application to demo GraphQL. Since gqlgen is a schema-first tool, we’ll start by building the graphql schema.

First, let’s define the type. And to keep it simple, we’ll narrow the fields to just id, text and done.

type Todo {
id: ID!
text: String!
done: Boolean!
}

Next, we need a GraphQL query to fetch our To-Do tasks:

type Query {
todos: [Todo!]!
}

And then, we need GraphQL mutations to create, mark as done, and remove our To-Do tasks:

type Mutation {
createTodo(input: NewTodo!): Todo!
toggleTodoDone(id: ID!): Todo!
removeTodo(id: ID!): Todo!
}

One last thing — even though we’re done with our schema, we’ll want to run code generation before we can proceed and implement our To-Do application:

go generate ./...

👉 Note: This will help generate the To-Do struct and resolvers needed for our queries and mutations.

The GoDev Corner unique separator

Resolvers

If you’re new to resolvers, they’re functions that help populate data for our schema from databases, APIs, etc., then return it to the caller.

In our case, we’ll need four unique resolvers:

  • todos — a resolver that will fetch all our To-Do tasks
  • createTodo — a mutation that will add new To-Do tasks
  • toggleTodoDone — a mutation that will mark To-Do tasks as complete (or not)
  • removeTodo — a mutation that will remove To-Do tasks

Let’s go ahead and implement them.

Todos Resolver — Fetching our To-Do tasks

To keep things simple, let’s store our “To-Do” in an array that will be passed to our resolver struct:

type Resolver struct {
TodoList []*model.Todo
}

We’ll also want to create a To-Do variable and pass it to our struct when we initialize our graphql server inside our server.go (where our main function is currently located).

func main() {
// ... other code generated by our todo

// ... our fake DB
todos := []*model.Todo{}

srv := handler.NewDefaultServer(graph.NewExecutableSchema(
graph.Config{
Resolvers: &graph.Resolver{
TodoList: todos,
},
},
))

// ... move code for starting http server
}

👉 Note: Feel free to replace the basic implementation of the DB here with your DB Client, as well as any other dependencies that your resolvers may require.

Now that that's out of the way, let’s fetch our To-Do list by returning to our application:

func (r *queryResolver) Todos(ctx context.Context) ([]*model.Todo, error) {
return r.TodoList, nil
}

If we were using a database, this is where our business logic would go to fetch and do any necessary data transformation ☝️

Ok, let’s move on to mutations.

CreateTodo Resolver — Adding new tasks

Let’s create a new To-Do struct. We need to generate an id:

id := uuid.New().String()
todo := &model.Todo{
ID: id,
Text: input.Text,
Done: false,
}

…and push the To-Do item to our list:

r.TodoList = append(r.TodoList, todo)

Finally, let’s return our To-Do item:

return todo, nil

Our mutation resolver code should now look like this:

func (r *mutationResolver) CreateTodo(ctx context.Context, input model.NewTodo) (*model.Todo, error) {
id := uuid.New().String()
todo := &model.Todo{
ID: id,
Text: input.Text,
Done: false,
}
r.TodoList = append(r.TodoList, todo)
return todo, nil
}

ToggleTodoDone Resolver— Mark tasks as complete

For our toggleTodoDone mutation, we need to find the task matching the provided id, then toggle and return the Done field:

func (r *mutationResolver) ToggleTodoDone(ctx context.Context, id string) (*model.Todo, error) {
for _, todo := range r.TodoList {
if todo.ID == id {
todo.Done = !todo.Done
return todo, nil
}
}
return nil, nil
}

RemoveTodo Resolver — Remove tasks

Similarly, for our removeTodo mutation, we need to find the task matching the provided id, remove it from the list, and return the removed task.

func (r *mutationResolver) RemoveTodo(ctx context.Context, id string) (*model.Todo, error) {
for i, todo := range r.TodoList {
if todo.ID == id {
r.TodoList = append(r.TodoList[:i], r.TodoList[i+1:]...)
return todo, nil
}
}
return nil, nil
}

And that’s it! Now we can test our application by running the Go server.

go run server.go

You can access the GraphiQL playground via the URL http://localhost:8000/ on your local server and the API endpoint via http://localhost:8000/query

The GoDev Corner unique separator

Conclusion

In this post, we built a simple To-Do application in GraphQL API using Go and gqlgen, then explored how we can customize different configurations for gqlgen.

In the next few articles, we’ll build more complex applications with field resolvers, data loaders, GraphQL custom scalers, etc., which will help us create production ready GraphQL APIs. Stay tuned

Main Takeaways

  1. gqlgen is a schema-first tool that automatically generates the required Go code for resolvers and data structures from a GraphQL schema
  2. gqlgen is highly configurable and allows you to bypass struct generation and link to pre-existing structures, which can provide greater versatility in the development process
  3. To build a Go application using gqlgen, you need to create a GraphQL schema, generate resolvers using gqlgen, then implement the code for resolvers (i.e., in our case — to fetch, create, update, and remove To-Do tasks) that will run queries CRUD queries against our database.
The GoDev Corner unique separator

Other Articles To Consider

Disclosure: In accordance with Medium.com’s rules and guidelines, I publicly acknowledge financial compensation from UniDoc for this article. All thoughts, opinions, code, pictures, writing, etc. are my own.

--

--