111 Adding a Mutation to the gqlgen Schema
111 Adding a Mutation to the gqlgen Schema
One of GraphQL’s great strengths lies in its flexibility and consistency when interacting with an API. In the Go backend world, gqlgen is a go-to framework that makes it easy for developers to build GraphQL schemas that are type-safe, fast, and easy to maintain. In this article, we’ll break down the process of adding a Mutation to a gqlgen schema, using a simple case study, sample code, and easy-to-follow explanations of the concepts.
What Is a Mutation in GraphQL?
Before diving into the code, it’s a good idea to understand this first: a mutation is the operation in GraphQL used to modify data (Create, Update, Delete), as opposed to a query, which is used only to read data.
Think of a mutation as the POST, PUT, PATCH, and DELETE of REST—but with the power and flexibility of GraphQL queries.
A Quick Look at gqlgen
gqlgen is a Go library that leverages generated code (code-first), ensuring that the GraphQL schema always stays in sync with your Go types. With just a little boilerplate, you can have a cutting-edge GraphQL API.
Case Study
Suppose we have a simple book catalog application. We want to be able to add a new book to the catalog through a GraphQL mutation.
Project Structure
Let’s assume the directories and files are laid out as follows (a standard gqlgen project):
1project/
2├── go.mod
3├── gqlgen.yml
4├── graph/
5│ ├── model/
6│ │ └── models_gen.go
7│ ├── schema.graphqls
8│ ├── resolver.go
9│ └── schema.resolvers.go
10└── main.go1. Defining the Mutation in the GraphQL Schema
The first step: open the schema.graphqls file. Here we’ll add a mutation for adding a book.
1type Book {
2 id: ID!
3 title: String!
4 author: String!
5}
6
7input NewBook {
8 title: String!
9 author: String!
10}
11
12type Mutation {
13 addBook(input: NewBook!): Book!
14}Explanation:
- Book: The primary data type
- NewBook: An input type—the data required to add a new book
- addBook: The mutation we’re going to implement
2. Generating Code Automatically
Whenever the schema changes (for example, when the mutation above is added), run:
1go run github.com/99designs/gqlgen generateThis command automatically:
- updates
models_gen.go(the Go structs derived from the GraphQL types), - updates the resolver interface in
resolver.goand reminds us to fill in the function bodies.
3. Implementing the Mutation Resolver
Now open schema.resolvers.go and find the skeleton of the AddBook function.
1// graph/schema.resolvers.go
2
3func (r *mutationResolver) AddBook(ctx context.Context, input model.NewBook) (*model.Book, error) {
4 // TODO: implementation, e.g. save to an in-memory slice
5}Here’s a simple example: store the data in a books slice held in the Resolver struct.
1// graph/model/models_gen.go (auto-generated)
2// type Book struct { ... }
3// type NewBook struct { ... }
4
5// graph/resolver.go
6
7type Resolver struct {
8 books []*model.Book
9}
10
11// graph/schema.resolvers.go
12
13func (r *mutationResolver) AddBook(ctx context.Context, input model.NewBook) (*model.Book, error) {
14 newBook := &model.Book{
15 ID: fmt.Sprintf("%d", len(r.books)+1),
16 Title: input.Title,
17 Author: input.Author,
18 }
19 r.books = append(r.books, newBook)
20 return newBook, nil
21}4. Simulating the GraphQL Mutation Query
Here’s an example mutation you’d typically run from the GraphQL playground or via curl:
1mutation {
2 addBook(input: { title: "Clean Code", author: "Robert C. Martin" }) {
3 id
4 title
5 author
6 }
7}And the response:
1{
2 "data": {
3 "addBook": {
4 "id": "1",
5 "title": "Clean Code",
6 "author": "Robert C. Martin"
7 }
8 }
9}5. Explaining the Workflow
To make things clearer, here’s a Mermaid diagram of the addBook mutation flow:
sequenceDiagram
participant Client
participant Server
participant Resolver
Client->>Server: send mutation addBook
Server->>Resolver: panggil AddBook resolver
Resolver-->>Server: return Book baru
Server-->>Client: return response Book baru
6. Process Summary Table
| Step | File | Explanation |
|---|---|---|
| 1. Define | schema.graphqls | Define the Mutation and Input |
| 2. Generate | go run ... generate | Generate models & resolver |
| 3. Implement | schema.resolvers.go | Fill the AddBook resolver with Go |
| 4. Query | Playground/Postman | Try the addBook mutation |
| 5. Testing | Automated/Manual | Confirm the result is as expected |
7. Tips & Best Practices
- Be Explicit About Data Types: Use input types so the schema stays clear and scalable.
- Error Handling: Always handle errors in the resolver.
- Persistence: For production, store data in a database, not just a slice.
- Authorization: Mutations often require stricter authentication.
- Scalability: Structure your codebase in a modular way so adding new mutations is easy.
8. Going Further: Middleware & Validation
A mutation is the initial foundation, but validation checks (for example, requiring a unique title) and the use of middleware such as authentication checks are essential for production.
For example, a simple validation in the resolver:
1for _, b := range r.books {
2 if b.Title == input.Title && b.Author == input.Author {
3 return nil, fmt.Errorf("book already exists")
4 }
5}Conclusion
With just a few simple steps, we’ve successfully added a mutation to a gqlgen schema. gqlgen is very powerful and has a friendly learning curve for Go developers. Mutations in GraphQL are highly flexible: whether you want to create, update, or delete, they can be tailored to your application’s needs at any time simply by modifying the schema and the resolver.
If you want a more advanced application, add a database layer, authentication, and more complete error handling. But the core mutation concept you learned today will remain consistent across a wide range of use cases.
Happy experimenting! 🚀
References:
If you enjoyed this article or want to dig deeper into GraphQL + Go, feel free to leave a comment or share! 👋