GraphQL compiler

GraphQL compiler is a library that simplifies data querying and exploration by exposing one simple query language to target multiple database backends. The query language is:

Written in valid GraphQL syntax
Since it uses GraphQL syntax, the user get access to the entire GraphQL ecosystem, including the typeahead capabilities and query validation capabilities of GraphiQL, user friendly error messages from the reference GraphQL python implementation, and more.
Directly compiled to the target database language
By compiling instead of interpreting the query language, the compiler highly improves query performance and empowers the user with the ability to write deep and complex queries. Furthermore, by using schema information from the target database, the compiler is able to extensively validate queries, often more so than the DB-API, (e.g. pymssql).

Getting Started

Generating the necessary schema info

To use GraphQL compiler the first thing one needs to do is to generate the schema info from the underlying database as in the example below. Even though the example targets an OrientDB database, it is meant as a generic schema info generation example. See the homepage of your target database for more instructions on how to generate the necessary schema info.

from graphql_compiler import (
    get_graphql_schema_from_orientdb_schema_data
)
from graphql_compiler.schema_generation.orientdb.utils import ORIENTDB_SCHEMA_RECORDS_QUERY

client = your_function_that_returns_a_pyorient_client()
schema_records = client.command(ORIENTDB_SCHEMA_RECORDS_QUERY)
schema_data = [record.oRecordData for record in schema_records]
schema, type_equivalence_hints = get_graphql_schema_from_orientdb_schema_data(schema_data)

In the snippet above the are two pieces of schema info:

  • schema which represents the database using GraphQL’s type system.
  • type_equivalence_hints which helps deal with GraphQL’s lack of concrete inheritance, (see schema types for more info).

When compiling, these will need to be bundled in a CommonSchemaInfo object.

Besides representing the database schema, a GraphQL schema includes other metadata such as a list of custom scalar types used by the compiler. We’ll talk more about this metadata in schema types. For now let’s focus on how a database schema might be represented in a GraphQL schema:

type Animal {
    name: String
    out_Animal_LivesIn: [Continent]
}

type Continent {
    name: String
    in_AnimalLivesIn: [Animal]
}

In the GraphQL schema above:

  • Animal represents a concrete, (non-abstract), vertex type. For relational databases, we think of tables as the concrete vertex types.
  • name is a property field which represents a property of the Animal vertex type. Think of property fields as leaf fields that represent concrete data.
  • out_Animal_LivesIn is a vertex field which represents an outbound edge to a vertex type in the graph. For graph databases, edges can be automatically generated from the database schema. However, for relational databases, edges currently have to be manually specified. See SQL for more information.

Query Compilation and Execution

Once we have the schema info we can write the following query to get the names of all the animals that live in Africa:

graphql_query = """
{
    Animal {
        name @output(out_name: "animal_name")
        out_Animal_LivesIn {
            name @filter(op_name: "=", value: ["$continent"])
        }
    }
}
"""
parameters = {'continent': 'Africa'}

There are a couple of things to notice about queries:

  • All queries start with a vertex type, (e.g. Animal), and expand to other vertex types using vertex fields.
  • Directives specify the semantics of a query. @output indicates the properties whose values should be returned. @filter specifies a filter operation.

Finally, with the GraphQL query and its parameters at hand, we can use the compiler to obtain a query that we can directly execute against OrientDB.

from graphql_compiler import graphql_to_match

compilation_result = graphql_to_match(
    schema, graphql_query, parameters, type_equivalence_hints)

# Executing query assuming a pyorient client. Other clients may have a different interface.
print([result.oRecordData for result in client.query(query)])
# [{'animal_name': 'Elephant'}, {'animal_name': 'Lion'}, ...]

Features

Language Specification

To learn more about the language specification see:

  • Definitions, for the definitions of key terms that we use to define the language.
  • Schema Types, for information about the full breadth of schema types that we use to represent database schemas and how to interact with them using GraphQL queries.
  • Query Directives, to learn more about the available directives and how to use them to create powerful queries.

Supported Databases

Refer to this section to learn how the compiler integrates with the target database. The database home pages include an end-to-end example, instruction for schema info generation, and any limitations or intricacies related to working with said database. We currently support two types of database backends:

Advanced Features

To learn more about the advanced features in the GraphQL compiler see:

  • Macro System to learn how to write “macro edges”, which allow users to define new edges that become part of the GraphQL schema, using existing edges as building blocks.
  • Schema Graph for an utility that makes it easy to explore the schema of a database, including the databases indexes.
  • Additional Tools for a list of additional tools included in the package, including a query pretty printer.

About GraphQL compiler

To learn more about the GraphQL compiler project see:

  • Contributing for instructions on how you can contribute.
  • Code of Conduct for the contributor code of conduct.
  • Changelog for a history of changes.
  • FAQ for a list of frequently asked questions.
  • Execution Model to learn more about the design principles guiding the development of the compiler and the guarantees the compiler provides.