Praise Adanlawo Praise is an Infrastructure and cloud engineer with good hands on knowledge building CI/CD pipelines, automation, infrastructure as code (terraform), source code management and source code containerization

3 tips for implementing GraphQL in Golang

6 min read 1745

The GraphQL and Golang logos.

What is GraphQL?

Backend technologists would typically say that GraphQL came as “the shiny new kid on the block” after REST APIs. Initially developed by Facebook to be a powerful data-fetching API for internal use, today it powers billions of API calls daily.

The official documentation defines GraphQL as a query language for APIs that gives a 360 degree view of the data in the API. But a definition from Freecode camp says:

“GraphQL is a syntax that describes how to ask for data, and is generally used to load data from a server to a client. It lets the client specify exactly what data it needs in a request, thereby making it easier to select data from multiple sources.”

Prerequisites

This tutorial will highlight some major points to note when implementing GraphQL with Golang. To follow along, it is strongly recommended that you have a working knowledge of:

Let’s brush up on GraphQL

In any scenario where GraphQL is discussed, some keywords will be used more often than others. As such, it is imperative to remind ourselves of some of those words:

GraphQL types: These are the most basic components of a GraphQL schema definition. They are JSON fields representing the kind of objects that can be fetched from the service. Check out this tutorial to understand GraphQL types in detail.

As an example, here is a sample type definition of an author that has three fields: name, id and book:

type author {
  name: String! //the `!` sign shows it’s required
  id: ID!
  book: String!
}

GraphQL queries: GraphQL queries are a way of requesting or fetching data from a GraphQL server. How the queries are structured would determine if data would be over-fetched or under-fetched. This tutorial explains queries in detail.

Here is a sample query on the names of authors, following our type definition above:

query {                     
  authors {                 
    name      
  }
}

GraphQL schema: Schemas are like maps. They help us understand the data by describing their relationships within the server. This helps clients know how to structure their requests when trying to reach the data. They are written in SDLs, i.e Schema Definition Language.

We made a custom demo for .
No really. Click here to check it out.

Resolvers: Resolvers hunt for the requested data in the server. They are field specific functions. It is the only mechanism GraphQL uses to return data per query request.

GraphQL mutations: Mutations are used to create and modify objects in GraphQL, similar to PUT and POST in REST APIs. They follow similar syntax as queries, with the exception that they need to start with the keyword mutation.

GraphQL subscriptions: This is a feature that allows the server to send data to its clients for every specific event it was configured for. To do this, the server maintains a steady connection with the client subscribed to it

Why GraphQL with Golang?

The Go programming language, otherwise known as Golang, is a general purpose programming language initially designed by Google. Golang tops the ranks for its simplicity, concurrency, and fast performance, among many other beautiful features. As such, it remains one of the top options when implementing GraphQL.

As many people came to realize the potential of this duo (GraphQL and Golang), software engineers over the years began to build libraries to integrate GraphQL seamlessly with Golang.

Here are some libraries that make the implementation smooth:

  • Graph-gophers/graphql-go : This provides support to parse the GraphQL Schema Language while giving resolver info at compile time without having to wait until runtime to find issues with schema and resolvers.
  • GraphQL-go/graphql : This has support for queries, mutations, and subscriptions like other libraries. It also models the official implementation of the GraphQL-js library
  • Gqlgen : This prioritizes type safety and is based on a schema first approach.

We won’t focus on the implementation of any of these libraries in this tutorial. However, each of these libraries has an examples folder you can navigate to in order to see firsthand how these libraries are implemented.

Here come the lightbulb moments

Let’s explore some scenarios we can experience when integrating GraphQL with Golang and what the solutions are.

Tip #1: Schema homogeneity

The graphql-go/graphql is a fully-featured implementation of GraphQL in Golang that supports queries, mutations, and subscriptions, among many other features.

Though this library takes advantage of other libraries to help you build production-ready GraphQL servers, it is an official reference implementation of GraphQL.js. In other words, it is the Golang version of the GraphQL.js library.

The benefit of its near-identical API to GraphQL.js makes it so easy to read another GraphQL schema written for Node.js/JavaScript, and you’ll still be able to rewrite it for Golang. Let’s take a look:

GraphQL schema definition for Golang with graphql-go

We import the graphql-go package and define the GraphQL object type named BaseQuery with a hello field to create the schema.

package main
 
import (
   "log"
   "github.com/graphql-go/graphql"
)
 
var queryType = graphql.NewObject(graphql.ObjectConfig{
   Name: "BaseQuery",
   Fields: graphql.Fields{
       "hello": &graphql.Field{
           Type: graphql.String,
           Resolve: func(p types.ResolveParams) interface{} {
               return "World!"
           },
       },
   },
})
 
var Schema, err = graphql.NewSchema(graphql.SchemaConfig{
   Query: queryType,
})
if err != nil {
   log.Fatalf("failed to create new schema, error: %v", err)
}

Creating the GraphQL server after the schema creation follows the normal steps as highlighted in the official graphql-go documentation here.

Graphql schema definition for JavaScript with GraphQL.js

From the code snippet below, we require the GraphQL package and define the GraphQL object type named BaseQuery with a hello field. This follows a similar process as highlighted in the schema above.

var graphql = require('graphql');
 
var queryType = new graphql.GraphQLObjectType({
   name: 'BaseQuery',
   fields: {
       hello: {
           type: graphql.GraphQLString,
           resolve: function () {
               return 'World!';
           }
       }
   }
});
 
var schema = new graphql.GraphQLSchema({
   query: queryType
});

Taking a close look at the two representations above, we can conclude that implementing GraphQL in Golang using the graphql-go library is a walk in the park irrespective of what programming language you’re familiar with.

This is because the graphql-go library closely mirrors the official reference implementation of GraphQL.js, which is the defacto library of choice. As a result, it provides near-identical APIs to the GraphQL.js library.

Tip #2: Solving the n+1 problem

Unlike REST APIs that use one resolver(see definition above) per API endpoint, GraphQL executes one resolver per field. This implies that there might be too many additional resolvers per request, especially for nested data. This is the n+1 problem.

Here is an example query to further buttress this:

query {                     
 authors {            # fetches authors - query 1
   name      
   book {             # fetches books for each author (N queries for N authors)
     title
   }
 }
}                     # N+1 round trips

Here is the response from the query:

{
 "writers": [{
   "name": "Tom Wolfe",
   "book": {
     "title": "The Bonfire of the Vanities"
   }
 }, {
   "name": "ALice Walker",
   "book": {
     "title": "The Color Purple"
   }    
 }]
}

The example above might not show how quickly this can escalate to become a problem. But assuming we need to fetch data for 100 authors, the resolver goes on 100+1 trips to fetch a response. Meanwhile, this could’ve been done in just two trips; i.e, the request and the response.

The fix to this problem is batching.

Batching is the primary feature of a Dataloader. The Dataloader was originally developed by Facebook in 2010 for GraphQL.js to be used as part of an application’s data fetching layer. It provides a simplified and consistent API over remote data sources via batching and caching. However, several other dataloader libraries have been developed for graphql-go, such as:

  • graph-gophers/dataloader : This is a replica implementation of the official facebook dataloader (written in JavaScript) in Golang
  • Dataloaden from the author of gqlgen : This generates type-safe dataloaders in Golang, for use with the gglgen library for prioritizing type safety. It is also inspired by the official dataloader library

And many others.

Dataloaders (irrespective of the library) solve the n+1 problem by allowing the resolver to request data and return a promise for the data, rather than immediately returning the exact field requested. This then batches all responses from the request and responds at once, hence two trips — request and response.

However, let’s look at the graphql-gophers/dataloader library in more detail.

Here’s a sample implementation of how the library could be used without cache. For more examples, follow the examples directory of the library here.

package no_cache_test
 
import (
 "context"
 "fmt"
 
 dataloader "github.com/graph-gophers/dataloader/v6"
)
 
func ExampleNoCache() {
 // go-cache will automaticlly cleanup expired items on given diration
 cache := &dataloader.NoCache{}
 loader := dataloader.NewBatchedLoader(batchFunc, dataloader.WithCache(cache))
 
 result, err := loader.Load(context.TODO(), dataloader.StringKey("some key"))()
 if err != nil {
   // handle error
 }
 
 fmt.Printf("identity: %s", result)
 // Output: identity: some key
}
 
func batchFunc(_ context.Context, keys dataloader.Keys) []*dataloader.Result {
 var results []*dataloader.Result
 // do some pretend work to resolve keys
 for _, key := range keys {
   results = append(results, &dataloader.Result{Data: key.String()})
 }
 return results
}

Tip #3: Client-server data schema mismatch

Typically, coding a GraphQL backend from the scratch involves duplicating quite a number of similar code (schema), e.g different but similar schema for the database and the API endpoint.

This is because the different schema has affected the way the document is stored.

Example: A query on an author from the database would return an authorID property, but the same query on an author from the server directly would return author property. This makes it difficult to share code between client and server to simplify the codebase.

const fetchBookTitleClient = author => {
   return author.book.title
}
 
const fetchBookTitleServer = author => {
   const authorDeets = Books.findOne(author.bookId)
   return authorDeets.title
}

A fix for this is server-server queries

In retrospect , this is done by adding the GraphQL schema to the GraphQL function like this:

graphql(
 schema: GraphQLSchema,
 requestString: string,
 rootValue?: ?any,
 contextValue?: ?any,
 variableValues?: ?{[key: string]: any},
 operationName?: ?string
): Promise<GraphQLResult>

But in implementation, within my code I run the server-to-server queries this way:

const result = await graphql(executableSchema, query, {}, context, variables);

This allows drastic simplification of code without trading performance.

Conclusion

GraphQL is more than just an API query language, as we have seen through the course of this tutorial. It has the potential to totally change the game in terms of data and API queries.

With such potentials, the full benefits of GraphQL must be harnessed by a programming language like Golang.

The Go programming language easily tops the ranks because of its concurrency, simplicity, and fast performance.

I hope you enjoyed reading this post about the gotchas when implementing GraphQL in Golang. Feel free to leave any questions or feedback.

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    Monitor failed and slow GraphQL requests in production

    While GraphQL has some features for debugging requests and responses, making sure GraphQL reliably serves resources to your production app is where things get tougher. If you’re interested in ensuring network requests to the backend or third party services are successful, try LogRocket.https://logrocket.com/signup/

    LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic GraphQL requests to quickly understand the root cause. In addition, you can track Apollo client state and inspect GraphQL queries' key-value pairs.

    LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
    Praise Adanlawo Praise is an Infrastructure and cloud engineer with good hands on knowledge building CI/CD pipelines, automation, infrastructure as code (terraform), source code management and source code containerization

    Leave a Reply