RepoPilotOpen in app →

graphql-go/graphql

An implementation of GraphQL for Go / Golang

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 4mo ago
  • 18 active contributors
  • MIT licensed
Show all 7 evidence items →
  • 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.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/graphql-go/graphql)](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:

  1. 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.
  2. 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.
  3. 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 |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>graphql-go/graphql</code></summary>
#!/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).

</details>

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 interface
  • executor.go — Core execution engine that resolves queries against a schema; handles field resolution and result coercion
  • definition.go — Type system definitions (Object, Interface, Union, Scalar, List, NonNull); foundational schema abstractions
  • language/parser/parser.go — Parses GraphQL query strings into AST; critical for query validation and execution
  • language/visitor/visitor.go — Visitor pattern implementation for AST traversal; used by validation rules and schema introspection
  • rules.go — Query validation rules registry; enforces GraphQL specification constraints before execution
  • introspection.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

  1. Create a new scalar type in your schema file by defining a GraphQLScalarType with Parse, Serialize, and ParseValue functions (definition.go)
  2. Implement the Parse function to convert AST value nodes to Go values (definition.go)
  3. Implement Serialize to convert Go values back to JSON-serializable outputs (definition.go)
  4. Reference your custom scalar in object field definitions and test with executor_test.go patterns (executor_test.go)

Add a New Validation Rule

  1. Create a new validation rule file following the naming pattern rules_*.go (rules.go)
  2. Implement the rule struct with Enter() and Leave() visitor methods for AST traversal (language/visitor/visitor.go)
  3. Register your rule in the SpecifiedRules or get it from rules.go and add to validation call (rules.go)
  4. 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

  1. Define a new GraphQLObjectType in your schema with fields as GraphQLFieldConfig map (definition.go)
  2. Implement Resolve functions for each field; signature is (p ResolveParams) (interface{}, error) (executor_resolve_test.go)
  3. Add the object type to your root Query or Mutation fields (definition.go)
  4. Test field resolution using executor_test.go patterns with graphql.Do() or graphql.Graphql() (executor_test.go)

Handle Context in Resolvers

  1. Access context in resolver Resolve function via ResolveParams.Context (executor.go)
  2. Store context values using context.WithValue() before calling graphql.Do() (examples/context/main.go)
  3. 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
  • graphql/graphql-js — Official reference implementation in JavaScript—graphql-go follows its spec and architecture closely; consult when behavior is ambiguous
  • graphql-go/graphql-go-handler — Official companion HTTP handler library for graphql-go; adds middlewares, playground support, and request parsing without touching core executor
  • 99designs/gqlgen — Alternative Go GraphQL server using code-gen from schema instead of runtime reflection; compare for type safety vs. flexibility trade-offs
  • graph-gophers/graphql-go — Another Go implementation; highlights different design choices (struct tags vs. func configs); useful for comparing API ergonomics
  • graphql/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

📝Recent commits

Click to expand
  • 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.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · graphql-go/graphql — RepoPilot