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

20 Managing Queries with Parameters in graphql-go

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

title: 20 Managing Queries with Parameters in graphql-go date: 2024-06-26 author: Faris Azhar tags: [Go, GraphQL, Back-End, API, Tutorial]

GraphQL offers remarkable flexibility for building modern APIs—including in how you manage queries with parameters. For backend engineers working in Go, the graphql-go package is one of the go-to choices. However, managing parameters in GraphQL queries can sometimes become a stumbling block of its own, whether in terms of schema design or resolver implementation.

In this article, I’ll thoroughly cover how to manage queries with parameters using graphql-go, complete with 20 best practices and essential tricks. We’ll discuss everything conceptually, through code practice, all the way to real-world examples involving filtering, pagination, validation, and error handling.


1. Defining Parameters in the Schema

The first step is understanding where parameters belong in a GraphQL schema. Parameters are attached as arguments on a field:

go
 1userType := graphql.NewObject(graphql.ObjectConfig{
 2    Name: "User",
 3    Fields: graphql.Fields{
 4        "id": &graphql.Field{Type: graphql.String},
 5        "name": &graphql.Field{Type: graphql.String},
 6    },
 7})
 8
 9queryType := graphql.NewObject(graphql.ObjectConfig{
10    Name: "Query",
11    Fields: graphql.Fields{
12        "user": &graphql.Field{
13            Type: userType,
14            Args: graphql.FieldConfigArgument{
15                "id": &graphql.ArgumentConfig{
16                    Type: graphql.NewNonNull(graphql.String),
17                },
18            },
19            Resolve: userResolver,
20        },
21    },
22})
With the schema above, a client can run a query like:

graphql
1{
2  user(id: "1") {
3    name
4  }
5}

2. Using a Default Value on an Argument

An argument can be given a default value, so the client doesn’t always have to provide it:

go
1"status": &graphql.ArgumentConfig{
2    Type:         graphql.String,
3    DefaultValue: "active",
4},

3. Defining Complex Arguments (Input Object)

For more complex parameters, use an Object Input Type:

go
 1userFilterInput := graphql.NewInputObject(graphql.InputObjectConfig{
 2    Name: "UserFilter",
 3    Fields: graphql.InputObjectConfigFieldMap{
 4        "name": &graphql.InputObjectFieldConfig{Type: graphql.String},
 5        "age":  &graphql.InputObjectFieldConfig{Type: graphql.Int},
 6    },
 7})
 8
 9"users": &graphql.Field{
10    Type: graphql.NewList(userType),
11    Args: graphql.FieldConfigArgument{
12        "filter": &graphql.ArgumentConfig{Type: userFilterInput},
13    },
14    Resolve: usersResolver,
15}

4. Reading Parameters in the Resolver

Arguments are obtained through params.Args inside the resolver:

go
1func userResolver(p graphql.ResolveParams) (interface{}, error) {
2    id, ok := p.Args["id"].(string)
3    if !ok {
4        return nil, errors.New("ID is required")
5    }
6    // Query the database
7}

5. Validating Arguments

Always validate before passing a parameter on to the next step in the process.

go
1if len(id) != 36 {
2    return nil, errors.New("Invalid ID format")
3}

6. Optional Parameters and Nullables

A common mistake happens when an optional parameter’s value isn’t checked (whether or not it exists in params.Args).

go
1var name string
2if n, exists := p.Args["name"]; exists {
3    name = n.(string)
4}

7. Multi-Field Filtering

Allow filtering with an array/list on an argument.

go
1"tags": &graphql.ArgumentConfig{
2    Type: graphql.NewList(graphql.String),
3},

8. Implementing Pagination

Pagination is very common. Make use of the limit and offset parameters:

go
1"users": &graphql.Field{
2    Type: graphql.NewList(userType),
3    Args: graphql.FieldConfigArgument{
4        "limit":  &graphql.ArgumentConfig{Type: graphql.Int, DefaultValue: 10},
5        "offset": &graphql.ArgumentConfig{Type: graphql.Int, DefaultValue: 0},
6    },
7    Resolve: usersResolver,
8}

9. Enum Parameters

Define an Enum type so the caller can only choose valid values.

go
1statusEnum := graphql.NewEnum(graphql.EnumConfig{
2    Name: "Status",
3    Values: graphql.EnumValueConfigMap{
4        "ACTIVE": &graphql.EnumValueConfig{Value: "active"},
5        "INACTIVE": &graphql.EnumValueConfig{Value: "inactive"},
6    },
7})

10. Nested Parameters

Create an input type within an input type for nested queries, for example a filter inside a filter.


11. Array Parameters

An argument of the list type, for example multiple IDs.

go
1"ids": &graphql.ArgumentConfig{
2    Type: graphql.NewList(graphql.String),
3}

12. Input Validation in the Resolver

In addition to the schema, add validation in the resolver. For example, checking uniqueness, foreign key relationships, and so on.


13. Simulating Queries with Parameters

Let’s compare queries with and without parameters:

QueryResponse
users(limit: 2)[{"id":"1"}, {"id":"2"}]
user(id: "xyz"){"id": "xyz", "name": "Dewi"}
users(filter: {...})[{"id":"a"}, {"id":"b"}]

14. Error Handling

Always return a “clear” error in the resolver when validation fails.


15. Query Variables

Use query variables instead of hard-coding parameters in the client:

graphql
1query getUser($id: String!) {
2  user(id: $id) {
3    name
4  }
5}

16. Documenting Schema Parameters

Make use of descriptions in the schema, so frontend developers can easily understand which arguments are available.


17. Dynamic Query Parameters

When a parameter can be one of many fields (for example, filtering by email or username):

go
1Args: graphql.FieldConfigArgument{
2    "email":    &graphql.ArgumentConfig{Type: graphql.String},
3    "username": &graphql.ArgumentConfig{Type: graphql.String},
4}

In the resolver, check which argument is filled in.


18. Process Flow Diagram for Parameterized Queries

For a clearer picture, here is the resolver flow in a query with parameters in graphql-go:

MERMAID
graph TD
    A[Klien Mengirim Query dgn Parameter] --> B[GraphQL Handler Receive]
    B --> C[Schema Validate Param]
    C --> D[Resolver Ambil Params]
    D --> E[Validasi & Filtering di Go]
    E --> F[Query ke DB]
    F --> G[Return Response]

19. Limiting Response Fields with Parameters

You can also limit which fields are queried using a parameter, similar to projection in SQL/MongoDB.


20. Separation of Concerns

Separate your GraphQL handler logic, parameter validation, data access, and error handling within your Go code.


Conclusion

Managing queries with parameters in graphql-go is fundamental if you want to build robust, scalable, and maintainable GraphQL APIs in Go. To wrap up, here is a summary table of best practices:

NoBest PracticeImplementation Example
1Parameters in the SchemaArgs: FieldConfigArgument{...}
4Params in the Resolverp.Args["param_name"]
8Paginationlimit, offset args & validate in Go
13Simulating QueriesCheck different results based on arguments
20Separation of ConcernsSeparate handler, validation, data, error

Start optimizing your implementation, even for queries that may look trivial. Security, efficiency, and ease of debugging are worthwhile investments for your next Go project!

Have other tips that weren’t covered here? Drop them in the comments—let’s discuss! 🚀

Related Articles

💬 Comments