Available since: v3.2.0
SurrealDB supports ISO/IEC 39075 GQL for graph pattern matching and data modification over your existing tables and RELATE edges. The surface syntax is closer to Cypher-style MATCH … RETURN than to SurrealQL SELECT, but it runs on the same storage model: node labels map to tables, edge types map to relation tables, and properties map to record fields.
From 3.3.0, ISO GQL is enabled by default on POST /gql, the gql RPC method, and MCP - no experimental capability is required. On 3.2.x, enable it with --allow-experimental gql (or SURREAL_CAPS_ALLOW_EXPERIMENTAL=gql). The surface includes read queries (MATCH … RETURN) and data-modifying statements (INSERT, SET, REMOVE, DELETE) - see GQL mutations.
GQL is not GraphQL
SurrealDB exposes two different graph query languages on separate endpoints. Do not abbreviate GraphQL to gql in product identifiers - the short name gql refers to ISO GQL only.
| GQL (this guide) | GraphQL | |
|---|---|---|
| Standard | ISO/IEC 39075 GQL | GraphQL |
| Syntax | MATCH (a)-[:knows]->(b) RETURN … | query { people { name } } |
| HTTP | POST /gql | POST /graphql |
| WebSocket RPC | method: "gql" | method: "graphql" |
| Setup | On by default from 3.3.0 (experimental gql on 3.2.x) | DEFINE CONFIG GRAPHQL |
| Schema | Tables and RELATE edges you already have | Auto-generated GraphQL schema from your database |
When to use GQL
You already think in graph patterns such as
(a)-[:knows]->(b),SHORTEST, andALL SHORTEST, and have yet to learn how to query such paths in SurrealQL.You are migrating a database from Neo4j to SurrealDB and want to test to ensure that existing Cypher queries map to the same output.
You want a stable graph query surface aligned with the ISO GQL standard.
For general-purpose schema changes, bulk load, and full SurrealQL expressiveness, keep using SurrealQL and the /sql endpoint. For ISO graph-pattern writes (INSERT, SET, REMOVE, DELETE), see GQL mutations.
Wire surfaces
| Surface | How to call |
|---|---|
| HTTP | POST /gql - raw GQL query in the request body |
| WebSocket RPC | { "method": "gql", "params": ["<query>", { "var": value }] } - use for typed $variables |
| Postgres wire | SET dialect = 'gql' (or options=-c dialect=gql at connect) on a Postgres client connection |
| MCP | gql tool (when the server exposes MCP) |
| SurrealQL | eval::gql - nested GQL in the caller's transaction (capability-gated) |
Session headers match /sql: Surreal-NS, Surreal-DB, and authentication. Responses use the same JSON envelope as /sql (status, result, time).
Try from SurrealQL (eval::gql)
To experiment in the REPL without curl or POST /gql, wrap a GQL string in eval::gql. The function runs the same engine as the HTTP endpoint and participates in the caller's transaction (including mutations).
eval::gql needs the eval capability that --allow-all does not enable: --allow-eval-query (denied for every subject by default). From 3.3.0 you do not also need --allow-experimental gql (that flag is still required on 3.2.x).
Embedded REPL (surreal sql against memory or a file path - no separate surreal start):
echo 'eval::gql("MATCH (n:person) RETURN n.name AS name ORDER BY name");' | surreal sql --user root --pass secret --allow-eval-query --pretty --hide-welcomeTo explore interactively instead, open the same REPL without piping:
surreal sql --user root --pass secret --allow-eval-queryRemote server (surreal start + surreal sql -e ws://…): pass --allow-eval-query on surreal start only. The client REPL does not enable eval at runtime.
surreal start --user root --pass secret --allow-eval-queryOptional bindings use an object as the second argument: eval::gql("… WHERE n.age > $min …", { min: 18 }). Full setup, seed data, and more examples can be found on the Eval functions page.
Data model mapping
| GQL | SurrealDB |
|---|---|
(:person) | Rows in table person |
-[k:knows]-> | Rows in relation table knows (in / out record IDs) |
n.name | Field name on the bound record |
$min | Parameter - bind via RPC params object (typed JSON values) |
GQL is not lowered to SurrealQL text - it compiles to an internal match plan and runs on the streaming engine. The sample queries page shows SurrealQL that returns the same shape where a close equivalent exists; some patterns (optional match blocks, path selectors) are much more concise in GQL.
Notable syntax differences from openCypher
--starts a line comment, not an undirected edge.Inequality is
<>, not!=.Label conjunction uses
&(:person&employee), not:person:employee.Variable-length quantifiers are postfix on the edge:
-[e:knows]->{1,3}(b), not*1..3inside the brackets.No
INlist membership operator in this subset.
Design notes, the supported subset, and mutation semantics are documented in the SurrealDB source tree under doc/opengql/ (REFERENCE.md, LOWERING.md). The parser grammar is vendored from the upstream opengql/grammar project; that name refers to the grammar repository, not the ISO standard SurrealDB implements.
Next steps
GQL via HTTP - enable GQL, load data, and call
POST /gqlwith cURL oreval::gqlfrom the REPLPostgres wire protocol - run GQL from
psqlor Postgres drivers withSET dialect = 'gql'GQL mutations -
INSERT,SET,REMOVE,DELETEPOST /gqlHTTP reference - headers, response envelope, and parametersSample GQL and SurrealQL queries - side-by-side examples on the seed graph
Eval functions - run GQL from inside SurrealQL with
eval::gql