Queries and mutations
Build a small GraphQL API with multiple entities - queries, mutations, and clean file layout.
Use this guide after Define your schema when one root type is no longer enough. It shows how to split a growing schema by domain while keeping the public GraphQL API easy to find.
This guide builds a small server with a User and a Post entity, each
with its own query and mutation surface, composed into the root types via
embedding.
If you think in GraphQL SDL, you are building two object types plus Query and Mutation roots; in grx that is the same shape, but each entity’s resolvers live in one Go file and get composed into the roots with embedding instead of a central “register everything” list.
hello-grx/
├── go.mod
├── main.go
└── graph/
├── schema.go # composes the root Query and Mutation types
├── user.go # User type, UserQuery, UserMutation
└── post.go # Post type, PostQuery, PostMutationPer-entity files
Each entity owns its types, inputs, payloads, and resolvers in a single file. This keeps the diff for "add a new entity" to one new file plus one embedded field per applicable root.
// graph/user.go
package graph
import "context"
type User struct {
ID string `gql:"id,nonNull"`
Name string `gql:"name,nonNull"`
Email *string `gql:"email"`
}
type UserArgs struct {
ID string `gql:"id,nonNull"`
}
type UserCreateInput struct {
Name string `gql:"name,nonNull"`
Email *string `gql:"email"`
}
type UserCreateArgs struct {
Input UserCreateInput `gql:"input,nonNull"`
}
type UserCreatePayload struct {
User *User `gql:"user,nonNull"`
}
type UserQuery struct{}
func (UserQuery) User(ctx context.Context, args UserArgs) (*User, error) {
email := "ada@example.com"
return &User{ID: args.ID, Name: "Ada Lovelace", Email: &email}, nil
}
type UserMutation struct{}
func (UserMutation) CreateUser(ctx context.Context, args UserCreateArgs) (*UserCreatePayload, error) {
user := &User{
ID: "user_1",
Name: args.Input.Name,
Email: args.Input.Email,
}
return &UserCreatePayload{User: user}, nil
}// graph/post.go
package graph
import "context"
type Post struct {
ID string `gql:"id,nonNull"`
Title string `gql:"title,nonNull"`
Body string `gql:"body,nonNull"`
Author *User `gql:"author,nonNull"`
}
type PostArgs struct {
ID string `gql:"id,nonNull"`
}
type PostCreateInput struct {
Title string `gql:"title,nonNull"`
Body string `gql:"body,nonNull"`
AuthorID string `gql:"authorId,nonNull"`
}
type PostCreateArgs struct {
Input PostCreateInput `gql:"input,nonNull"`
}
type PostCreatePayload struct {
Post *Post `gql:"post,nonNull"`
}
type PostQuery struct{}
func (PostQuery) Post(ctx context.Context, args PostArgs) (*Post, error) {
return &Post{
ID: args.ID,
Title: "Hello, grx",
Body: "Composing root types from per-entity resolver structs.",
Author: &User{ID: "user_1", Name: "Ada Lovelace"},
}, nil
}
type PostMutation struct{}
func (PostMutation) CreatePost(ctx context.Context, args PostCreateArgs) (*PostCreatePayload, error) {
return &PostCreatePayload{
Post: &Post{
ID: "post_1",
Title: args.Input.Title,
Body: args.Input.Body,
Author: &User{ID: args.Input.AuthorID, Name: "Ada Lovelace"},
},
}, nil
}Compose the roots
// graph/schema.go
package graph
import "github.com/grx-gql/grx/schema"
type Query struct {
UserQuery
PostQuery
}
type Mutation struct {
UserMutation
PostMutation
}
func Schema() schema.Config {
return schema.Config{
Query: Query{},
Mutation: Mutation{},
}
}Wire up the server
// main.go
package main
import (
"log"
"net/http"
"example.com/hello-grx/graph"
"github.com/grx-gql/grx"
)
func main() {
srv, err := grx.NewServer(
grx.WithSchema(graph.Schema()),
grx.WithPlaygroundPath("/"),
)
if err != nil {
log.Fatal(err)
}
log.Println("listening on http://localhost:4000")
log.Fatal(http.ListenAndServe(":4000", srv))
}Try it
go run .Open the playground at http://localhost:4000 and run:
mutation {
createUser(input: { name: "Ada", email: "ada@example.com" }) {
user { id name email }
}
}
query {
user(id: "1") { id name email }
post(id: "1") { id title author { id name } }
}Adding a new entity
- Create
graph/<entity>.gowith the entity struct, input/payload structs,XQuery, andXMutation.
See also
- Testing with the HTTP client -
httptest.Serverplusclientfor integration tests against your handler.
Last updated on