Two tools in one:
-
gqlcliCLI — A GraphQL client for querying any GraphQL API. Discover fields, execute queries and mutations, explore schemas—all from the command line. -
gqlclilibrary — Build GraphQL-backed CLI applications in Go. Write CLIs where GraphQL is the interface language, not subcommands and flags. Perfect for AI agents that can introspect schemas and construct queries.
go install github.com/wricardo/gqlcli/cmd/gqlcli@latest
# or clone and build
git clone https://github.com/wricardo/gqlcli.git
cd gqlcli && make install# Discover what queries are available
gqlcli queries
# Find mutations related to "campaign"
gqlcli mutations --filter campaign
# Execute a query
gqlcli query --query "{ users { id name } }"
# Try against a different server
export GRAPHQL_URL=https://api.example.com/graphql
gqlcli queries --filter user# List all Query fields with descriptions
gqlcli queries --desc
# Show mutation arguments and types
gqlcli mutations --args
# Explore schema in readable format
gqlcli introspect --format table
# Export schema as JSON
gqlcli introspect --format json > schema.json
# Execute a mutation with variables
gqlcli mutation \
--mutation "mutation CreateUser(\$input: CreateUserInput!) { createUser(input: \$input) { id } }" \
--input '{"name":"Alice","email":"alice@example.com"}'
# Use a query from a file
gqlcli query --query-file ./queries/getUser.graphql --variables '{"id":"123"}'gqlcli queries --filter user -f json-pretty # Pretty JSON
gqlcli queries --filter user -f table # Aligned columns
gqlcli queries --filter user -f toon # Token-optimized (default)
gqlcli queries --filter user -f llm # Markdown for LLMs
gqlcli queries --filter user -f compact # Minimal JSONquery— Execute GraphQL queries with variables and multiple input methodsmutation— Execute mutations with auto-wrapped input objectsintrospect— Download and explore full GraphQL schematypes— List all schema types with filteringqueries— Discover available Query fields instantlymutations— Discover available Mutation fields instantly
json/json-pretty— Pretty or compact JSONtable— Aligned columns for terminal viewingtoon— Token-optimized format (40-60% smaller) — defaultllm— Markdown-friendly for AI/LLM consumptioncompact— Minimal JSON (strips nulls)
- Default endpoint:
http://localhost:8080/graphql - Override via
--urlflag orGRAPHQL_URLenvironment variable - Per-directory config file:
.gqlcli.jsonwith named environments (local,prod,qa, …) - Switch environments at runtime with
--env prod - Bearer token authentication support
- Custom HTTP headers per environment
- Debug mode for request/response logging
- Inline:
--query "{ users { id } }" - From files:
--query-file queries/getUser.graphql - As arguments:
query "{ ... }" - Variables inline:
--variables '{"id":"123"}' - Variables from files:
--variables-file vars.json - Named operations in multi-operation files
# List all queries (TOON format — token-efficient)
gqlcli queries
# List with descriptions
gqlcli queries --desc
# Show arguments and types
gqlcli queries --args
# Filter by name
gqlcli queries --filter user
gqlcli mutations --filter campaign
# Different formats
gqlcli queries -f json-pretty
gqlcli mutations -f table# Simple query
gqlcli query --query "{ users { id name email } }"
# Query from file
gqlcli query --query-file ./queries/getUser.graphql
# With variables
gqlcli query \
--query "query GetUser(\$id: ID!) { user(id: \$id) { id name } }" \
--variables '{"id":"123"}'
# Variables from file
gqlcli query \
--query-file ./queries/getUser.graphql \
--variables-file ./variables.json
# Named operation (from multi-operation file)
gqlcli query \
--query-file ./queries/operations.graphql \
--operation "GetUser"# Basic mutation
gqlcli mutation \
--mutation "mutation { createUser(name: \"Alice\") { id } }"
# With auto-wrapped input
gqlcli mutation \
--mutation "mutation CreateUser(\$input: CreateUserInput!) { createUser(input: \$input) { id } }" \
--input '{"name":"Alice","email":"alice@example.com"}'
# Alternative: explicit variables
gqlcli mutation \
--mutation-file ./mutations/createUser.graphql \
--variables '{"input":{"name":"Alice"}}'# Full schema introspection
gqlcli introspect --format json-pretty > schema.json
# LLM-friendly schema
gqlcli introspect --format llm
# List all types
gqlcli types
# Filter types by name
gqlcli types --filter User
# Filter by kind
gqlcli types --kind OBJECT
gqlcli types --kind ENUM
gqlcli types --kind INPUT_OBJECT
# Compact output (good for piping)
gqlcli types -f compactexport GRAPHQL_URL="http://staging-api.example.com/graphql"
gqlcli queriesCreate .gqlcli.json in your project directory to define named environments:
{
"default": "local",
"environments": {
"local": {
"url": "http://localhost:8080/graphql",
"headers": {
"Authorization": "Bearer dev-token"
}
},
"staging": {
"url": "http://staging-api.example.com/graphql",
"headers": {
"Authorization": "Bearer staging-token",
"X-Tenant": "acme"
}
},
"prod": {
"url": "https://api.example.com/graphql",
"headers": {
"Authorization": "Bearer prod-token"
}
}
}
}# Uses "local" (the default)
gqlcli queries
# Switch to prod
gqlcli queries --env prod
gqlcli query --query "{ users { id } }" --env prod
# Override URL on top of a named env
gqlcli queries --env staging --url http://other-host/graphqlPriority (lowest → highest): hardcoded default → .gqlcli.json env → GRAPHQL_URL → --url flag
# Query result to file
gqlcli query --query "{ users { id } }" --output results.json
# Schema to file
gqlcli introspect --format json --output schema.json
# Types list to file
gqlcli types --output types.json-u, --url VALUE GraphQL endpoint (default: http://localhost:8080/graphql, env: GRAPHQL_URL)
--env VALUE Environment to use from .gqlcli.json (e.g. local, prod)
-f, --format VALUE Output format: json, json-pretty, table, compact, toon, llm (default: toon)
-p, --pretty Pretty print JSON output
-h, --help Show help
-q, --query STRING GraphQL query
--query-file PATH Read query from file
-v, --variables JSON Query variables as JSON
--variables-file PATH Read variables from file
-o, --operation STRING Named operation to execute
-f, --format FORMAT Output format
--output FILE Write to file
-u, --url URL GraphQL endpoint (env: GRAPHQL_URL)
--env VALUE Environment from .gqlcli.json
-d, --debug Enable HTTP debug logging
-m, --mutation STRING GraphQL mutation
--mutation-file PATH Read mutation from file
--input JSON Input object (auto-wrapped as {"input":{...}})
-v, --variables JSON Variables as JSON
--variables-file PATH Read variables from file
-o, --operation STRING Named operation
-f, --format FORMAT Output format
--output FILE Write to file
-u, --url URL GraphQL endpoint (env: GRAPHQL_URL)
--env VALUE Environment from .gqlcli.json
-d, --debug Enable HTTP debug logging
--desc Include field descriptions
--args Include field arguments with types
--filter PATTERN Filter by name (case-insensitive)
-f, --format FORMAT Output format (default: toon)
-u, --url URL GraphQL endpoint (env: GRAPHQL_URL)
--env VALUE Environment from .gqlcli.json
-d, --debug Enable debug logging
--desc Include field descriptions
--args Include field arguments with types
--filter PATTERN Filter by name (case-insensitive)
-f, --format FORMAT Output format (default: toon)
-u, --url URL GraphQL endpoint (env: GRAPHQL_URL)
--env VALUE Environment from .gqlcli.json
-d, --debug Enable debug logging
-f, --format FORMAT Output format (default: llm)
-o, --output FILE Write schema to file
-p, --pretty Pretty print JSON
-u, --url URL GraphQL endpoint (env: GRAPHQL_URL)
--env VALUE Environment from .gqlcli.json
-d, --debug Enable debug logging
--filter PATTERN Filter by name (substring match)
--kind KIND Filter by kind (OBJECT, ENUM, INPUT_OBJECT, SCALAR, INTERFACE, UNION)
-f, --format FORMAT Output format (default: compact)
-u, --url URL GraphQL endpoint (env: GRAPHQL_URL)
--env VALUE Environment from .gqlcli.json
-d, --debug Enable debug logging
The gqlcli package provides two ways to build CLI tools:
| Mode | Use Case |
|---|---|
| HTTP Mode | Build a CLI that queries external GraphQL APIs over HTTP |
| Inline Mode | Build a GraphQL-backed CLI with inline execution (using gqlgen) — perfect for AI agents and schema-driven CLIs |
See sections below for detailed examples of each mode.
go install github.com/wricardo/gqlcli/cmd/gqlcli@latestOr clone and build:
git clone https://github.com/wricardo/gqlcli.git
cd gqlcli
make install
gqlcli --helpgo get github.com/wricardo/gqlcliBuild a CLI that connects to external GraphQL servers over HTTP. Useful for API testing, schema exploration, and CI/CD pipelines:
package main
import (
"os"
"log"
"github.com/urfave/cli/v2"
"github.com/wricardo/gqlcli/pkg"
)
func main() {
cfg := &gqlcli.Config{
URL: "http://localhost:8080/graphql",
Format: "toon",
Timeout: 30,
}
builder := gqlcli.NewCLIBuilder(cfg)
app := &cli.App{
Name: "gql",
Usage: "GraphQL CLI",
}
builder.RegisterCommands(app)
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}Build GraphQL-native CLI applications where GraphQL is the interface language, not subcommands and flags. This is especially powerful for AI agents that can introspect schemas and construct queries dynamically.
Why GraphQL for CLIs:
| Traditional CLI | GraphQL-Native CLI |
|---|---|
myapp --user-type=active --limit 10 --format json |
myapp query '{ users(type: "active", limit: 10) { id name } }' |
| Multiple commands for different operations | One unified query language |
| AI must learn your CLI's custom flags | AI naturally understands GraphQL |
| Hard to combine operations | Execute multiple queries in parallel |
| Schema is implicit | Schema is explicit and queryable |
If you have a gqlgen schema, you can run operations in-process without an HTTP server. This is useful for building a CLI that ships alongside your application binary.
package main
import (
"log"
"os"
"github.com/urfave/cli/v2"
gqlcli "github.com/wricardo/gqlcli/pkg"
"github.com/myorg/myapp/graph" // your gqlgen package
)
func main() {
// 1. Create your gqlgen ExecutableSchema.
r := graph.NewResolver()
execSchema := graph.NewExecutableSchema(graph.Config{Resolvers: r})
// 2. Inline executor — runs operations directly in-process.
// WithSchemaHints attaches compact type SDL to validation errors.
exec := gqlcli.NewInlineExecutor(execSchema,
gqlcli.WithSchemaHints(),
)
// 3. Command set — adds query, mutation, describe, types commands.
commands := gqlcli.NewInlineCommandSet(exec)
// 4. Mount onto any urfave/cli app.
app := &cli.App{Name: "myapp", Usage: "CLI for my GraphQL API"}
commands.Mount(app)
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}This adds the following subcommands:
| Command | Description |
|---|---|
query |
Execute a query (TOON format by default) |
mutation |
Execute a mutation (JSON format by default) |
describe TYPE |
Print SDL definition of a type |
types |
List all types in the schema |
Schema hints — when WithSchemaHints() is enabled, validation errors include a compact SDL description of the referenced type:
Error: Cannot query field "titl" on type "Book".
Schema hint:
type Book {
id: ID!
title: String!
author: Author!
}
Available only in inline execution mode. Print the SDL definition of a type:
# Describe a type
./myapp describe Query
./myapp describe Book
./myapp describe AddBookInput
# Output shows field signatures and relationships
type Book {
id: ID!
title: String!
author: Author!
}Useful for AI agents to discover schema structure before constructing queries.
See example/README.md for a complete working example of a GraphQL-native CLI — no subcommands, no flags, just GraphQL queries and mutations. The example demonstrates:
- GraphQL as the interface — Execute queries like
./myapp query '{ books { id title author { name } } }' - Schema introspection — AI agents can discover capabilities with
./myapp describe Book - Parallel execution — Multiple top-level queries in one command
- Inline execution — No HTTP server needed, runs in-process against a gqlgen schema
- File-based persistence — Data stored in
store.json - Forced resolvers — Using
@goField(forceResolver: true)for lazy-loading - Split schema files — Organized with follow-schema layout
This is the ideal paradigm for:
- AI agents — Introspect schema, construct queries, explore data
- CLI automation — Write complex queries instead of chaining commands
- Consistent interfaces — GraphQL works everywhere, agents already understand it
See example/README.md for detailed setup and usage.
| Component | Purpose |
|---|---|
| Config | Configuration holder (URL, format, timeout) |
| CLIBuilder | HTTP-based CLI command generator |
| InlineExecutor | In-process executor for gqlgen schemas |
| InlineCommandSet | CLI commands backed by an InlineExecutor |
| TokenStore | JWT persistence at ~/.{appName}/token |
| Describer | Introspects a schema and returns SDL for a type |
| Formatter | Output format converter (JSON, table, TOON, etc.) |
| FormatterRegistry | Manages available formatters |
pkg/
├── cli.go # HTTP-based CLI command builders (CLIBuilder)
├── client.go # HTTP GraphQL client
├── inline.go # InlineExecutor — in-process execution
├── inline_commands.go # InlineCommandSet — query/mutation/describe/login commands
├── projectconfig.go # .gqlcli.json loader and environment resolution
├── token.go # TokenStore — JWT persistence and parsing
├── describe.go # Describer — schema introspection and SDL formatting
├── formatter.go # Output formatters
└── types.go # Type definitions and interfaces
package main
import "github.com/wricardo/gqlcli/pkg"
type CSVFormatter struct{}
func (f *CSVFormatter) Format(data map[string]interface{}) (string, error) {
// Your CSV formatting logic
return csvOutput, nil
}
func (f *CSVFormatter) Name() string {
return "csv"
}
// Usage:
registry := gqlcli.NewFormatterRegistry()
registry.Register("csv", &CSVFormatter{})type CachedClient struct {
cache map[string]interface{}
}
func (c *CachedClient) Execute(ctx context.Context, mode gqlcli.ExecutionMode, opts gqlcli.QueryOptions) (map[string]interface{}, error) {
// Check cache first
// Fall back to HTTP if not found
return result, nil
}# Discover available operations
gqlcli queries
gqlcli mutations
# Test a mutation
gqlcli mutation \
--mutation-file ./test/mutations/createUser.graphql \
--variables-file ./test/variables.json# Generate schema documentation
gqlcli introspect --format llm > SCHEMA.md
# List all types
gqlcli types --format json-pretty > types.json# Verify schema changes
gqlcli introspect --format json > current-schema.json
git diff previous-schema.json current-schema.json# Get schema in token-efficient format
gqlcli introspect --format toon
# Discover operations for LLM context
gqlcli queries --desc --format toon
gqlcli mutations --desc --args --format toon# Run all tests
make test
# Test with coverage
make test-coverage
# Run linter
make lint
# Format code
make fmt# Build
make build
# Build and test
make dev
# Install locally
make install
# Clean artifacts
make clean
# View all available commands
make helpRich error messages with context:
🚨 GraphQL Validation/Execution Errors:
❌ 1. Cannot query field "unknown" on type "Query"
📂 Path: unknown
🏷️ Code: GRAPHQL_VALIDATION_FAILED
📍 Position: Line 1, Column 3
📝 Query that caused the error:
1 | { unknown }
- Zero Dependencies — Single binary, no runtime dependencies
- Production-Ready — Extensively tested and battle-hardened
- Token-Efficient — TOON format reduces tokens by 40-60%
- Extensible — Clean interfaces for custom formatters and clients
- Flexible Input — Multiple ways to specify queries and variables
- DevOps Friendly — Perfect for scripts, CI/CD, and automation
- Open Source — MIT licensed, community-driven
We welcome contributions! Whether it's bug fixes, features, documentation, or examples.
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Run tests:
make test - Run linter:
make lint - Commit:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing-feature - Open a Pull Request
- Keep commits focused and descriptive
- Add tests for new functionality
- Update documentation as needed
- Follow Go conventions and style
- Run
make fmtbefore committing
MIT License — see LICENSE file for details.
- Issues — GitHub Issues for bugs and features
- Discussions — GitHub Discussions for questions
- Email — Open an issue with the
questionlabel
Built with:
- urfave/cli — CLI framework
- go-resty/resty — HTTP client
- toon-format/toon-go — Token-optimized format
Active Development — Maintained and open to contributions.
Latest features:
- ✅
.gqlcli.jsonproject config — named environments with URL and custom headers,--envflag - ✅ Inline execution — run operations in-process against a gqlgen schema (no HTTP server)
- ✅ Schema hints — attach type SDL to GraphQL validation errors
- ✅ Token store — JWT persistence and parsing for login/logout/whoami
- ✅ Query and Mutation operation discovery (
queries,mutationscommands) - ✅ Token-optimized TOON format (default)
- ✅ Environment variable support (
GRAPHQL_URL) - ✅ Multiple output formats
- ✅ Extensible architecture
Made with ❤️ for the GraphQL community