GraphQL

ArcadeDB Server supports a subset of the GraphQL specification. Please open an issue or a discussion on GitHub to increase the support for GraphQL.

If you’re using ArcadeDB as embedded, please add the dependency to the arcadedb-graphql library. If you’re using Maven include this dependency in your pom.xml file.

<dependency>
    <groupId>com.arcadedb</groupId>
    <artifactId>arcadedb-graphql</artifactId>
    <version>26.5.1</version>
</dependency>

GraphQL is supported in ArcadeDB as a query language engine. This means you can execute a GraphQL command from:

  • Java API by using the non-idempotent .command() and the idempotent .query() API by using "graphql" as language. Example: Resultset resultset = db.query("graphql", "{ bookById(id: "book-1"){ id name authors { firstName, lastName } }");

  • HTTP API by using /command and /query commands and "graphql" as language

  • Postgres Driver by prefixing with {graphql} your query to execute. Example: {graphql}{ bookById(id: "book-1"){ id name authors { firstName, lastName } }

Type definition

GraphQL requires to define the types used. If you’re using the Document Model and links to connect documents, then you can map 1-1 the GraphQL type to ArcadeDB type. Example:

type Book {
  id: ID
  name: String
  pageCount: Int
  authors: [Author]
}

If you’re using a Graph Model for your domain, then you need to declare with a GraphQL directive how the relationship is translated on the graph model.

In the example below, the authors is a collection of Author retrieved by looking at the incoming (direction: IN) edges of type "IS_AUTHOR_OF" (type: "IS_AUTHOR_OF"):

type Book {
  id: ID
  name: String
  pageCount: Int
  authors: [Author] @relationship(type: "IS_AUTHOR_OF", direction: IN)
}
Directives can be defined on both types and queries. Directives defined in queries override any directives defined in types, only for the query execution context.

You can define your model incrementally and apply it to the current database instance by executing a command containing the type definition. Example by using Java API (but the same by using HTTP Command API):

String types = "type Query {" +
              "  bookById(id: ID): Book" +
              "}" +
              "type Book {" +
              "  id: ID" +
              "  name: String" +
              "  pageCount: Int" +
              "  authors: [Author] @relationship(type: \"IS_AUTHOR_OF\", direction: IN)" +
              "}" +
              "type Author {" +
              "  id: ID" +
              "  firstName: String" +
              "  lastName: String" +
              "}";

database.command("graphql", types);

With this example the types Book and Author are defined together with the query bookById. You can add new types or replace existing types by just submitting the type(s) again. The GraphQL module will update the current definition of types.

This definition is not saved in the database and must be declared after the database is open, before executing any GraphQL queries.

Query arguments

Query fields can declare arguments of the standard GraphQL scalar types: ID, String, Int, Float and Boolean. Example:

type Query {
  books(inStock: Boolean, rating: Float): [Book]
}

type Book {
  id: ID
  name: String
  inStock: Boolean
  rating: Float
}
ResultSet resultSet = database.query("graphql", "{ books(inStock: true, rating: 4.5) { name } }");

A query field can also be declared without any argument, for example to list every record of a type:

type Query {
  allBooks: [Book]
}

Argument values are passed to the database as parameters, never written into the generated query text, so a value containing quotes or backslashes is always treated as data. The standard GraphQL escape sequences (\", \\, \/, \b, \f, \n, \r, \t and \uXXXX) are decoded, so this matches a book whose name really contains a line break:

{ bookByName(name: "Line one\nLine two") { id } }

The where argument

An argument named where is reserved: its value is used as the SQL condition of the generated query instead of being compared against a property.

type Query {
  books(where: WHERE): [Book]
}
ResultSet resultSet = database.query("graphql", "{ books( where: \"name = 'Mr. brain'\" ) { name } }");
The where value is SQL and is used exactly as written, so it must be authored in the query, never built from end-user input. For this reason it cannot be filled from a variable. If your condition has to change at runtime, use a @sql directive with a parameter instead.

Query variables

A query can declare variables and receive their values at execution time, so the same query text can be reused with different values:

query($name: String) {
  bookByName(name: $name) { id name }
}
ResultSet resultSet = database.query("graphql", "query($name: String) { bookByName(name: $name) { id name } }",
                                     "name", "Harry Potter and the Philosopher's Stone");

From the HTTP API the variables are the request parameters, and from the Postgres Driver they are the query parameters.

A variable can declare a default value, used when no value is passed:

query($name: String = "Mr. brain") { bookByName(name: $name) { id } }

Variables can also be used in the arguments of a directive written in the query:

query($edge: String) { bookById(id: "book-1") { authors @relationship(type: $edge, direction: IN) { lastName } } }

The value passed must match the type the variable is declared with, otherwise the query is rejected:

  • Int accepts a whole number in the 32-bit range

  • Float accepts any number, but not NaN or infinity

  • Boolean accepts only a boolean, String only a string

  • ID accepts a string or a whole number of any size

  • any other type name (a custom scalar, an enum, a list) is passed through unchecked

A variable declared with ! (for example $name: String!) must receive a value, and that value cannot be null. A variable used but never declared is an error, as is a variable used for the where argument.

Commas between variable declarations are not supported yet: write query($a: String $b: Int), not query($a: String, $b: Int).

Field aliases

A selection can rename a field with the standard GraphQL alias syntax alias: field. The alias becomes the key of the field in the result, while the underlying field is resolved normally.

Example:

{ bookById(id: "book-1")
  {
    title: name
    authors {
      first: firstName
      lastName
    }
  }
}

This returns title (instead of name) and first (instead of firstName) as the result keys.

The top-level query field itself can also be aliased, including when it takes arguments:

{ myBooks: books(inStock: true) { name } }

Supported directives

Directives can be defined on both types and queries. Directives defined in queries override any directives defined in types, only for the query execution context.

@relationship

Applies to: Query Field and Field Definition

Syntax: @relationship([type: "<type-name>"] [, direction: <OUT|IN|BOTH>])

Where:

  • type is the edge type, optional. If not specified, then all the types are considered

  • direction is the direction of the edge, optional. If not specified, then BOTH is used

Example:

friends: [Account] @relationship(type: "FRIEND", direction: BOTH)

Relationships can be traversed at any depth, each level being resolved against its own type. The query below walks from a book to its authors, then back to every book each author wrote:

{ bookById(id: "book-1")
  {
    name
    authors {
      lastName
      wrote { name }
    }
  }
}

@sql

Applies to: Query Field and Field Definition

Syntax: @sql( statement: <sql-statement> )

Executes a SQL query. The query can use parameters passed at invocation time.

Example of definition of a query using SQL in GraphQL:

bookByName(bookNameParameter: String): Book @sql(statement: "select from Book where name = :bookNameParameter")

Invoke the query defined above passing the book name as parameter:

ResultSet resultSet = database.query("graphql", "{ bookByName(bookNameParameter: \"Harry Potter and the Philosopher's Stone\")}"));

@gremlin

Applies to: Query Field and Field Definition

Syntax: @gremlin( statement: <gremlin-statement> )

Executes a Gremlin query. The query can use parameters passed at invocation time.

Example of definition of a query using Gremlin in GraphQL:

bookByName(bookNameParameter: String): Book @gremlin(statement: "g.V().has('name', bookNameParameter)")

Invoke the query defined above passing the book name as parameter:

ResultSet resultSet = database.query("graphql", "{ bookByName(bookNameParameter: \"Harry Potter and the Philosopher's Stone\")}"));

@cypher

Applies to: Query Field and Field Definition

Syntax: @cypher( statement: <cypher-statement> )

Executes a Cypher query. The query can use parameters passed at invocation time.

Example of definition of a query using Cypher in GraphQL:

bookByName(bookNameParameter: String): Book @cypher(statement: "MATCH (b:Book {name: $bookNameParameter}) RETURN b")

Invoke the query defined above passing the book name as parameter:

ResultSet resultSet = database.query("graphql", "{ bookByName(bookNameParameter: \"Harry Potter and the Philosopher's Stone\")}"));

@rid

Applies to: Query Field and Field Definition

Syntax: @rid

Mark the field as the record identity or concepts/basics.adoc#rid.

Example:

{ bookById(id: "book-1")
  {
    rid @rid
    id
    name
    authors {
      firstName
      lastName
    }
  }
}