Introduction
GraphQL is a modern query language for APIs, developed by Facebook. Unlike REST, it allows the client to request only the necessary data, which reduces network load and speeds up development. In this tutorial, we will create a full-fledged GraphQL API in Go (Golang) using the popular gqlgen library. You will learn how to set up a server, define a schema, write resolvers, and execute queries. The article is aimed at developers familiar with Go basics, but even beginners can follow the steps thanks to detailed comments.
1. Project Setup and Dependency Installation
First, let's create a new Go project and install the necessary packages.
Step 1: Module Initialization
mkdir graphql-go-tutorialcd graphql-go-tutorialgo mod init graphql-go-tutorialStep 2: Installing gqlgen
gqlgen is a GraphQL code generator for Go. It automatically creates resolvers based on the schema.
go get github.com/99designs/gqlgen@latestStep 3: Generating the Project Skeleton
Run the initialization command:
go run github.com/99designs/gqlgen initAfter execution, you will see the structure:
graph/— folder with schema and resolversserver.go— entry pointgqlgen.yml— generator configuration
2. Defining the GraphQL Schema
The schema describes the data types and API operations. Open the file graph/schema.graphqls and replace its contents with the following:
type User { id: ID! name: String! email: String! age: Int!}
type Query { users: [User!]! user(id: ID!): User}
type Mutation { createUser(name: String!, email: String!, age: Int!): User! updateUser(id: ID!, name: String, email: String, age: Int): User deleteUser(id: ID!): Boolean}Here we defined:
- User — an object with fields id, name, email, age.
- Query — read operations (get all users or one by ID).
- Mutation — write operations (create, update, delete).
After saving, run the code generation:
go run github.com/99designs/gqlgen generateThis will create the files models_gen.go (Go types) and generated.go (resolver interfaces).
3. Implementing Resolvers and Data Storage
Now we need to write the logic for handling requests. For simplicity, we will store data in memory (a slice of structs).
Step 1: Create the Storage
Create the file graph/store.go:
package graph
import "sync"
type Store struct { mu sync.RWMutex users []User nextID int}
func NewStore() *Store { return &Store{ users: []User{ {ID: "1", Name: "Alice", Email: "alice@example.com", Age: 30}, {ID: "2", Name: "Bob", Email: "bob@example.com", Age: 25}, }, nextID: 3, }}
func (s *Store) GetUsers() []User { s.mu.RLock() defer s.mu.RUnlock() return s.users}
func (s *Store) GetUser(id string) *User { s.mu.RLock() defer s.mu.RUnlock() for _, u := range s.users { if u.ID == id { return &u } } return nil}
func (s *Store) CreateUser(name, email string, age int) User { s.mu.Lock() defer s.mu.Unlock() user := User{ ID: fmt.Sprintf("%d", s.nextID), Name: name, Email: email, Age: age, } s.nextID++ s.users = append(s.users, user) return user}
func (s *Store) UpdateUser(id string, name, email *string, age *int) *User { s.mu.Lock() defer s.mu.Unlock() for i, u := range s.users { if u.ID == id { if name != nil { s.users[i].Name = *name } if email