A flexible and type-safe GraphQL schema builder for Go that automatically generates GraphQL schemas from Go types and functions. It nicely stands on the shoulders of github.com/graphql-go/graphql, a battle-tested package widely used in production environments.
GraphQL in Go can be frustrating. If you've tried gqlgen, you might have been overwhelmed by the code generation, hidden complexity, and unnecessary bloat. On the other hand, graphql-go/graphql offers a more manual approach but comes with verbosity and an unintuitive structure that makes schema definition cumbersome. Gql is here to solve that problem by providing a flexible, type-safe, and intuitive way to build GraphQL schemas directly from Go types and functions—without code generation or unnecessary boilerplate.
- Type-Safe: Uses Go types to define your GraphQL schema effortlessly.
- Automatic Schema Generation: No need to manually define GraphQL types; just annotate your Go structs.
- Built on
graphql-go/graphql: Leverages an established GraphQL implementation for Go. - Flexible and Extensible: Supports custom resolvers, input types, and mutations.
- Flexible Resolver Signature: Resolver methods can accept
context.Context,graphql.ResolveInfo, and input structs in any order and combination. - GQL Tags for Struct Fields: Uses struct tags to define GraphQL fields and arguments easily.
go get github.com/kadirpekel/gqlHere's a simple example of how to define and use a GraphQL schema with gql:
package main
import (
"context"
"fmt"
"github.com/graphql-go/graphql"
"github.com/kadirpekel/gql"
)
// Struct fields are annotated with gql tags to define GraphQL schema
// The tag format is `gql:"fieldName[,modifier]"`
// Example: `gql:"ID,nonNull"` ensures the field is required in GraphQL
type User struct {
ID string `gql:"ID"`
FirstName string `gql:"firstName"`
LastName string `gql:"lastName"`
}
func (u *User) FullName() (string, error) {
return fmt.Sprintf("%s %s", u.FirstName, u.LastName), nil
}
type UserInput struct {
ID string `gql:"ID,nonNull"`
}
type query struct{}
func (q query) GetUser(ctx context.Context, args UserInput, info graphql.ResolveInfo) (*User, error) {
return &User{ID: args.ID, FirstName: "John", LastName: "Doe"}, nil
}
func (q query) ListUsers(info graphql.ResolveInfo) ([]*User, error) {
return []*User{
{ID: "1", FirstName: "John", LastName: "Doe"},
{ID: "2", FirstName: "Jane", LastName: "Doe"},
}, nil
}
func main() {
schema, err := gql.NewSchemaBuilder().
WithQuery(query{}).
BuildSchema()
if err != nil {
panic(err)
}
// Use the schema with your GraphQL server
}The gql struct tags define GraphQL schema properties directly on Go structs:
- Basic Mapping:
gql:"fieldName"maps the Go struct field to a GraphQL field. - Modifiers: Add modifiers such as
nonNullfor required fields. - Example Usage:
type User struct {
ID string `gql:"ID,nonNull"` // Required GraphQL ID field
Name string `gql:"name"` // Maps to GraphQL "name" field
}Resolvers in gql are flexible and can accept parameters in any order:
context.Context: Allows passing request-scoped values like authentication data.graphql.ResolveInfo: Provides details about the query execution.- Input structs: Used for passing arguments to the resolver.
For example, all of the following resolver signatures are valid:
func (q query) GetUser(ctx context.Context, args UserInput) (*User, error) {}
func (q query) GetUser(info graphql.ResolveInfo, ctx context.Context, args UserInput) (*User, error) {}
func (q query) GetUser(args UserInput) (*User, error) {}You can also define mutations using the same approach:
type mutation struct{}
func (m mutation) CreateUser(ctx context.Context, args User) (*User, error) {
return &User{ID: "3", FirstName: args.FirstName, LastName: args.LastName}, nil
}Then include it in your schema:
schema, err := gql.NewSchemaBuilder().
WithQuery(query{}).
WithMutation(mutation{}).
BuildSchema()To integrate with a GraphQL server, use github.com/graphql-go/handler:
import (
"net/http"
"github.com/graphql-go/handler"
)
func main() {
schema, err := gql.NewSchemaBuilder().WithQuery(query{}).BuildSchema()
if err != nil {
panic(err)
}
h := handler.New(&handler.Config{
Schema: schema,
GraphiQL: true,
})
http.Handle("/graphql", h)
http.ListenAndServe(":8080", nil)
}This project is licensed under the MIT License.
