structql

Generate type-safe Go code for hasura/go-graphql-client from GraphQL schema and query files.
Features
- Generates Go structs with correct
graphql:"..." struct tags for hasura/go-graphql-client
- Produces compilable,
gofmt'd, goimports'd Go code
- Handles queries, mutations, and subscriptions
- Supports inline fragments for interfaces and unions
- Generates enum types with constants and input type structs
- Configurable custom scalar type mappings
- Automatic cleanup of obsolete generated files when schema/queries change
- Recursive query file discovery
- Golden file test infrastructure for codegen correctness
Installation
go install github.com/nseba/structql/cmd/structql@latest
Or download a binary from the releases page.
Quick Start
# Initialize a config file
structql init
# Edit structql.yaml to point to your schema and queries
# Then generate Go code
structql generate
Configuration
structql uses a YAML configuration file (structql.yaml):
# GraphQL schema file(s) - glob patterns supported
schema:
- "schema/*.graphql"
# Directory containing .graphql query/mutation/subscription files (recursive)
queries: "queries/"
# Output directory for generated Go files
output: "generated/"
# Go package name for generated files
package: "generated"
# Custom scalar type mappings (optional)
scalars:
DateTime:
type: "time.Time"
import: "time"
JSON:
type: "json.RawMessage"
import: "encoding/json"
UUID:
type: "string"
# Override default GraphQL-to-Go type mappings (optional)
type_mappings:
ID: "string" # default is graphql.ID
# Prefix for generated type names (optional)
# prefix: "GQL"
Usage with go generate
Add a generate.go file to your project:
//go:generate structql generate --config structql.yaml
package mypackage
Then run:
go generate ./...
Example
Given this schema:
type Query {
hero(episode: Episode!): Character
}
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
interface Character {
name: String!
friends: [Character]
}
type Human implements Character {
name: String!
friends: [Character]
homePlanet: String
}
type Droid implements Character {
name: String!
friends: [Character]
primaryFunction: String
}
And this query file:
query HeroQuery($episode: Episode!) {
hero(episode: $episode) {
name
friends {
name
}
... on Droid {
primaryFunction
}
... on Human {
homePlanet
}
}
}
structql generates:
// Code generated by structql. DO NOT EDIT.
package generated
// HeroQuery represents the GraphQL query HeroQuery.
type HeroQuery struct {
Hero *struct {
Name string
Friends *[]struct {
Name string
}
Droid struct {
PrimaryFunction *string
} `graphql:"... on Droid"`
Human struct {
HomePlanet *string
} `graphql:"... on Human"`
} `graphql:"hero(episode: $episode)"`
}
// HeroQueryVariables returns the variables for HeroQuery.
func HeroQueryVariables(episode Episode) map[string]interface{} {
return map[string]interface{}{
"episode": episode,
}
}
And a shared types.go with enums and input types:
// Code generated by structql. DO NOT EDIT.
package generated
// Episode represents the GraphQL enum Episode.
type Episode string
const (
EpisodeNewhope Episode = "NEWHOPE"
EpisodeEmpire Episode = "EMPIRE"
EpisodeJedi Episode = "JEDI"
)
Use the generated code with hasura/go-graphql-client:
client := graphql.NewClient("https://api.example.com/graphql", nil)
var q generated.HeroQuery
vars := generated.HeroQueryVariables(generated.EpisodeJedi)
err := client.Query(context.Background(), &q, vars)
Generated File Management
structql tracks generated files by their header comment (// Code generated by structql. DO NOT EDIT.). When you re-run structql generate:
- New files are created for new operations
- Existing files are updated for changed operations
- Obsolete generated files are automatically deleted (e.g., if you remove a query file)
- Files without the generated header are never touched
Type Mappings
| GraphQL |
Go (struct field) |
Go (variable) |
String / String! |
*string / string |
graphql.String |
Int / Int! |
*int / int |
graphql.Int |
Float / Float! |
*float64 / float64 |
graphql.Float |
Boolean / Boolean! |
*bool / bool |
graphql.Boolean |
ID / ID! |
*graphql.ID / graphql.ID |
graphql.ID |
[T!]! |
[]T |
- |
[T]! |
[]*T |
- |
[T!] |
*[]T |
- |
[T] |
*[]*T |
- |
| Enums |
TypeName (string alias) |
TypeName |
| Input types |
TypeName (struct) |
TypeName |
| Custom scalars |
Configurable via scalars |
Configurable |
CLI Reference
structql generate [--config structql.yaml] # Generate Go code
structql init # Create default config file
structql version # Print version
Development
make build # Build binary
make test # Run tests with race detector
make test-update # Update golden files
make lint # Run golangci-lint
make all # Run vet + lint + test + build
License
MIT License - see LICENSE for details.
Author
Sebastian Negomireanu