# Overview

Query SurrealDB graph data with ISO GQL - a Cypher-like graph pattern language over HTTP and RPC.

_(since v3.2.0)_

SurrealDB supports **[ISO/IEC 39075 GQL](https://www.iso.org/standard/76120.html)** 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.

> [!NOTE]
> 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](/docs/learn/querying/gql/mutations.md).

## GQL is not GraphQL

SurrealDB exposes two different graph query languages on **separate** endpoints. Do not abbreviate [GraphQL](/docs/learn/querying/graphql/overview.md) to `gql` in product identifiers - the short name **`gql`** refers to ISO GQL only.

| | **GQL** (this guide) | **[GraphQL](/docs/learn/querying/graphql/overview.md)** |
| --- | --- | --- |
| Standard | [ISO/IEC 39075 GQL](https://www.iso.org/standard/76120.html) | [GraphQL](https://graphql.org/) |
| Syntax | `MATCH (a)-[:knows]->(b) RETURN …` | `query { people { name } }` |
| HTTP | [`POST /gql`](/docs/reference/rest-api/http-protocol.md#gql) | [`POST /graphql`](/docs/reference/rest-api/http-protocol.md#graphql) |
| WebSocket RPC | `method: "gql"` | `method: "graphql"` |
| Setup | On by default from 3.3.0 (experimental `gql` on 3.2.x) | [`DEFINE CONFIG GRAPHQL`](/docs/reference/query-language/statements/define/config.md) |
| 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`, and `ALL 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](/docs/reference/query-language.md) and the [`/sql`](/docs/reference/rest-api/http-protocol.md) endpoint. For ISO graph-pattern **writes** (`INSERT`, `SET`, `REMOVE`, `DELETE`), see [GQL mutations](/docs/learn/querying/gql/mutations.md).

## Wire surfaces

| Surface | How to call |
| --- | --- |
| **HTTP** | [`POST /gql`](/docs/reference/rest-api/http-protocol.md#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'`](/docs/reference/rest-api/postgres-protocol.md) (or `options=-c dialect=gql` at connect) on a Postgres client connection |
| **MCP** | `gql` tool (when the server exposes MCP) |
| **SurrealQL** | [`eval::gql`](/docs/reference/query-language/functions/database-functions/eval.md#evalgql) - 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`](/docs/reference/query-language/functions/database-functions/eval.md#evalgql). The function runs the same engine as the HTTP endpoint and participates in the caller's transaction (including [mutations](/docs/learn/querying/gql/mutations.md)).

`eval::gql` needs the **`eval`** capability that `--allow-all` does not enable: [`--allow-eval-query`](/docs/learn/security/authorization/capabilities.md#eval-queries) (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`):

**Bash**

```bash
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-welcome
```

**PowerShell**

```powershell
'eval::gql("MATCH (n:person) RETURN n.name AS name ORDER BY name");' | surreal sql --user root --pass secret --allow-eval-query --pretty --hide-welcome
```

To explore interactively instead, open the same REPL without piping:

```bash
surreal sql --user root --pass secret --allow-eval-query
```

**Remote server** (`surreal start` + `surreal sql -e ws://…`): pass `--allow-eval-query` on **`surreal start`** only. The client REPL does not enable `eval` at runtime.

```bash
surreal start --user root --pass secret --allow-eval-query
```

Optional 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](/docs/reference/query-language/functions/database-functions/eval.md#evalgql) 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](/docs/learn/querying/gql/sample-queries.md) 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..3` inside the brackets.
- No `IN` list 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](https://github.com/opengql/grammar) project; that name refers to the grammar repository, not the ISO standard SurrealDB implements.

## Next steps

- [GQL via HTTP](/docs/learn/querying/gql/via-http.md) - enable GQL, load data, and call `POST /gql` with cURL or `eval::gql` from the REPL
- [Postgres wire protocol](/docs/reference/rest-api/postgres-protocol.md) - run GQL from `psql` or Postgres drivers with `SET dialect = 'gql'`
- [GQL mutations](/docs/learn/querying/gql/mutations.md) - `INSERT`, `SET`, `REMOVE`, `DELETE`
- [`POST /gql` HTTP reference](/docs/reference/rest-api/http-protocol.md#gql) - headers, response envelope, and parameters
- [Sample GQL and SurrealQL queries](/docs/learn/querying/gql/sample-queries.md) - side-by-side examples on the seed graph
- [Eval functions](/docs/reference/query-language/functions/database-functions/eval.md) - run GQL from inside SurrealQL with `eval::gql`
