GraphQL API with Go: Step-by-Step Practical Tutorial

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why GraphQL and Go are the Perfect Pair

\n

In modern web development, GraphQL has become the standard for creating flexible and efficient APIs. Combined with the Go language (Golang), known for its performance and simplicity, you get a powerful tool for backend development. In this step-by-step tutorial, we will build a full-fledged GraphQL API in Go from scratch using the popular gqlgen library.

\n

You will learn how to set up a project, define a schema, implement resolvers, and add middleware for authentication. By the end of the article, you will have a working API ready for integration with any frontend.

\n\n

1. Project Setup and Dependency Installation

\n

First, let's create a new Go module and install the necessary packages. Make sure you have Go version 1.18 or higher installed.

\n
mkdir graphql-go-tutorial\ncd graphql-go-tutorial\ngo mod init graphql-go-tutorial\ngo get github.com/99designs/gqlgen@latest\ngo get github.com/99designs/gqlgen/graphql/introspection@latest
\n

Now let's initialize gqlgen in the project. This command will create a basic file structure:

\n
go run github.com/99designs/gqlgen init
\n

After execution, you will see the following structure:

\n
    \n
  • graph/ — directory with schema and resolver files
  • \n
  • graph/schema.graphqls — GraphQL schema definition
  • \n
  • graph/resolver.go — resolver stubs
  • \n
  • server.go — application entry point
  • \n
  • gqlgen.yml — generator configuration
  • \n
\n\n

2. Defining the GraphQL Schema

\n

Open the graph/schema.graphqls file and define our data model. For this example, let's create a simple Task Manager system:

\n
type Task {\n  id: ID!\n  title: String!\n  description: String\n  status: TaskStatus!\n  createdAt: Time!\n}\n\nenum TaskStatus {\n  TODO\n  IN_PROGRESS\n  DONE\n}\n\ntype Query {\n  tasks(status: TaskStatus): [Task!]!\n  task(id: ID!): Task\n}\n\ntype Mutation {\n  createTask(title: String!, description: String): Task!\n  updateTask(id: ID!, title: String, description: String, status: TaskStatus): Task!\n  deleteTask(id: ID!): Boolean!\n}\n\nscalar Time
\n

After saving the schema, run the code generation:

\n
go run github.com/99designs/gqlgen generate
\n

This command will create the graph/model/models_gen.go file with Go structures and update the resolvers.

\n\n

3. Implementing Resolvers and Business Logic

\n

Now let's implement the logic in the graph/schema.resolvers.go file. First, create a simple in-memory store:

\n
// In graph/resolver.go add the store\ntype Resolver struct {\n  tasks []*model.Task\n  mu    sync.Mutex\n}\n\nfunc NewResolver() *Resolver {\n  return &Resolver{\n    tasks: make([]*model.Task, 0),\n  }\n}
\n

Now implement the resolvers for Query and Mutation:

\n
// Query.Task\nfunc (r *queryResolver) Task(ctx context.Context, id string) (*model.Task, error) {\n  r.mu.Lock()\n  defer r.mu.Unlock()\n  \n  for _, task := range r.tasks {\n    if task.ID == id {\n      return task, nil\n    }\n  }\n  return nil, fmt.Errorf("task not found")\n}\n\n// Mutation.CreateTask\nfunc (r *mutationResolver) CreateTask(ctx context.Context, title string, description *string) (*model.Task, error) {\n  r.mu.Lock()\n  defer r.mu.Unlock()\n  \n  task := &model.Task{\n    ID:          uuid.New().String(),\n    Title:       title,\n    Description: description,\n    Status:      model.TaskStatusTodo,\n    CreatedAt:   time.Now(),\n  }\n  \n  r.tasks = append(r.tasks, task)\n  return task, nil\n}\n\n// Mutation.UpdateTask\nfunc (r *mutationResolver) UpdateTask(ctx context.Context, id string, title *string, description *string, status *model.TaskStatus) (*model.Task, error) {\n  r.mu.Lock()\n  defer r.mu.Unlock()\n  \n  for _, task                

Blogs

Book Recommendations