Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
13 Jul 2025 · 6 min read ·Article 13 / 125
Go

13 Building Your First Resolver in graphql-go

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

13 Building Your First Resolver in graphql-go: A Complete Guide for Beginners

If you want to build a modern API that’s flexible and efficient, GraphQL has become almost mandatory in today’s developer toolset. Unlike REST, GraphQL lets the client decide exactly which data it wants to fetch in a single request. One of the most popular libraries in the Go ecosystem is graphql-go , which offers a straightforward way to build a GraphQL API in Go.

In this article, we’ll walk through the process of building your first resolver in graphql-go step by step. We’ll explain the concept, the basic implementation, how to add mock data, and break down the code structure and execution flow, complete with code examples, simulations, a flow diagram, and a table of query results.

Let’s get started!


What Is a Resolver in GraphQL?

In GraphQL, a resolver is a function responsible for processing and returning data for each field in the schema. Every field on Query, Mutation, or any other type is executed using one of these resolver functions.

A Simple Analogy

If the schema is the “structure of the question,” then the resolver is the “way you answer” that question against a database, another API, or in-memory data.

Table: GraphQL Schema vs. Resolver

ComponentRoleExample
SchemaDefines the questiontype Query { user(id:ID):User }
ResolverAnswers the questionfunc (r *Resolver) User(…) …

Step 1: Set Up the Project

First, initialize a new Go project.

bash
1go mod init github.com/username/graphql-go-demo
2go get github.com/graph-gophers/graphql-go
3go get github.com/graph-gophers/graphql-go/relay

Step 2: Define the GraphQL Schema

Let’s create a schema.graphql file:

graphql
 1type Query {
 2  hello: String!
 3  user(id: ID!): User
 4}
 5
 6type User {
 7  id: ID!
 8  name: String!
 9  email: String!
10}

The schema above has:

  • A hello query that returns a string
  • A user query that accepts an id argument and returns a User with the fields id, name, and email.

Step 3: Mock Data Mode

Create some dummy (mock) data in a model.go file:

go
 1package main
 2
 3type User struct {
 4    ID    string
 5    Name  string
 6    Email string
 7}
 8
 9// Simulated dummy database
10var users = []*User{
11    {ID: "1", Name: "Alice", Email: "alice@example.com"},
12    {ID: "2", Name: "Bob", Email: "bob@example.com"},
13    {ID: "3", Name: "Charlie", Email: "charlie@example.com"},
14}

Step 4: Building the Resolver

Now for the interesting part: we’ll build a resolver that maps the GraphQL schema above to our Go data.

Create a resolver.go file:

go
 1package main
 2
 3import (
 4    "context"
 5)
 6
 7type Resolver struct{}
 8
 9func (r *Resolver) Hello(ctx context.Context) (string, error) {
10    return "Halo dari resolver pertama!", nil
11}
12
13func (r *Resolver) User(ctx context.Context, args struct{ ID string }) (*userResolver, error) {
14    for _, u := range users {
15        if u.ID == args.ID {
16            return &userResolver{u}, nil
17        }
18    }
19    return nil, nil // or you could use a custom error
20}
21
22type userResolver struct {
23    u *User
24}
25
26func (ur *userResolver) ID() string     { return ur.u.ID }
27func (ur *userResolver) Name() string   { return ur.u.Name }
28func (ur *userResolver) Email() string  { return ur.u.Email }

Notes:

  • Every method (for example Hello, User) MUST have a capitalized (exported) name, and its arguments must follow the order of context.Context first, then the schema arguments (if any).
  • For the User type, we create our own resolver (userResolver) because graphql-go requires a dedicated resolver struct for each type that exposes fields.

Step 5: Set Up the HTTP Handler

In main.go:

go
 1package main
 2
 3import (
 4    "io/ioutil"
 5    "log"
 6    "net/http"
 7
 8    "github.com/graph-gophers/graphql-go"
 9    "github.com/graph-gophers/graphql-go/relay"
10)
11
12func main() {
13    schemaData, err := ioutil.ReadFile("schema.graphql")
14    if err != nil {
15        log.Fatalf("failed to read schema: %v", err)
16    }
17
18    schema := graphql.MustParseSchema(string(schemaData), &Resolver{})
19
20    http.Handle("/graphql", &relay.Handler{Schema: schema})
21
22    log.Println("GraphQL server listening at http://localhost:8080/graphql")
23    log.Fatal(http.ListenAndServe(":8080", nil))
24}

Step 6: Your First Query to the Resolver

Run the server:

bash
1go run .

Try using a tool like GraphiQL , Insomnia, or curl:

hello query:

graphql
1query {
2  hello
3}

Response:

json
1{
2  "data": {
3    "hello": "Halo dari resolver pertama!"
4  }
5}

user query:

graphql
1query {
2  user(id: "2") {
3    id
4    name
5    email
6  }
7}

Response:

json
1{
2  "data": {
3    "user": {
4      "id": "2",
5      "name": "Bob",
6      "email": "bob@example.com"
7    }
8  }
9}


Resolver Execution Flow

Let’s visualize the flow to make it easier to understand:

MERMAID
flowchart TD
    Client -->|Query| GraphQLServer
    GraphQLServer -->|Parse Schema| ResolverFunctions
    ResolverFunctions -->|Ambil Data| DatabaseSimulasi
    ResolverFunctions -->|Return| GraphQLServer
    GraphQLServer -->|Response JSON| Client

Explanation:

  1. The client sends a query to the GraphQL endpoint.
  2. The server parses it and finds the resolver matching the field.
  3. The resolver runs its logic, pulling data from the (simulated) database.
  4. The data is returned in JSON format according to the client’s request.

Table: Simulated User Queries

QueryArgumentResult
hello-“Halo dari resolver pertama!”
user(id: "1")id = “1”{“id”:“1”, “name”:“Alice”, …}
user(id: "3")id = “3”{“id”:“3”, “name”:“Charlie”, …}
user(id: "999")id = “999”null

graphql-go Resolver Best Practices

  1. Separate the schema, resolver, and model. This keeps the code clean and easy to maintain.
  2. Use context to propagate auth/logging data, for example injecting the user ID or trace ID from the headers into the resolver.
  3. Test your resolvers with unit tests using mock data to make sure the output matches expectations.
  4. Data mutations (such as create/update/delete) should use a Mutation; the resolver pattern is similar to a Query.
  5. Error handling: return descriptive errors, for example when a user is not found, and surface them to the client through the errors field in the payload.

Conclusion

Building a resolver in graphql-go is very straightforward and follows idiomatic Go principles. Once you understand resolvers, you can easily control how data is processed, fetched, and returned according to the client’s request. This resolver model is also very easy to extend into something more complex: for example, fetching data asynchronously, aggregating data across services, or validating user access.

With this article, you’ve learned how to:

  • Write a GraphQL schema and map it to Go resolvers
  • Create mock data and run your first query against a GraphQL endpoint
  • Understand the resolver execution flow visually
  • Apply basic best practices when developing a GraphQL API

Have fun building your first resolver in graphql-go! If you have any questions or suggestions for follow-up topics, don’t hesitate to ask in the comments. 🚀


References:


Happy coding! 👨‍💻👩‍💻

Related Articles

💬 Comments