Building an API with GraphQL and Go

Logo from graph-gophers/graphql-go

In this blog post we are going set up an API using Go, GraphQL, and PostgreSQL. I’ve gone through a few different iterations on the structure of this application and so far this is the one I like the best. For the most part, my background in writing web APIs has been with Node.js and Ruby/Rails. I’m finding that this created a bit of a struggle when first trying to figure out designing web apis in Go. One article that was really helpful to me was Structuring Applications in Go by Ben Johnson. It has influenced some of the code in this tutorial and I recommend reading!


First things first, lets start by getting the project set up. I’ll be using macOS for the tutorial but for the most part that won’t matter. Here are links to setting up Go and PostgreSQL on mac if you don’t have them on your machine. Below is a link to the code in full for reference:

Create a new project - we’ll call it go-graphql-api. To make it easier lets also create the whole project structure right away.

├── gql
│ ├── gql.go
│ ├── queries.go
│ ├── resolvers.go
│ └── types.go
├── main.go
├── postgres
│ └── postgres.go
└── server
└── server.go

There are a couple of Go dependencies we’ll also want to grab. I like using realize for hot reloading while developing, chi and render from go-chi as a lightweight router and to help manage request/response payloads, and graphql-go/graphql as our GraphQL implementation.

go get
go get
go get
go get
go get

Lastly, let’s set up a database and some mock data to be able to test against. Run psql to enter the Postgres console and we’ll create a database.

CREATE DATABASE go_graphql_db;

Then we’ll connect to it:

\c go_graphql_db

Once connected, go ahead and paste the following SQL into the console.

We just created a basic users table and inserted six new users. This will do just fine for the purpose of this walkthrough. We should now be good to start building out our API!


Throughout this post all of the code snippets will include a fair bit of comments within them to help with explaining things step by step.

We’ll start with main.go.

Keep in mind the import paths up top for your local gql, postgres, and server packages will be different (path will include your name not mine) as well as the Postgres user you pass into postgres.ConnString(). There are a few different things going on inside of initializeAPI() so we’ll go through step by step and build out each piece.

The router is created with chi.NewRouter() which returns a mux for us. The next few lines we are creating a new connection to our Postgres database. We build a connection string with postgres.ConnString() and pass it into postgres.New(). This is our package code so let’s build that out! We’ll hop over to postgres.go.

The general idea here is that we provide the ability to create a new database which returns a Db struct holding the connection. We then write GetUserByUsername() as a method on Db. As you can tell the actual functionality/query is pretty useless, but the point is to get everything wired up as a good starting point.

Our next concern back in main.go is on line 40 where we’re creating a a root query that will get used to create our GraphQL schema. Let’s jump into queries.go inside our gql package.

We pass our db into NewRoot() and then create a Resolver with it. This way all of theResolver methods have access to the database. We then create a new root that has a users query. It takes a an argument called name and is of type graphql.NewList of User (slice/array of User) which we’ve moved out into types.go inside of the gql package. This is where we could start to grow our root to hold many different kinds of queries. Remember to change the import path for your local postgres package to your own. Let’s take a look at resolvers.go.

Same thing again here with updating the postgres import. Here is where we would start adding more resolver methods when we need them. Let’s open up types.go.

In a similar fashion, here is where we would start adding all of our different types. As you can see within each field we specify the type as well. Now if you look on line 42 back in main.go we create a new schema with our root query.

Almost there!

Down a little further on line 51 we create a new server. It holds a pointer to our GraphQL schema. Let’s check out server.go.

Here we have a method on our server called GraphQL. For all intents and purposes this will be the only method needed to handle GraphQL queries. Reminder: update the import path for gql. The last file we need to look at is gql.go.

This simply holds the function ExecuteQuery() which runs our GraphQL queries. I imagined that in here is where we would create something like an ExecuteMutation() type function as well to handle GraphQL mutations.

At the end of initializeAPI() we add some middleware to the router and mount our GraphQL server method to handle POSTs to /graphql. If any RESTful routes were needed, they could be mounted here and methods for handling them could be added to Server.

Go ahead and run realize init in the root of the project. You should be prompted twice and respond with n both times.

This creates the .realize.yaml file in the root of your project. Let’s go ahead and overwrite it with:

The important piece of this config is that it will watch for any changes within the project and when it detects one, it will automatically restart the server and rerun main.go.

There are some great tools for exploring your GraphQL API like graphiql, insomnia, and graphql-playground. You can also just make a POST request sending over a raw application/json body like this:

"query": "{users(name:\"kevin\"){id, name, age}}"

In Postman it would look something like this:

Go ahead and ask for just one, or any combination of attributes in your query. In true GraphQL fashion we can request just the information we want sent over the wire.

Great Success!

That’s it! Hopefully this has been useful to help get you off the ground with writing a GraphQL API in Go. I tried to break it down into it’s package/file layout so that it had room to grow/scale, for clarity, and to make testing each piece easier. Please feel free to leave comments if you have any recommendations or questions!

Follow me on Twitter for content on software engineering, blockchain, and video games.

Bradford Lamson-Scribner

Written by

Software Developer 💾 Eth Geek 👽 Bird Whisperer 🐤

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade