HyperGraphQL logo

A GraphQL interface for querying and serving linked data on the Web.

Home
Documentation
Demo
Tutorial

Overview

HyperGraphQL is a GraphQL interface for querying and serving linked data on the Web. It serves several key objectives and application scenarios:

To facilitate exploration, each HyperGraphQL instance is by default accompanied by a GraphiQL interface:

HyperGraphQL-screenshot

The core response object of HyperGraphQL is JSON-LD (embedded in the standard GraphQL response), which extends JSON with a JSON-LD context enabling semantic disambiguation of the served data. Support for other RDF serialisation formats is also provided.


Running

Clone the Git repository into a local directory. In the root of the project execute the following:

Maven:

  1. mvn install
  2. mvn exec:java

(Note: in Windows these must be executed in a cmd terminal, not PowerShell).

Gradle:

  1. gradle build
  2. gradle execute

By deafault, the HyperGraphQL server starts at:

http://localhost:8080/graphql

The GraphiQL UI is initiated at:

http://localhost:8080/graphiql


Example

The following query requests a single person instance with its URI (_id) and RDF type (_type), its name, birth date, birth place with its URI, an English label, and the country in which it is located, also including its URI and an English label.

HyperGraphQL query

{
  Person_GET(limit: 1, offset: 6) {
    _id
    _type
    name
    birthDate
    birthPlace {
      _id
      label(lang: "en")
      country {
        _id
        label(lang: "en")
      }
    }
  }
}

The response to this query consists of the usual GraphQL JSON object, further augmented with a JSON-LD context included as the value of the property @context in the data object.

HyperGraphQL response

{
  "extensions": {},
  "data": {
    "Person_GET": [
      {
        "_id": "http://dbpedia.org/resource/Sani_ol_molk",
        "_type": "http://dbpedia.org/ontology/Person",
        "name": "Mirza Abolhassan Khan Ghaffari",
        "birthDate": "1814-1-1",
        "birthPlace": {
          "_id": "http://dbpedia.org/resource/Kashan",
          "label": [
            "Kashan"
          ],
          "country": {
            "_id": "http://dbpedia.org/resource/Iran",
            "label": [
              "Iran"
            ]
          }
        }
      }
    ],
    "@context": {
      "birthPlace": "http://dbpedia.org/ontology/birthPlace",
      "country": "http://dbpedia.org/ontology/country",
      "_type": "@type",
      "name": "http://xmlns.com/foaf/0.1/name",
      "_id": "@id",
      "label": "http://www.w3.org/2000/01/rdf-schema#label",
      "people": "http://hypergraphql.org/query/Person_GET",
      "birthDate": "http://dbpedia.org/ontology/birthDate"
    }
  },
  "errors": []
}

It is easy to find out, using e.g. JSON-LD playground, that the data node in this response is in fact a valid JSON-LD object encoding the following RDF graph (in N-TRIPLE notation):

_:b0 <http://hypergraphql.org/query/Person_GET> <http://dbpedia.org/resource/Sani_ol_molk> .
<http://dbpedia.org/resource/Sani_ol_molk> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://dbpedia.org/ontology/Person> .
<http://dbpedia.org/resource/Sani_ol_molk> <http://xmlns.com/foaf/0.1/name> "Mirza Abolhassan Khan Ghaffari" .
<http://dbpedia.org/resource/Sani_ol_molk> <http://dbpedia.org/ontology/birthDate> "1814-1-1" .
<http://dbpedia.org/resource/Sani_ol_molk> <http://dbpedia.org/ontology/birthPlace> <http://dbpedia.org/resource/Kashan> .
<http://dbpedia.org/resource/Kashan> <http://www.w3.org/2000/01/rdf-schema#label> "Kashan" .
<http://dbpedia.org/resource/Kashan> <http://dbpedia.org/ontology/country> <http://dbpedia.org/resource/Iran> .
<http://dbpedia.org/resource/Iran> <http://www.w3.org/2000/01/rdf-schema#label> "Iran" .

This graph (except for the first triple, added by HyperGraphQL) is a subset of the DBpedia dataset.


HyperGraphQL instance = configuration + annotated GraphQL schema

To set up a HyperGraphQL instance you only need to provide the top-level configuration, including specification of the linked data services from which the data is to be fetched, and a GraphQL schema. The schema must be annotated with the URIs associated with every field and type in the schema, and with the pointers to the respective linked data services. The complete GraphQL wiring is done automatically on initiating the instance.

Service configuration

The configuration of an instance is defined in config.json file, located in the root directory of the repository. For example, the following configuration describes an instance pointing at DBpedia’s SPARQL endpoint:

{
    "name": "dbpedia-sparql-hgql-demo",
    "schema": "schema.graphql",
    "server": {
        "port": 8080,
        "graphql": "/graphql",
        "graphiql": "/graphiql"
    },
    "services": [
        {
            "id": "dbpedia-sparql",
            "type": "SPARQLEndpointService",
            "url": "http://dbpedia.org/sparql/",
            "graph": "http://dbpedia.org",
            "user": "",
            "password": ""
        }
    ]
}

The meaning of the JSON properties is as follows:

Currently, HyperGraphQL supports three types of linked data services: (remote) SPARQL endpoints (SPARQLEndpointService), local RDF files (LocalModelSPARQLService) and (remote) HyperGraphQL instances (HGraphQLService).

Remote SPARQL endpoints

{
    "id": "dbpedia-sparql",
    "type": "SPARQLEndpointService",
    "url": "http://dbpedia.org/sparql/",
    "graph": "http://dbpedia.org",
    "user": "",
    "password": ""
}

Local RDF files

{
    "id": "agrovoc-local",
    "type": "LocalModelSPARQLService",
    "filepath": "agrovoc.ttl",
    "filetype": "TTL"
}

Remote HyperGraphQL instances

{
    "id": "agrovoc-hgql",
    "type": "HGraphQLService",
    "url": "http://localhost:8082/graphql"
}

Schema

The schema definition complies with the syntax of the GraphQL schema spec. However, it is required to satisfy a number of structural constraints. The following is an example of a valid HyperGraphQL schema aimed to work over a DBpedia SPARQL endpoint.

type __Context {
    Person:         _@href(iri: "http://dbpedia.org/ontology/Person")
    Company:        _@href(iri: "http://dbpedia.org/ontology/Company")
    City:           _@href(iri: "http://dbpedia.org/ontology/City")
    Country:        _@href(iri: "http://dbpedia.org/ontology/Country")
    name:           _@href(iri: "http://xmlns.com/foaf/0.1/name")
    label:          _@href(iri: "http://www.w3.org/2000/01/rdf-schema#label")
    birthPlace:     _@href(iri: "http://dbpedia.org/ontology/birthPlace")
    birthDate:      _@href(iri: "http://dbpedia.org/ontology/birthDate")
    locationCity:   _@href(iri: "http://dbpedia.org/ontology/locationCity")
    owner:          _@href(iri: "http://dbpedia.org/ontology/owner")
    country:        _@href(iri: "http://dbpedia.org/ontology/country")
    leader:         _@href(iri: "http://dbpedia.org/ontology/leaderName")
    country:        _@href(iri: "http://dbpedia.org/ontology/country")
    capital:        _@href(iri: "http://dbpedia.org/ontology/capital")
}

type Person @service(id:"dbpedia-sparql") {
    name: String @service(id:"dbpedia-sparql")
    label: [String] @service(id:"dbpedia-sparql")
    birthPlace: City @service(id:"dbpedia-sparql")
    birthDate: String @service(id:"dbpedia-sparql")
}

type Company @service(id:"dbpedia-sparql") {
    name: [String] @service(id:"dbpedia-sparql")
    label: [String] @service(id:"dbpedia-sparql")
    locationCity: City @service(id:"dbpedia-sparql")
    owner: [Person] @service(id:"dbpedia-sparql")
}

type City @service(id:"dbpedia-sparql") {
    label: [String] @service(id:"dbpedia-sparql")
    country: Country @service(id:"dbpedia-sparql")
    leader: Person @service(id:"dbpedia-sparql")
}

type Country @service(id:"dbpedia-sparql") {
    label: [String] @service(id:"dbpedia-sparql")
    capital: City @service(id:"dbpedia-sparql")
}

A HyperGraphQL schema must contain a designated type called __Context, which encodes the mapping from every object type and every field to a corresponding IRI used in the target services. For instance, the following line from the schema above informs the instance that the field name corresponds to the IRI http://xmlns.com/foaf/0.1/name, and therefore this IRI will be used when fetching the values for the field name from designated services.

name:      _@href(iri: "http://xmlns.com/foaf/0.1/name")

Note that each type/field must be mapped to a unique such IRI. Intuitively, GraphQL types should be mapped to IRIs representing RDF/OWL classes, while fields to OWL data properties (whenever the values of the field are scalars, e.g., String, Int, Boolean, ID) and OWL object properties (whenever the values of the field are IRI resources).

Furthermore, types and fields in the schema are annotated with the directives of the form:

@service(id:"dbpedia-sparql")

which inform the instance from which service the data for given type/field should be fetched. The id must correspond to those declared in the services section of the configuration file. Currently, only one service can be associated with each type and field. Types annotated with a service id are automatically made quereable, i.e., the HyperGraphQL instance automatically exposes two query fields named: TypeName_GET and TypeName_GET_BY_ID, e.g.: Person_GET and Person_GET_BY_ID. Query fields of type *_GET are parametrised with arguments limit:Int and offset:Int, while those of type *_GET_BY_ID with the argument uris:[String] (see: the Demo section for examples). Annotation of selected types in the schema (and only types) can be skipped. In such cases the type will not become quereable.

The Query type of the type is generated automatically by the service. Additional fields and arguments that are introduced automatically are:


Request and response formats

HyperGraphQL accepts POST requests with the body of the form:

{
  "query":"{ City_GET(limit:10) {  label(lang:\"en\") _id  } }"
}

The default content type is application/json for an accept header application/json. In such case the response fromat is a standard GraphQL, JSON-based response that includes a JSON-LD object as a value of the data property, e.g.:

{
  "extensions": {},
  "data": {
    "Person_GET": [
      {
        "_id": "http://dbpedia.org/resource/Sani_ol_molk",
        "_type": "http://dbpedia.org/ontology/Person",
        "name": "Mirza Abolhassan Khan Ghaffari",
        "birthDate": "1814-1-1",
        "birthPlace": {
          "_id": "http://dbpedia.org/resource/Kashan",
          "label": [
            "Kashan"
          ],
          "country": {
            "_id": "http://dbpedia.org/resource/Iran",
            "label": [
              "Iran"
            ]
          }
        }
      }
    ],
    "@context": {
      "birthPlace": "http://dbpedia.org/ontology/birthPlace",
      "country": "http://dbpedia.org/ontology/country",
      "_type": "@type",
      "name": "http://xmlns.com/foaf/0.1/name",
      "_id": "@id",
      "label": "http://www.w3.org/2000/01/rdf-schema#label",
      "people": "http://hypergraphql.org/query/Person_GET",
      "birthDate": "http://dbpedia.org/ontology/birthDate"
    }
  },
  "errors": []
}

Other supported content types enable replacing the data object of the response above with a string of RDF data in selected serialisation format, or serving the entire response purely as RDF data.

GraphQL-compatible content types

For instance, the response above in application/json+rdf+xml is served as:

{
    "extensions": {},
    "data": "<rdf:RDF\n    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n    xmlns:j.0=\"http://hypergraphql.org/query/\"\n    xmlns:j.1=\"http://dbpedia.org/ontology/\"\n    xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\"\n    xmlns:j.2=\"http://xmlns.com/foaf/0.1/\">\n  <rdf:Description rdf:about=\"http://hypergraphql.org/query\">\n    <j.0:Person_GET>\n      <j.1:Person rdf:about=\"http://dbpedia.org/resource/Sani_ol_molk\">\n        <j.1:birthDate rdf:datatype=\"http://www.w3.org/2001/XMLSchema#date\"\n        >1814-1-1</j.1:birthDate>\n        <j.1:birthPlace>\n          <j.1:City rdf:about=\"http://dbpedia.org/resource/Kashan\">\n            <j.1:country>\n              <j.1:Country rdf:about=\"http://dbpedia.org/resource/Iran\">\n                <rdfs:label xml:lang=\"en\">Iran</rdfs:label>\n              </j.1:Country>\n            </j.1:country>\n            <rdfs:label xml:lang=\"en\">Kashan</rdfs:label>\n          </j.1:City>\n        </j.1:birthPlace>\n        <j.2:name xml:lang=\"en\">Mirza Abolhassan Khan Ghaffari</j.2:name>\n      </j.1:Person>\n    </j.0:Person_GET>\n  </rdf:Description>\n</rdf:RDF>\n",
    "errors": []
}

Pure RDF content types

For instance, the response above in application/rdf+xml is served as:

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:j.0="http://hypergraphql.org/query/"
    xmlns:j.1="http://dbpedia.org/ontology/"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:j.2="http://xmlns.com/foaf/0.1/">
    <rdf:Description rdf:about="http://hypergraphql.org/query">
        <j.0:Person_GET>
            <j.1:Person rdf:about="http://dbpedia.org/resource/Sani_ol_molk">
                <j.1:birthPlace>
                    <j.1:City rdf:about="http://dbpedia.org/resource/Kashan">
                        <j.1:country>
                            <j.1:Country rdf:about="http://dbpedia.org/resource/Iran">
                                <rdfs:label xml:lang="en">Iran</rdfs:label>
                            </j.1:Country>
                        </j.1:country>
                        <rdfs:label xml:lang="en">Kashan</rdfs:label>
                    </j.1:City>
                </j.1:birthPlace>
                <j.2:name xml:lang="en">Mirza Abolhassan Khan Ghaffari</j.2:name>
                <j.1:birthDate rdf:datatype="http://www.w3.org/2001/XMLSchema#date"
        >1814-1-1</j.1:birthDate>
            </j.1:Person>
        </j.0:Person_GET>
    </rdf:Description>
</rdf:RDF>


Data fetching and query execution

By principle, HyperGraphQL aims to make as few roundtrips between the local instance and the remote services as possible. On posting the query, the query is partitioned into maximal subqueries that can be executed independently over respective remote services. After the execution, once the entire relevant data is brought into the local instance, it is then processed using the standard GraphQL fetchers, running locally, in order to construct the final response.

Depending on the type of the target service the query is either rewritten to SPARQL (remote SPARQL endpoints and local RDF files) or to GraphQL (remote HyperGraphQL instances).

Rewritting to SPARQL

All top-level query fields are rewritten into instance subqueries as follows:

{
  PERSON_GET(limit:1, offset:6) {
    ...
  }
}

is rewritten into:

{
  SELECT ?subject 
  WHERE {
    ?subject a <http://dbpedia.org/ontology/Person> .
  } LIMIT 1 OFFSET 6
}
...
{
  PERSON_GET_BY_ID(uris:["http://dbpedia.org/resource/Sani_ol_molk"]) {
    ...
  }
}

is rewritten into:

  VALUES ?subject { <http://dbpedia.org/resource/Sani_ol_molk> }
  {
    ?subject a <http://dbpedia.org/ontology/Person> .
    ...
  }

Fields are translated into optional SPARQL triple patterns, additionally filtered with the RDF type associated with the output type of the field, for instance:

{
  ... 
  {
    birthplace 
    ...
  }
}

is rewritten:

...
  OPTIONAL {
      ?subject <http://dbpedia.org/ontology/birthPlace> ?object .
      ?object a <http://dbpedia.org/ontology/City> .
      ...
  }

Rewritting to GraphQL

Currently, the rewrting to GraphQL is conducted under the assumption that the schemas of the local and the remote HyperGraphQL instances are aligned, meaning that types/fields with the same lables are mapped to the same IRIs, and the output types are associated with the same fields in both schemas. This assumption will be relaxed in the future versions of HyperGraphQL.


Accessing the schema

Similarly to GraphQL, HyperGraphQL supports introspection queries, following the exact same GraphQL syntax (see: Demo section for an example). Additionally, it also exposes its own, internal representation of the schema in the RDF format, which captures also the mapping of types/fields to IRIs and respective services. This representation can be accessed by making a GET request to the HyperGraphQL server endpoint with either of the following accept headers:

In the future versions, this representation will also be made available via suitable introspection queries.


Other references