graphql-go/graphql
An implementation of GraphQL for Go / Golang
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 4mo ago
- ✓18 active contributors
- ✓MIT licensed
Show all 7 evidence items →Show less
- ✓Tests present
- ⚠Slowing — last commit 4mo ago
- ⚠Concentrated ownership — top contributor handles 54% of recent commits
- ⚠No CI workflows detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/graphql-go/graphql)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/graphql-go/graphql on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: graphql-go/graphql
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/graphql-go/graphql shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 4mo ago
- 18 active contributors
- MIT licensed
- Tests present
- ⚠ Slowing — last commit 4mo ago
- ⚠ Concentrated ownership — top contributor handles 54% of recent commits
- ⚠ No CI workflows detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live graphql-go/graphql
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/graphql-go/graphql.
What it runs against: a local clone of graphql-go/graphql — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in graphql-go/graphql | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 161 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of graphql-go/graphql. If you don't
# have one yet, run these first:
#
# git clone https://github.com/graphql-go/graphql.git
# cd graphql
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of graphql-go/graphql and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "graphql-go/graphql(\\.git)?\\b" \\
&& ok "origin remote is graphql-go/graphql" \\
|| miss "origin remote is not graphql-go/graphql (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "graphql.go" \\
&& ok "graphql.go" \\
|| miss "missing critical file: graphql.go"
test -f "executor.go" \\
&& ok "executor.go" \\
|| miss "missing critical file: executor.go"
test -f "definition.go" \\
&& ok "definition.go" \\
|| miss "missing critical file: definition.go"
test -f "language/parser/parser.go" \\
&& ok "language/parser/parser.go" \\
|| miss "missing critical file: language/parser/parser.go"
test -f "language/visitor/visitor.go" \\
&& ok "language/visitor/visitor.go" \\
|| miss "missing critical file: language/visitor/visitor.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 161 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~131d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/graphql-go/graphql"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
graphql-go is a complete GraphQL server implementation for Go that follows the official graphql-js reference spec, enabling developers to define type-safe schemas and execute queries, mutations, and subscriptions natively in Go. It provides core GraphQL execution, type system (scalars, objects, interfaces, unions, enums), directives, and schema validation without requiring a separate service. Monolithic package structure: core type system (definition.go, directives.go), execution engine (executor.go), schema management (graphql.go implied), and extensions system (extensions.go). Examples/ directory contains runnable reference implementations (star-wars/, todo/, crud/, http/, context/). Tests are colocated with source files (*_test.go).
👥Who it's for
Go backend developers and service owners who need to expose data via GraphQL APIs (REST-to-GraphQL bridges, microservice graph layers, real-time APIs) and want a production-grade implementation that tracks the official GraphQL specification rather than managing external services.
🌱Maturity & risk
Production-ready and actively maintained. The project has comprehensive test coverage (abstract_test.go, definition_test.go, executor_test.go, executor_resolve_test.go, executor_schema_test.go), CircleCI CI/CD integration, Go 1.13+ support, and multiple battle-tested examples (star-wars, todo, crud, http). The codebase is ~1.1M lines of Go with stable release cadence.
Low risk for core functionality; minimal external dependencies (vendoring not evident in file list, pure Go implementation). Single-author repositories sometimes face maintenance gaps—verify last commit recency and check for stale PRs in the GitHub UI. No hidden breaking changes expected given spec-compliance focus, but always test schema migrations against your resolver logic.
Active areas of work
Without access to git log or PR list, ongoing work is inferred from test files: executor_resolve_test.go and extensions.go suggest active work on resolver performance and extension hooks. The CircleCI config indicates continuous integration on master branch.
🚀Get running
git clone https://github.com/graphql-go/graphql.git
cd graphql
go mod tidy
go test ./...
Daily commands:
Examples are standalone binaries. To run the hello-world example: cd examples/hello-world && go run main.go. To run the HTTP server example: cd examples/http && go run main.go (starts on default port, check main.go for details). Tests: go test ./... from root.
🗺️Map of the codebase
graphql.go— Entry point and main API surface; defines Do() function and core GraphQL execution interfaceexecutor.go— Core execution engine that resolves queries against a schema; handles field resolution and result coerciondefinition.go— Type system definitions (Object, Interface, Union, Scalar, List, NonNull); foundational schema abstractionslanguage/parser/parser.go— Parses GraphQL query strings into AST; critical for query validation and executionlanguage/visitor/visitor.go— Visitor pattern implementation for AST traversal; used by validation rules and schema introspectionrules.go— Query validation rules registry; enforces GraphQL specification constraints before executionintrospection.go— Schema introspection implementation; enables runtime schema discovery and API documentation
🧩Components & responsibilities
- graphql.Do() (Go interfaces, error handling) — Orchestrates full query lifecycle: parsing → validation → execution → result formatting
- Failure mode: Returns {data: null, errors: [...]} if parsing, validation, or execution fails
- Parser (language/parser) (Recursive descent parsing, Go string handling) — Converts GraphQL query string to AST nodes; implements GraphQL grammar spec exactly
- Failure mode: Returns syntax error with location (line/column) if query is malformed
- Validator (rules.go + visitor) (Visitor pattern, rule registry) — Applies GraphQL specification rules (type matching, argument validity, etc.) to AST before execution
- Failure mode: Collects multiple validation errors (e.
🛠️How to make changes
Add a Custom Scalar Type
- Create a new scalar type in your schema file by defining a GraphQLScalarType with Parse, Serialize, and ParseValue functions (
definition.go) - Implement the Parse function to convert AST value nodes to Go values (
definition.go) - Implement Serialize to convert Go values back to JSON-serializable outputs (
definition.go) - Reference your custom scalar in object field definitions and test with executor_test.go patterns (
executor_test.go)
Add a New Validation Rule
- Create a new validation rule file following the naming pattern rules_*.go (
rules.go) - Implement the rule struct with Enter() and Leave() visitor methods for AST traversal (
language/visitor/visitor.go) - Register your rule in the SpecifiedRules or get it from rules.go and add to validation call (
rules.go) - Write tests following rules_*_test.go pattern to verify rule catches violations (
rules_arguments_of_correct_type_test.go)
Add a New Object Type & Resolvers
- Define a new GraphQLObjectType in your schema with fields as GraphQLFieldConfig map (
definition.go) - Implement Resolve functions for each field; signature is (p ResolveParams) (interface{}, error) (
executor_resolve_test.go) - Add the object type to your root Query or Mutation fields (
definition.go) - Test field resolution using executor_test.go patterns with graphql.Do() or graphql.Graphql() (
executor_test.go)
Handle Context in Resolvers
- Access context in resolver Resolve function via ResolveParams.Context (
executor.go) - Store context values using context.WithValue() before calling graphql.Do() (
examples/context/main.go) - Retrieve context in resolver for auth, logging, or request-scoped data (
executor_resolve_test.go)
🔧Why these technologies
- Go (1.13+) — Statically compiled, high-performance language with excellent concurrency primitives for parallel field resolution
- Visitor Pattern (language/visitor) — Enables clean separation between AST structure and operations (validation, introspection) without modifying AST nodes
- Recursive Descent Parser (language/parser) — Directly mirrors GraphQL grammar specification; easier to maintain and extend with new syntax
- Interface-based Type System (definition.go) — Allows flexible runtime type handling and supports GraphQL's complex type relationships (interfaces, unions, non-null wrapping)
⚖️Trade-offs already made
-
Eager validation before execution via rules.go
- Why: Catch errors early and fail-fast before expensive resolver calls; matches graphql-js behavior
- Consequence: Upfront validation overhead (~1-10ms) prevents bad queries from reaching resolvers, but adds latency to every request
-
Synchronous field resolution (executor.go)
- Why: Simpler implementation and predictable ordering; mirrors graphql-js blocking behavior
- Consequence: Resolvers must be fast; slow resolvers block entire query; no built-in async/await like graphql-js promises
-
No built-in type coercion in scalars (definition.go)
- Why: Explicit control: developers define Parse/Serialize logic; clear error messages
- Consequence: More boilerplate for custom scalars; must implement bidirectional conversion
-
AST-based validation before execution
- Why: Catches syntax and schema violations before resolvers run; matches spec exactly
- Consequence: Cannot short-circuit validation; all rules run even if first rule fails
🚫Non-goals (don't propose these)
- Subscriptions (listen to real-time updates)
- Batching or DataLoader integration (N+1 mitigation)
- Built-in authentication or authorization middleware
- GraphQL Federation or schema stitching
- ORM integration or database query generation
🪤Traps & gotchas
No explicit .env or config files: examples hard-code ports and schemas. If extending examples, verify port availability (http example uses 8080). Interface casting required: Resolve functions return interface{}, so you must assert types (e.g., value.(string)); type mismatches fail at runtime, not build time—test your resolvers. Nil resolver behavior: omitting a Resolve function on a Field may cause panics if that field is queried; always provide resolvers or use default field name resolution. Circular type definitions: ObjectType A referencing ObjectType B which references A requires lazy field resolution via thunks (check definition_test.go for examples).
🏗️Architecture
💡Concepts to learn
- GraphQL Type System — definition.go implements the core abstraction (Object, Interface, Union, Enum, Scalar types); understanding how these compose is essential to using or extending graphql-go
- Resolver Functions & ResolveParams — The Resolve callback signature and context passing (ResolveParams with Context, Args, Source, FieldAST) is how you inject business logic; mastering this is required for any custom implementation
- Field Resolution & Traversal — executor.go implements the recursive field resolution algorithm; understanding how it walks the query AST and resolves fields in order is critical for debugging nested queries and error handling
- Directives (@skip, @include) — directives.go shows how directives modify query behavior at runtime; essential for conditional field inclusion and custom directive patterns
- Schema Validation & Type Coercion — definition.go and executor.go enforce scalar coercion rules (Int, String, Boolean, Float, custom scalars); incorrect coercion causes silent failures or panics
- Thunks for Lazy Type Resolution — definition.go uses func() *ObjectType patterns to break circular type dependencies; required for self-referential or mutually recursive schemas (see definition_test.go for examples)
- Interface & Union Polymorphism — Interfaces and Unions in definition.go enable polymorphic types and require ResolveType callbacks; critical for queries that return multiple possible types and for understanding fragment selection
🔗Related repos
graphql/graphql-js— Official reference implementation in JavaScript—graphql-go follows its spec and architecture closely; consult when behavior is ambiguousgraphql-go/graphql-go-handler— Official companion HTTP handler library for graphql-go; adds middlewares, playground support, and request parsing without touching core executor99designs/gqlgen— Alternative Go GraphQL server using code-gen from schema instead of runtime reflection; compare for type safety vs. flexibility trade-offsgraph-gophers/graphql-go— Another Go implementation; highlights different design choices (struct tags vs. func configs); useful for comparing API ergonomicsgraphql/graphql-spec— Official GraphQL specification repository; referenced for correctness of type validation, coercion, and execution model
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive unit tests for language/ast package
The language/ast directory contains AST node definitions (arguments.go, definitions.go, directives.go, document.go) but there are no corresponding _test.go files visible in the file structure. This is critical since AST parsing and manipulation is fundamental to GraphQL execution. Adding tests would improve reliability of query parsing, catch edge cases, and provide examples for contributors.
- [ ] Create language/ast/arguments_test.go with tests for argument parsing and validation
- [ ] Create language/ast/definitions_test.go covering type definitions, fields, and input types
- [ ] Create language/ast/directives_test.go for directive parsing and application
- [ ] Create language/ast/document_test.go for document structure and traversal
- [ ] Ensure >80% code coverage for the language/ast package
Add integration tests for subscription support
The README explicitly states 'Supports: queries, mutations & subscriptions' but the visible test files (executor_test.go, executor_resolve_test.go, executor_schema_test.go) don't show dedicated subscription testing. Subscriptions are complex (stateful, real-time) and warrant specific test coverage including edge cases like subscription cancellation, error handling, and concurrent subscriptions.
- [ ] Create executor_subscription_test.go with tests for basic subscription execution
- [ ] Add tests for subscription field resolution with data streams
- [ ] Add tests for subscription cleanup and cancellation
- [ ] Add tests for multiple concurrent subscriptions on the same schema
- [ ] Add tests for subscription error handling and invalid subscription queries
Add GitHub Actions CI workflow to replace CircleCI
The repo currently uses CircleCI (visible in .circleci/config.yml), but GitHub Actions is now standard for GitHub-hosted projects and reduces external dependencies. Adding a parallel GitHub Actions workflow would improve accessibility for contributors, reduce vendor lock-in, and allow tests to run on multiple Go versions (1.13+, 1.18+, 1.20+) consistently.
- [ ] Create .github/workflows/tests.yml for running unit tests across Go 1.13, 1.18, 1.20, and latest
- [ ] Add code coverage reporting to the workflow (using codecov or similar)
- [ ] Add linting checks (using golangci-lint for style consistency)
- [ ] Add benchmark comparison for performance regressions on tagged releases
- [ ] Document the workflow setup in CONTRIBUTING.md
🌿Good first issues
- Add comprehensive benchmarks for deeply nested queries: examples/concurrent-resolvers/ exists but benchutil/ (list_schema.go, wide_schema.go) is minimal. Extend BenchmarkListSchema and BenchmarkWideSchema in benchutil/ to test 100+ levels of nesting and add results to README.
- Write integration docs for extensions.go: The extensions.go file exists but has no corresponding example. Create examples/extensions/main.go demonstrating custom extension hooks (e.g., logging middleware, timing, error tracking) with test coverage.
- Expand enum type tests: enum_type_test.go is sparse. Add test cases for: enum value serialization edge cases, invalid values, case sensitivity, and custom scalar enum coercion—reference definition_test.go patterns.
⭐Top contributors
Click to expand
Top contributors
- @chris-ramon — 54 commits
- @remorses — 17 commits
- @bhoriuchi — 10 commits
- @adelsz — 2 commits
- @dariuszkuc — 2 commits
📝Recent commits
Click to expand
Recent commits
d5f8f3c— Merge pull request #734 from graphql-go/issue-731 (chris-ramon)f6845ed— definition: adds NewScalar unit tests (chris-ramon)76aa82b— Merge pull request #733 from graphql-go/issue-issue-731 (chris-ramon)d4e5a87— definition: adds GetNullable unit tests (chris-ramon)3ca1f2d— Merge pull request #732 from graphql-go/issue-731 (chris-ramon)bdd8b17— definition: adds IsAbstractType unit tests (chris-ramon)fd79992— Merge pull request #727 from graphql-go/issue-726 (chris-ramon)f59020a— introspection: grammar fixes (chris-ramon)f2a7e55— Merge pull request #725 from graphql-go/issue-724 (chris-ramon)e297ede— language/parser: updates test names for compatibility (chris-ramon)
🔒Security observations
The graphql-go/graphql codebase demonstrates generally good security practices with no obvious critical vulnerabilities in the visible structure. However, there are medium-risk concerns: (1) the outdated Go 1.13 version requirement poses maintenance and security patch risks, and (2) potential missing input validation and query complexity limits that are common attack vectors for GraphQL APIs. Low-severity issues include example applications that may inadvertently expose introspection or lack production-ready security controls (rate limiting, timeouts). Recommendations focus on modernizing the Go version, implementing GraphQL-specific security controls, and adding comprehensive security documentation for users implementing production systems based on this library.
- Medium · Outdated Go Version Specification —
go.mod. The go.mod file specifies Go 1.13, which was released in September 2019 and is now significantly outdated. This version lacks numerous security patches and bug fixes available in newer Go versions. Go 1.13 reached end-of-life in April 2021. Fix: Update the Go version requirement to at least Go 1.21 or the latest stable version. Run 'go mod tidy' and test with a modern Go version. Update CI/CD configurations to build and test against supported Go versions. - Medium · Missing Input Validation in GraphQL Executor —
executor.go, language/parser/parser.go. GraphQL executors commonly process untrusted user input (queries, variables). Without examining executor.go and related files, there is a potential risk of inadequate validation leading to DoS attacks (deeply nested queries, large lists) or query complexity attacks. Fix: Implement query complexity analysis and depth limiting. Add validation for maximum query depth, maximum query complexity, and field count limits. Consider implementing persistent query allowlisting for production environments. - Low · Example Applications May Expose Debug Interfaces —
examples/http/main.go, examples/http-post/main.go, examples/httpdynamic/main.go. The examples directory contains multiple HTTP server implementations (examples/http, examples/http-post, examples/httpdynamic). These examples may enable GraphQL introspection or expose GraphiQL/playground interfaces by default, which could leak schema information in production if used as templates. Fix: Add clear security documentation noting that introspection should be disabled in production. Implement environment-based configuration to disable introspection and GraphQL playground. Add comments in example code highlighting security considerations. - Low · No Visible Rate Limiting or Timeout Mechanisms —
examples/http/main.go, examples/httpdynamic/main.go. The HTTP examples do not appear to implement rate limiting, request timeouts, or connection limits, which could be exploited for DoS attacks if these examples are used as templates for production code. Fix: Add HTTP timeout configurations, rate limiting middleware, and request size limits to example code. Document best practices for production GraphQL API deployments including authentication, authorization, and rate limiting strategies. - Low · SQL Injection Example Without Clear Warnings —
examples/sql-nullstring/main.go. The examples/sql-nullstring directory contains database examples. While this appears to be for demonstrating NULL handling, if not properly implemented, SQL queries could be vulnerable to injection. Fix: Ensure all database examples use parameterized queries exclusively. Add security warnings in README files noting the dangers of string concatenation for SQL queries. Consider adding a security note in the main example about prepared statements.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.