Building a GraphQL API with Golang, Postgres, and Hasura
Known for its simplicity, advanced performance, concurrency, and distributed systems benefits, Golang (or Go) is growing in popularity among the development community, and so is GraphQL — a simplified and efficient query language for retrieving and mutating data from APIs.
By building GraphQL APIs in Golang, developers get the efficiency, fast performance, and concurrency benefits of Golang combined with GraphQL’s data simplification and performance features, making it easy for developers to build scalable, reliable, and efficient software.
This article will explore building a GraphQL API for a cinema movie catalog using Golang connected to a Postgres database on Hasura. Our API can retrieve a list of movies and add a new movie to the catalog.
Check out the Go backend course. In this course, you will learn how to integrate Go in a GraphQL backend server stack with Hasura.
To Get Started, Set Up a Few Prerequisites
You will need the following prerequisites to begin this tutorial.
- Golang installed on your local development machine (see the official docs)
- Your preferred code editor (we recommend Visual Studio Code)
- A basic understanding of GraphQL, which you can find in our GraphQL tutorial
Step 1: Select a Golang GraphQL Library
There are several library options for building GraphQL APIs in Golang: graphql-go, Thunder, gophers, and gqlgen;however, we will be using gqlgen in this article. gqlgen is a popular library for building GraphQL APIs in Golang. It is a popular choice because of type safety and code generation features, which help developers build GraphQL APIs quickly and efficiently.
Another advantage of the gqlgen GraphQL Golang library is its schema-first approach. The schema-first approach allows you to define the data requirements (schema) of the GraphQL service using the GraphQL Schema Definition Language (SDL) and then focus on the actual implementation (business logic) later. With gqlgen, you get automatic code generation for query and mutation resolvers based on your GraphQL schema.
Step 2: Set Up Your Golang GraphQL Project and gqlgen
In this step, we will be setting up our Golang project and installing the gqlgen Golang GraphQL library.
- Create a directory for your project. In your working directory, open your terminal and initialize it as a Go Module.
go mod init github.com/<user-name>/golang-graphql
- Execute the following command to install the gqlgen library.
go install github.com/99designs/gqlgen@latest
- Create a file called tools.go at the root of your project, where you will list all your dependencies and paste the following code snippet.
// tools.go
package tools
import _ "github.com/99designs/gqlgen"
- Open your terminal and execute the tidy command to add all missing dependencies.
go mod tidy
- Create the Golang GraphQL project structure using the init command.
go run github.com/99designs/gqlgen init
This command creates the project layout and files to run the GraphQL API. The server.go file is for running the GraphQL API, and the schema.graphqls contains some boilerplate schema definitions for the GraphQL API.
- Open http://localhost:8080/ to test the GraphQL playground.
Let's define the GraphQL schema for our Movie API now that we've successfully set up our project.
Step 3: Define Your GraphQL Schema
A GraphQL schema defines the data requirements that clients can request from the GraphQL API. In this next step, we will describe the GraphQL schema for our Movie API by modifying the schema. graphqls file.
Navigate to the schema.graphqls file and replace the contents with this code snippet, defining the Query and Mutation schema for our Movie API.
// schema.graphql
type Movie {
id: ID!
title: String!
url: String!
releaseDate: String!
}
type Query {
movies: [Movie!]!
}
input NewMovie {
title: String!
url: String!
}
type Mutation {
createMovie(input: NewMovie!): Movie!
}
The code above defines a Query and a Mutation type. The Query type retrieves all movies, and the Mutation type contains a createMovie method that takes in a new movie object as its input parameter.
Step 4: Generate Your GraphQL Resolvers
GraphQL resolvers are functions that generate responses for a GraphQL query. The gqlgen library automatically generates resolvers based on the schema's defined schema. graphqls file, so you don't need to write the boilerplate code manually for generating resolvers.
- Navigate to the schema.resolvers.go file and delete the file's contents.
- Execute the following command to use the gqlgen code generation feature to generate resolvers based on your defined schema.
go run github.com/99designs/gqlgen generate
The command above generates the skeleton of two GraphQL resolvers: one Query resolver function and one Mutation resolver function. The createMovie method will implement the movie creation logic, and the function Movies will contain the movie retrieval logic. Later in this tutorial, we will come back to implement the logic for these resolvers.
Step 5: Provision Your Postgres Database on Hasura Cloud
We will be creating a managed Postgres database on Hasura cloud to store data for our movie application. (You can also use any database of your choice and connect it to your Golang project.)
- Sign in to Hasura Cloud.
- Create a new project and click on the Launch Console button.
- Navigate to the Data tab and connect an existing database or create a new managed Heroku Postgres database.
- After creating the database, click on the Create Table button in the Data tab and create the table movies with the following columns defined in your GraphQL schema.
- Head over to the Project → Settings → Env vars → Custom Env vars to copy your Postgres database URL, which you will connect to in your Golang project.
Step 6: Connect Your Postgres Database to Golang
We'll use the Postgres URL we copied in step 5 to link our Postgres database to the Golang project in this step.
- Create an a .env file in the root of your project folder and paste the Postgres database URL as shown below.
DB_URL=postgres://znydojgffymetm:bb7d8540de0280a0lru2oau96q3
After that, you'll need a database driver to connect to the Postgres database using Golang's native SQL package. go-pg is a Golang library that converts ORM (object-relational mapping) queries into Postgres SQL Queries. godotenv is a Golang package that allows you to load environment credentials from an a.env file into your app.
- Execute this command to install these drivers.
go get github.com/go-pg/pg/v10 github.com/joho/godotenv
- Open your terminal and execute the tidy command to add any other missing dependencies.
go mod tidy
- In the graph project folder, create a db.go file and paste the following code block.
// db.go
package graph
import (
"os"
"github.com/go-pg/pg"
)
func Connect() *pg.DB {
connStr := os.Getenv("DB_URL")
opt, err := pg.ParseURL(connStr)
if err != nil {
panic(err)
}
db := pg.Connect(opt)
if _, DBStatus := db.Exec("SELECT 1"); DBStatus != nil {
panic("PostgreSQL is down")
}
return db
}
The above code block defines a function that connects to the Postgres database using the database URL specified in the environment variable.
We will set up this connection in our server.go file to establish a link to the Postgres database as the server starts.
- Modify the contents of the server.go file using the code snippets highlighted in yellow.
// server.go
package main
import (
"github.com/joho/godotenv"
)
const defaultPort = "8080"
func main() {
// ...
err := godotenv.Load(); if err != nil {
log.Fatal("Error loading .env file")
}
// ...
Database := graph.Connect()
srv := handler.NewDefaultServer(
generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{DB: Database}}))
// ...
}
The above code block calls the Connect function in the db.go file and stores that connection within the Resolver struct.
- In the graph folder, navigate to the resolver.go file to add the DB connection field in the Resolver struct as a dependency.
// resolver.go
package graph
import "github.com/go-pg/pg"
type Resolver struct {
DB *pg.DB
}
We have successfully configured our Postgres database in our Golang project. In the next step, we will be implementing our resolver functions.
Step 7: Implement Your Generated Resolvers
This step will implement the logic for the resolver methods in the schema.resolvers.go file.
- Paste the following code snippet in the createMovie function; it takes in the movie object as input and inserts it into the Postgres database.
// schema.resolvers.go
func (r *mutationResolver) CreateMovie(ctx context.Context, input model.NewMovie) (*model.Movie, error) {
movie := model.Movie{
Title: input.Title,
URL: input.URL,
}
_, err := r.DB.Model(&movie).Insert()
if err != nil {
return nil, fmt.Errorf("error inserting new movie: %v", err)
}
return &movie, nil
}
- Paste the following code snippet in the Movies function. The function retrieves a collection of movies from the Postgres database based on the defined movie model.
// schema.resolvers.go
func (r *queryResolver) Movies(ctx context.Context) ([]*model.Movie, error) {
var movies []*model.Movie
err := r.DB.Model(&movies).Select()
if err != nil {
return nil, err
}
return movies, nil
}
Now that we have successfully connected our Postgres database and set up our resolvers, we can test the project.
Step 8: Test Your Golang GraphQL API
In our final step, we will be testing our Golang GraphQL API’s query and mutation.
- Start your Golang server
go run ./server.go
and head over to your browser, then navigate to http://localhost:8080 to access the in-built GraphQL playground. - To test for Mutations, paste the GraphQL Query below into the playground editor to insert a new movie object.
mutation createMovie {
createMovie(
input: {
title: "Rise of GraphQL Warrior Pt1"
url: "https://riseofgraphqlwarriorpt1.com/"
}
){
id
}
}
- To test Queries, paste the GraphQL Query below into the playground editor to fetch all created movie records.
query getMovies {
movies {
title
url
releaseDate
}
}
Get Instant GraphQL APIs on Your Databases with Hasura
As you can see, building a GraphQL API with Golang is a simple process, but it can be made even easier with Hasura. Setting up a GraphQL API could be as easy as clicking a button. Hasura gives you instant GraphQL APIs on new and existing data sources. Connect Hasura to your data source and get APIs in under a minute.
With built-in support for caching, authentication, and role-based permission down to the column level, Hasura speeds up the time required to create and configure GraphQL APIs, allowing you to focus on the things that matter.