For 2 years I've been working with Go, building new projects with it and it's usually my first choice when I have to build a new backend project.
In this article I will show you how to build a production-ready GraphQL API with tips don't avoid errors I made when I spent hours building GraphQL APIs in Go. This architecture and setup is my current go-to one and is the result of many iterations.
In this article I won't see Go basics in-depth, I imagine that you already have some experience with the language.
The code of this article is available on my Github.
Summary
- Requirements
- Project setup
- Choosing a GraphQL framework
- Creating the GraphQL API
- Understanding code generation
- Implementing mutations and queries
- Choosing an SQL ORM
- Creating our models
- GraphQL + ORM
- Playing with our API
- Final note
Requirements
Since we will use Go, make sure you have Go installed on your machine. You can check the official page on how to install it.
I'm using the last Go version at this date which is 1.16.5. The only requirement is that your Go version supports Go Modules.
Project setup
First, you have to create a new directory containing your code and then create a new go module. For this example, I'm using my personal Github repository, but make sure to replace it with your repository name.
The
go mod init
command will generate a go.mod
at the root of your project. This file contains the dependencies of your project and specifies some information about your module:Then we will create our main. For now, we will just create an empty main to test that everything works fine:
We can run our code with the
go run
command.Your project is now setup, we now have to choose a GraphQL framework and setting up our API.
Choosing a GraphQL framework
The three most popular GraphQL framework (for server-side) in Go are the following:
I personally tested all of them but I ended using gqlgen. I personally love the API, tooling, and code generation. I used it for many projects that run in production and I can tell you that's it's really stable and makes easy the GraphQL API development in Go.
gqlgen is a Schema first framework, which means that you will write some GraphQL schema first and then gqlgen will generate the Go types and functions that will match your GraphQL schema. This is really convenient and everything is strictly typed, zero
interface{}
! If you make changes in your GraphQL schema they are also changed in your Go code.I really recommend you to use gqlgen if you want to setup a GraphQL API in Go. This is the one we will use for this example.
Creating the GraphQL API
To easily setup a GraphQL with gqlgen you can follow the official instructions. But here are the setup process:
First, you need to fetch the gqlgen module and then run the init command to generate all the basic configurations for our GraphQL API.
This will create several files and folders in your project:
The
server.go
file is just a main that will create an HTTP server, listen on a port, and expose our GraphQL API and a Playground to visually make GraphQL requests.By convention, we will rename this file
main.go
since we will use it as our main. To run it, you can use go run
command:If you open http://localhost:8080/ in your browser, it will automatically open the GraphQL playground and start making requests.

Wait, what? If we try to run any
mutation
or query
we have an internal system error
.This is because our API is generated but you now have to define your query and mutation resolvers. These resolvers are some functions that will automatically be generated by gqlgen.
Understanding code generation
Since gqlgen is a schema first framework, everything starts by defining a GraphQL schema. There is a generated one
graph/schema.graphqls
:This schema is just an example, you should replace it with your own GraphQL schema. For this project we will update it has the following:
For each
*.graphqls
file inside the graph
folder, gqlgen will generate a *.resolvers.go
file containing the Queries, Mutations...schema.graphqls
→schema.resolvers.go
This is defined in your
gqlgen.yml
at the root of your project:This config specifies that our GraphQL schemas are all files that match the glob
graph/*.graphqls
. Of course, you can add entries or modify this glob to change the folder, file extension... In this example, we will keep the default config.Now if you go to
graph/model/models_gen.go
you will see that generated file contains all the types we defined in our GraphQL schema:There is the
NewTodo
input we defined in our Schema and the Todo
and User
type. If gqlgen doesn't find these types in your Go code it will generate them by default. So we don't have to manually create these types. And finally the
graph/schema.resolvers.go
file:As I told you, each
*.graphqls
will generate a corresponding *.resolvers.go
. So this file implements the Queries and Mutations of our schema.graphqls
. This is the core of your GraphQL API. If you see the functions inside these resolvers, we can find the CreateTodo
mutation and the Todos
and UserTodos
queries.But these resolvers are by default not implemented, gqlgen only generates the function definition and lets an empty body. This is normal, you are responsible for implementing the code logic to perform the Mutation or the Query.
Choosing an SQL ORM
In this example, we want to keep our Todos inside a database. To easily query, insert and delete rows in our database we will use an ORM. It will be responsible of generating the SQL queries for us.
The two best options for me are:
These two ORMs are popular. Gorm is a more mature ORM and is maintained by a Go developer and the community since 2013. Ent is a more recent ORM is maintained by two developers from Facebook and with the community.
The main difference between the two ones is that Ent uses code generation which means everything is typed creates a lot of helpers which makes Querying data and relations really easy and the querying system is really different.
Using Ent is much easier from my perspective and the API is safer. Ent also has better integration with GraphQL. For example, it supports the GraphQL Cursor Connections Specification while you will have to implement it by hand with Gorm.
But you have to understand that Ent is still in heavy development and hasn't reached v1 yet. Some companies include me use it in production and it's really stable but you have to understand the risks if you plan to use it in production.
We will use Ent to show you how easy is it to use it with a GraphQL API.
Creating our models
In this example project, we will keep our generated schema and created models based on it:
We have two types: a
Todo
and the User
. So we will create these models with Ent. We will use go get
to fetch the Ent module and then run the ent init
command to create our User
and Todo
models.These commands will generate a new
ent
folder at the root of your project. It will contain the generated code with everything related to the strictly typed ORM API. The only interesting files in the ent
folder are located inside the ent/schema
folder. This folder contains all the schema definitions of your models. You have to define your model fields with Go code. By default an empty schema is defined for
todo.go
and user.go
:I recommend you read the official documentation to better understand how this schema works.
Then we just have to define our fields inside the
Fields
method, starting with user.go
:We define a
name
field for our User
that must be unique and the minimum length is 3. There is a ton of options, such as validation, minimum and max length, default value... I recommend you to read the documentation or to play with the autocompletion of your IDE to find the methods your need. By default, if we don't define an id
field It will be generated by default with an auto-increment Integer
as ID.Then we can create our
Todo
model fields:And then we have to run the
ent generate
command to generate all the types:I will generate tons of files in the
ent
folder for you. Remember that the only files you should care about in this folder are in the ent/schema
folder.Good job, your models are now created. You now have created the database client to connect to your Database and start performing SQL requests. To get started we will be using a
sqlite3
database.We have to go edit our
main.go
:We have to import
_ "github.com/mattn/go-sqlite3"
to use our Sqlite3 database, and then we define a newClient
function to open a sqlite3
database connection and run the database migrations (creating our models in the database).Then we store the
client
inside a variable and call the defer Close
method to close the database connection on server shutdown.We now have to pass this
client
to our GraphQL resolvers so we can perform SQL requests inside our Queries or Mutations. To do so we have to update the graph/resolver.go
file:We had the
Client
field in our Resolver
struct. This structure will be shared across all our resolvers.And we update our main to pass the
client
as parameter to the NewDefaultServer
function:That's it, we created our models, created a Database connection, and passed our Database client to the GraphQL resolver.
GraphQL + ORM
As I told you before, gqlgen will generated types that are not found. We generated an
ent.Todo
model but in our graph/resolver.go.Todos
we have the following function definition:As you can see the function except for a
[]*model.Todo
but we want to use our new model []*ent.Todo
.To do so we have to update our
gqlgen.yml
config and update the autobind
field to resolve our ent
types:This will tell gqlgen to find our models generated with Ent and use them instead of generated new ones. Note that the names inside your GraphQL schema and Ent model have to be the same. If not you have to manually define them inside the
models
fields like it is done for the ID
and Int
fields.Now we need to fetch the
ent/entgql
module. It provides integration with the gqlgen framework we're currently using:We only defined our
Todo
model fields but we didn't define a User
field connection. To do so we have to go to our ent/schema/todo.go
file and update the Edges
.We create an Edge connection
to
our User
and make it Unique
(there is only one user linked to a Todo
) and Required
(it must have a creator).Then we can also create a reverse relations, for example to fetch all the
Todo
of an User
inside ent/schema/user.go
:We use the
From
method to create a reverse relation, we specify the name we want and use the Ref
which is the Todo
edge To
name we just defined.Create the following file
ent/entc.go
:And update the
ent/generate.go
file:And create the
graph/generate.go
file:Now you can run the
go generate ./...
command at the root of your project to both generate ent and gqlgen code generation.We can now start implementing our
graph/schema.resolvers.go
. Here is the full code:Playing with our API
Now you can run the server and start using our GraphQL API.
We can execute the
createTodo
mutation:
And we obtain the following response:
Everything works well! Let's create another
Todo
with a different text:
We can also query the
Todo
:
Or query the Query by our
User
name field. If we specify our user name user-1
we get the list of its Todo
:
But if we put a non-existing user name the list is empty as it should be:

Final notes
As you saw, setting up a GraphQL API with Go can be a bit tricky. I tried to give you the best quick-start so you can create yourself an API with good practices and avoid the errors I made in the past.
Make sure to check the full code on my Github. Feel free to open an issue for any improvement or error you spotted!