<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jeffmo/libgraphql/refs/heads/main/crates/libgraphql-parser/assets/readme-banner-dark.svg" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/jeffmo/libgraphql/refs/heads/main/crates/libgraphql-parser/assets/readme-banner-light.svg" />
<img src="https://raw.githubusercontent.com/jeffmo/libgraphql/refs/heads/main/crates/libgraphql-parser/assets/readme-banner-light.svg" alt="libgraphql-parser — Blazing fast, error-resilient GraphQL parser" width="100%" />
</picture>
</p>
<p align="center">
<a href="https://crates.io/crates/libgraphql-parser"><img src="https://img.shields.io/crates/v/libgraphql-parser.svg?style=flat-square" alt="crates.io" /></a>
<a href="https://docs.rs/libgraphql-parser/"><img src="https://img.shields.io/docsrs/libgraphql-parser?style=flat-square" alt="docs.rs" /></a>
<a href="https://github.com/jeffmo/libgraphql/blob/main/LICENSE"><img src="https://img.shields.io/crates/l/libgraphql-parser.svg?style=flat-square" alt="license" /></a>
<img src="https://img.shields.io/badge/GraphQL_Spec-September_2025-e535ab?style=flat-square" alt="GraphQL Spec" />
</p>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jeffmo/libgraphql/refs/heads/main/crates/libgraphql-parser/assets/readme-code-dark.svg" />
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/jeffmo/libgraphql/refs/heads/main/crates/libgraphql-parser/assets/readme-code-light.svg" />
<img src="https://raw.githubusercontent.com/jeffmo/libgraphql/refs/heads/main/crates/libgraphql-parser/assets/readme-code-light.svg" alt="Two-column code preview showing GraphQL schema and query parsing with syntax-highlighted error diagnostics" width="100%" />
</picture>
<br />
<br />
## Features
- **Error-resilient parsing** — documents produce a partial AST alongside a list
of errors, even when the input is malformed. Never panics.
- **Rust-inspired error output** — error messages with source snippets, span
highlighting, contextual notes, and fix suggestions.
- **Blazing fast** - [Perf metrics](#performance) exceeding
[`apollo_parser`](https://crates.io/crates/apollo-parser) (v0.8.4) and
matching or exceeding
[`graphql_parser`](https://crates.io/crates/graphql-parser) (v0.4.1) on all
fixtures.
- **Zero-copy lexing** — uses `Cow<'src, str>` to avoid allocations for
tokens that match the source verbatim.
- **Parse schema, executable, and mixed documents** — parses type definitions,
operations/fragments, or documents containing both interleaved together.
- **[September 2025](https://spec.graphql.org/September2025/) GraphQL
specification** compliance.
- **Dual column tracking** — reports both UTF-8 character positions (for
display) and UTF-16 code unit positions (for LSP integration).
- **Comment/trivia preservation** — captures comments and other trivia as
"preceding trivia" attached to tokens.
- **Generic over token sources** — the parser works with any
`GraphQLTokenSource` (string input, Rust proc-macro token stream input, etc.).
- **Configurable AST access** — `valid()` API for strict consumers that
require error-free input, `ast()` for best-effort tooling that needs
error-recovery (IDEs, linters, formatters, etc).
- **Fuzz-tested at scale** — [70M+ `libfuzzer` executions](#fuzz-testing) across
4 fuzz targets, zero crashes.
- **Lossless syntax tree** — every AST node carries byte-offset spans; combined
with the `SourceMap`, the original source text can be reconstructed
losslessly via `AstNode::append_source()`.
- **`graphql-parser` v0.4 compatibility** — bidirectional conversion between
`libgraphql-parser`'s AST and the
[`graphql_parser`](https://crates.io/crates/graphql-parser) v0.4 AST via the
[`compat`](https://docs.rs/libgraphql-parser/latest/libgraphql_parser/compat/)
module.
_Coming soon:_
- **Drop-in compat with `apollo-parser`** — translation utils to make it easy to
integrate with tools that already depend on the
[`apollo_parser::cst`](https://docs.rs/apollo-parser/0.8.4/apollo_parser/cst/index.html)
AST structure.
## Getting Started
```bash
cargo add libgraphql-parser
```
Or add this to your `Cargo.toml`:
```toml
[dependencies]
libgraphql-parser = "0.0.4"
```
## Usage
Parse a GraphQL schema document:
```rust
use libgraphql_parser::GraphQLParser;
let result = GraphQLParser::new("type Query { hello: String }")
.parse_schema_document();
assert!(!result.has_errors());
let (doc, _) = result.valid().unwrap();
```
Parse an executable document (queries, mutations, subscriptions):
```rust
use libgraphql_parser::GraphQLParser;
let result = GraphQLParser::new("{ user { name email } }")
.parse_executable_document();
assert!(!result.has_errors());
```
Get the line and column of a node:
```rust
use libgraphql_parser::GraphQLParser;
let result = GraphQLParser::new("type Query { hello: String }")
.parse_schema_document();
let (doc, source_map) = result.valid().unwrap();
let query_def_node = &doc.definitions[0];
let source_span = query_def_node.source_span(&source_map).unwrap();
// line_col() returns 0-based (line, column) pairs where columns
// are measured in UTF-8 characters (not bytes or UTF-16 units).
let ((start_line, start_col), (end_line, end_col)) = source_span.line_col();
```
### Error Recovery
Unlike many GraphQL parsers that stop at the first error,
`libgraphql-parser` collects multiple errors and still produces a
best-effort AST. This is essential for IDE integration, linters, and
formatters:
```rust
use libgraphql_parser::GraphQLParser;
// This schema has errors: missing `:` on field type, unclosed brace
let source = "type Query { hello String";
let result = GraphQLParser::new(source).parse_schema_document();
// Errors are collected — inspect them all at once
assert!(result.has_errors());
for error in result.errors() {
eprintln!("{}", error.format_detailed(Some(source)));
}
// A partial AST is always available for best-effort tooling
let doc = result.ast();
// IDE completions, formatting, linting can still work
// on the partially-parsed document
println!("Parsed {} definitions", doc.definitions.len());
```
### Diagnostic Output
Error messages include source spans, contextual notes, and fix
suggestions — inspired by the Rust compiler's diagnostic style:
```text
error: unknown directive location `FIELD_DEFINTION`
--> <input>:1:42
|
= help: did you mean `FIELD_DEFINITION`?
```
Unclosed delimiters point back to the opening location:
```text
error: unclosed `{`
--> <input>:9:2
|
= note: opening `{` in selection set here
1 | query {
| -
```
### Strict vs. Best-Effort AST Access
`ParseResult` offers two modes for accessing the AST:
```rust
use libgraphql_parser::GraphQLParser;
let source = "type Query { hello: String }";
let result = GraphQLParser::new(source).parse_schema_document();
// Strict mode: returns AST only if there were zero errors.
// Use this when compiling schemas or executing queries.
if let Some((doc, _source_map)) = result.valid() {
// Guaranteed: no parse errors
}
// Best-effort mode: AST is always available, even with errors.
// Use this for IDE features, formatters, and linters.
let doc = result.ast();
// May be a partial/recovered AST — check result.has_errors()
```
## Design Goals
- **Performance** — zero-copy lexing via `Cow<'src, str>`, minimal
allocations (e.g., `SmallVec` for trivia), and hand-written recursive
descent parsing.
- **Error resilience** — always produce as much AST as possible, collecting
all errors in a single pass rather than stopping at the first failure.
- **Spec correctness** — targets the
[September 2025](https://spec.graphql.org/September2025/) GraphQL
specification.
- **Extensible architecture** — the parser is generic over
`GraphQLTokenSource`, enabling the same parsing logic to work across
string input, proc-macro token streams, and other token sources.
- **Tooling-ready** — designed for IDE integration, linters, formatters,
and compiler frontends with dual UTF-8/UTF-16 position tracking and
configurable AST access.
## Comparison to Alternatives
| **Spec version** | Sep 2025 | Oct 2016 | Oct 2021 | Oct 2021 | Oct 2021 |
| **Error recovery** | ✅ Partial AST + errors | ❌ Fail-fast | ✅ Full CST + errors | ✅ Multiple errors | ❌ Fail-fast |
| **Zero-copy lexing** | ✅ `Cow<'src, str>` | ❌ | ✅ `&'str` | ✅ | ❌ |
| **Output type** | Lossless AST | Lossy AST | Lossless CST | Lossy AST (arena) | Lossy AST |
| **Mixed documents** | ✅ | ❌ | ✅ | ✅ | ❌ |
| **Trivia preserved** | ✅ Comments | ❌ | ✅ All whitespace | ❌ | ❌ |
| **GitHub schema parse** | **8.33 ms** | 8.53 ms | 12.0 ms | ?? | ?? |
## Performance
Performance is a first-class design goal. The lexer avoids allocations via
`Cow<'src, str>` (borrowing directly from the source string for tokens that
don't need transformation) and uses `SmallVec` for trivia storage and
token-buffering. The parser itself is a hand-written recursive descent
parser, avoiding the overhead of parser generator runtimes and allowing for
more helpful and structured error information, notes, and possible-fix
suggestions.
Benchmarks run via [Criterion](https://github.com/bheisler/criterion.rs)
on synthetic schemas (small ~1.5KB, medium ~106KB, large ~500KB),
vendored real-world schemas (Star Wars ~4KB, GitHub ~1.2MB), and a
locally-fetched real-world schema (Shopify Admin ~3.1MB), plus
executable documents.
You can run these benchmarks yourself with
`cargo bench --package libgraphql-parser`, or use the high-confidence
script which will also aggregate results in a table like below:
`./crates/libgraphql-parser/scripts/run-benchmarks.sh`.
> **Measured:** 2026-03-19 on Apple M2 Max (arm64), 64 GB RAM, macOS,
> rustc 1.90.0-nightly (0d9592026 2025-07-19), `--release` profile.
> Comparison parsers: `graphql-parser` 0.4.1, `apollo-parser` 0.8.4.
> All values are Criterion point estimates at a 99% confidence level.
### Schema Parsing
| small (~1.5 KB) | **28.8 µs** | 43.6 µs | 45.4 µs |
| medium (~106 KB) | **1.61 ms** | 1.92 ms | 2.01 ms |
| large (~500 KB) | **8.04 ms** | 8.84 ms | 9.46 ms |
| starwars (~4 KB) | **34.7 µs** | 49.3 µs | 54.1 µs |
| github (~1.2 MB) | **8.33 ms** | 8.53 ms | 12.0 ms |
| shopify_admin (~3.1 MB) | **14.7 ms** | 16.7 ms | 24.6 ms |
### Executable Document Parsing
| simple query | **1.78 µs** | 2.81 µs | 2.96 µs |
| complex query | **28.7 µs** | 38.9 µs | 38.0 µs |
### Lexer Throughput
| small (~1.5 KB) | 6.81 µs | ~331 MiB/s |
| medium (~106 KB) | 318 µs | ~317 MiB/s |
| large (~500 KB) | 1.48 ms | ~322 MiB/s |
| starwars (~4 KB) | 9.02 µs | ~440 MiB/s |
| github (~1.2 MB) | 2.06 ms | ~568 MiB/s |
| shopify_admin (~3.1 MB) | 3.68 ms | ~840 MiB/s |
## Core Types
| [`GraphQLParser<S>`] | Generic recursive-descent parser. Entry points: `parse_schema_document()`, `parse_executable_document()`, `parse_mixed_document()`. |
| [`ParseResult<T>`] | Result type holding both a (possibly partial) AST and accumulated errors. |
| [`StrGraphQLTokenSource`] | Zero-copy lexer producing `GraphQLToken` streams from `&str` input. |
| [`GraphQLParseError`] | Parse error with message, source span, categorized kind, and contextual notes. |
| [`GraphQLTokenSource`] | Trait for pluggable token sources (string input, proc-macro tokens, etc.). |
[`GraphQLParser<S>`]: https://docs.rs/libgraphql-parser/latest/libgraphql_parser/struct.GraphQLParser.html
[`ParseResult<T>`]: https://docs.rs/libgraphql-parser/latest/libgraphql_parser/enum.ParseResult.html
[`StrGraphQLTokenSource`]: https://docs.rs/libgraphql-parser/latest/libgraphql_parser/token/struct.StrGraphQLTokenSource.html
[`GraphQLParseError`]: https://docs.rs/libgraphql-parser/latest/libgraphql_parser/struct.GraphQLParseError.html
[`GraphQLTokenSource`]: https://docs.rs/libgraphql-parser/latest/libgraphql_parser/token/trait.GraphQLTokenSource.html
## Part of the `libgraphql` Ecosystem
`libgraphql-parser` is the parsing foundation of the
[`libgraphql`](https://crates.io/crates/libgraphql) project — a
comprehensive GraphQL engine library for building tools, clients, and
servers in Rust. It is used by `libgraphql-core` for schema building,
operation validation, and type system logic.
## Running Tests
```bash
cargo test --package libgraphql-parser
```
## Fuzz Testing
The crate includes a [`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz)
setup under `fuzz/` with four targets:
| `fuzz_lexer` | `StrGraphQLTokenSource` (full token iteration) |
| `fuzz_parse_schema` | `GraphQLParser::parse_schema_document()` |
| `fuzz_parse_executable` | `GraphQLParser::parse_executable_document()` |
| `fuzz_parse_mixed` | `GraphQLParser::parse_mixed_document()` |
### Prerequisites
```bash
rustup toolchain install nightly
cargo install cargo-fuzz
```
### Running Fuzz Tests
**Quick smoke test (1 minute per target, parallel):**
```bash
./crates/libgraphql-parser/scripts/run-fuzz-tests.sh
```
**Sustained run (15 minutes per target, parallel):**
```bash
./crates/libgraphql-parser/scripts/run-fuzz-tests.sh 15
```
**Single target:**
```bash
./crates/libgraphql-parser/scripts/run-fuzz-tests.sh 5 fuzz_lexer
```
**Raw `cargo fuzz` (from the fuzz directory):**
```bash
cd crates/libgraphql-parser/fuzz
cargo +nightly fuzz run fuzz_lexer -- -max_total_time=60
```
### Latest Fuzz Testing Results
**Date:** 2026-02-10
**Duration:** 60 minutes per target (4 targets in parallel)
**Platform:** macOS (aarch64), nightly Rust
| `fuzz_lexer` | 39,990,040 | ~11,105 | 31,875 | 0 |
| `fuzz_parse_schema` | 9,220,187 | ~2,560 | 43,160 | 0 |
| `fuzz_parse_executable` | 11,261,274 | ~3,127 | 43,633 | 0 |
| `fuzz_parse_mixed` | 10,173,478 | ~2,825 | 48,957 | 0 |
**Total:** 70,644,979 executions across all targets, zero crashes.
## License
Licensed under the [MIT license](https://github.com/jeffmo/libgraphql/blob/main/LICENSE).