Introduction to GraphQL and Go
GraphQL is a query language for APIs developed by Facebook. Unlike REST, where each endpoint returns a fixed data structure, GraphQL allows the client to request only the fields it needs. This reduces the number of requests and the amount of data transferred. Go (Golang) is an excellent choice for building high-performance APIs due to its speed, static typing, and built-in concurrency support.
In this tutorial, we will create a simple GraphQL API in Go for managing a list of books. You will learn how to set up a server, define a schema, write resolvers, and execute queries and mutations.
Project setup and library installation
Initializing a Go module
Create a new directory for the project and initialize the module:
mkdir graphql-go-tutorialcd graphql-go-tutorialgo mod init graphql-go-tutorialInstalling the graphql-go library
We will use the popular graphql-go/graphql library. Install it using the command:
go get github.com/graphql-go/graphqlgo get github.com/graphql-go/handlerThe first library provides the GraphQL core, and the second provides an HTTP handler for integration with the web server.
Creating the data structure and GraphQL schema
Defining the Book type
Create a file main.go and start by defining the book data structure and the corresponding GraphQL type:
package main
import ( "github.com/graphql-go/graphql" "github.com/graphql-go/handler" "net/http")
// Book — book data structuretype Book struct { ID string `json:"id"` Title string `json:"title"` Author string `json:"author"` Year int `json:"year"`}
// Define GraphQL type for bookvar bookType = graphql.NewObject( graphql.ObjectConfig{ Name: "Book", Fields: graphql.Fields{ "id": &graphql.Field{Type: graphql.String}, "title": &graphql.Field{Type: graphql.String}, "author": &graphql.Field{Type: graphql.String}, "year": &graphql.Field{Type: graphql.Int}, }, },)Creating the root query (Query)
Let's add an in-memory book storage and a root query for retrieving the list and a single book:
// Simulated databasevar books = []Book{ {ID: "1", Title: "War and Peace", Author: "Leo Tolstoy", Year: 1869}, {ID: "2", Title: "Crime and Punishment", Author: "Fyodor Dostoevsky", Year: 1866}, {ID: "3", Title: "The Master and Margarita", Author: "Mikhail Bulgakov", Year: 1967},}
// Root queryvar queryType = graphql.NewObject( graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ "books": &graphql.Field{ Type: graphql.NewList(bookType), Resolve: func(p graphql.ResolveParams) (interface{}, error) { return books, nil }, }, "book": &graphql.Field{ Type: bookType, Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{Type: graphql.String}, }, Resolve: func(p graphql.ResolveParams) (interface{}, error) { id, _ := p.Args["id"].(string) for _, book := range books { if book.ID == id { return book, nil } } return nil, nil }, }, }, },)Adding mutations (Mutation)
Mutations allow data modification. Let's add the ability to add new books:
// Mutation for adding a bookvar mutationType = graphql.NewObject(