cypher_dart is a pure Dart package for parsing and formatting Cypher queries.
It is designed for tools that need query validation, editor feedback, and normalized query output in Dart or Flutter.
- Parse Cypher text into a typed AST.
- Get diagnostics with source locations (
line,column, offsets). - Use strict OpenCypher by default, or enable Neo4j-specific features explicitly.
- Format parsed queries into stable, canonical output.
- Run the same API on VM and Flutter (mobile/web/desktop).
- Clause-level OpenCypher parsing for common query flows.
- Partial semantic validation (clause ordering, duplicate aliases, feature gating).
- Experimental in-memory execution engine for query flows plus writes (
MATCH/WHERE/WITH/RETURN/ORDER BY/SKIP/LIMIT/UNWIND/UNION/CREATE/MERGE/SET/REMOVE/DELETE/DETACH DELETE/CALL).
dart pub add cypher_dartRecommended entrypoint:
import 'package:cypher_dart/cypher_dart.dart';Equivalent lower-level entrypoint:
import 'package:cypher_dart/opencypher.dart';- DevRel docs index
- Getting started
- Execution engine
- Benchmarking
- Technical report (LaTeX)
- Technical report build guide
import 'package:cypher_dart/cypher_dart.dart';
void main() {
const query = '''
MATCH (n:Person)
WHERE n.age > 30
RETURN n.name AS name
ORDER BY name
LIMIT 5
''';
final result = Cypher.parse(query);
if (result.hasErrors) {
for (final d in result.diagnostics) {
print('${d.code} ${d.severity.name}: ${d.message}');
print('at ${d.span.start.line + 1}:${d.span.start.column + 1}');
}
return;
}
final formatted = CypherPrinter.format(result.document!);
print(formatted);
}Use default options (recoverErrors: false) when invalid queries must be rejected.
final result = Cypher.parse(userQuery);
if (result.hasErrors) {
throw FormatException(result.diagnostics.first.message);
}Use recoverErrors: true while users are typing incomplete queries.
final result = Cypher.parse(
userTypingQuery,
options: const CypherParseOptions(recoverErrors: true),
);
// You can still inspect result.document with diagnostics.Strict OpenCypher is default. Enable extensions explicitly when needed.
final strictResult = Cypher.parse('USE neo4j MATCH (n) RETURN n');
// -> CYP204 in strict mode
final relaxedResult = Cypher.parse(
'USE neo4j MATCH (n) RETURN n',
options: const CypherParseOptions(
recoverErrors: true,
enabledFeatures: <CypherFeature>{
CypherFeature.neo4jUseClause,
},
),
);If your app is Neo4j-first, use CypherDialect.neo4j5.
final result = Cypher.parse(
query,
options: const CypherParseOptions(dialect: CypherDialect.neo4j5),
);final result = Cypher.parse('MATCH (n) RETURN n ORDER BY n.name LIMIT 3');
if (!result.hasErrors && result.document != null) {
final pretty = CypherPrinter.format(result.document!);
print(pretty);
}final result = Cypher.parse('MATCH (n) RETURN n');
if (result.document != null) {
final jsonMap = cypherNodeToJson(result.document!);
print(jsonMap);
}final graph = InMemoryGraphStore()
..createNode(
labels: {'Person'},
properties: {'name': 'Alice', 'age': 34},
);
final execution = CypherEngine.execute(
'MATCH (n:Person) WHERE n.age >= 30 RETURN n.name AS name',
graph: graph,
);
print(execution.records); // [{name: Alice}]Engine notes:
- Supports relationship pattern matching for single-hop patterns like
(a)-[r:TYPE]->(b). - Supports relationship type alternation like
[r:T1|:T2]and path variables inMATCHlikep = (a)-[r]->(b). - Supports basic aggregation in
WITH/RETURN(count,sum,avg,min,max). MERGE ... ON CREATE SET ... ON MATCH SET ...is supported for clause-localSETchains.CALLsupports built-in in-memory procedures:db.labels(),db.relationshipTypes(),db.propertyKeys().
dialect:CypherDialect.openCypher9(default) orCypherDialect.neo4j5enabledFeatures: explicit Neo4j extension allow-listrecoverErrors:false(fail-fast) ortrue(best-effort parse)
AST(Abstract Syntax Tree): A tree representation of a query, split into structured nodes instead of raw text.Cypher: A query language for graph databases.OpenCypher: A vendor-neutral Cypher specification.Neo4j: A graph database that extends OpenCypher with additional syntax and features.Dialect: Parser behavior preset (for example, strict OpenCypher vs Neo4j mode).Feature gate: A switch that explicitly allows/disallows specific syntax features.Parse: The process of converting query text into a structured model (AST).Parser: The component that performs parsing and produces diagnostics.Clause: A major query unit such asMATCH,WHERE,RETURN,ORDER BY.Diagnostic: A parser/validator message with code, severity, and source location.Span: The source range (startandend) tied to a node or diagnostic.Offset: Character index in the original query text.Fail-fast: Stop on errors and return no usable document (recoverErrors: false).Recovery mode: Continue parsing after errors to keep partial structure (recoverErrors: true).Canonical formatting: Converting equivalent queries into a consistent output style.
MatchClause(OPTIONAL MATCHincluded)WhereClauseWithClauseReturnClauseOrderByClauseLimitClauseSkipClauseCreateClauseMergeClauseSetClauseRemoveClauseDeleteClauseUnwindClauseCallClauseUnionClause(UNION,UNION ALL)
CYP1xx: syntax/parser errorsCYP2xx: extension/feature-gate violationsCYP3xx: semantic validation errorsCYP9xx: internal parser failures
The library is pure Dart and does not depend on dart:io in lib/.
Typical Flutter flow:
- Bind query input to
TextEditingController. - Parse on text changes (usually with debounce +
recoverErrors: true). - Render
result.diagnosticsin UI. - Render
CypherPrinter.format(result.document!)when parse succeeds.
Sample app: example/flutter_cypher_lab/lib/main.dart
- CLI example:
example/main.dart - Parser tests:
test/parser - Diagnostics tests:
test/diagnostics - Feature-gate tests:
test/extensions - Browser compatibility test:
test/web/web_platform_test.dart
./tool/release_check.shThis runs format, analyze, tests, browser tests (if Chrome exists), docs validation, parser generation, and generated-file sync checks.
ANTLR setup details: tool/antlr/README.md
MIT