Database mapping

This page describes how to use directives for database mapping. Each type in your GraphQL type definitions can be mapped to an entity in your Neo4j database, such as nodes, relationships, and relationship properties.

@relationship

Relationships are represented by marking particular fields with a directive — in this case, @relationship. It defines the relationship type in the database, as well as which direction that relationship goes in.

To add a second node type, "Actor", and connect the two together, you should do the following:

type Movie {
    title: String
    actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN)
}

type Actor {
    name: String
    movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}

Note that, in this case, there is a directive on each "end" of the relationship, but it is not essential.

@relationshipProperties

Definition

"""Required to differentiate between interfaces for relationship properties, and otherwise."""
directive @relationshipProperties on OBJECT

@relationshipProperties can only be used on interfaces.

Usage

In order to add properties to a relationship, add a new type to your type definitions decorated with the @relationshipProperties directive.

For example, for the "ACTED_IN" relationship, add a property "roles":

type Movie {
    title: String
    actors: [Actor!]! @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn")
}

type Actor {
    name: String
    movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, properties: "ActedIn")
}

type ActedIn @relationshipProperties {
    roles: [String]
}

Note that in addition to this type, there is an added key properties in the existing @relationship directives. For more information, see Type definitions → Relationships.

@node

The most basic mapping uses GraphQL type names to map to a Neo4j node label. For example, to represent a node with the label "Movie" and a single property "title" of type string:

type Movie {
    title: String
}

With the @node directive, you have more control over this mapping, and you can specify the configuration of a GraphQL object type which represents a Neo4j node. It can be appended with the following optional parameters:

labels

This parameter defines the list of label to be used in Neo4j instead of the GraphQL type name:

type Dog @node(labels: ["K9"]) {
    name: String!
}

This way, the following query:

{
  dogs {
    name
  }
}

Generates the Cypher query:

MATCH (this: K9)
RETURN this { .name } as name

If the GraphQL type name should still be used as a label, it needs to be specified as well:

type Dog @node(labels: ["Dog", "K9"]) {
    name: String!
}

This way, the following query:

{
  dogs {
    name
  }
}

Generates the Cypher query:

MATCH (this:Dog:K9)
RETURN this { .name } as this

Defining labels means you take control of the database labels of the node. Indexes and constraints in Neo4j only support a single label, for which the first element of the labels argument is used.

The following example results in a unique constraint to be asserted for the label K9 and the property name:

type Dog @node(labels: ["K9", "Dog"]) {
    name: String! @unique
}

Using $jwt and $context

In some cases, you may want to generate dynamic labels depending on the user requesting. For that, you can use the variable $jwt to define a custom label in the JWT:

type User @node(labels: ["$jwt.username"]) {
    name: String!
}

The following query yields a different Cypher query depending on the user JWT:

{
  users {
    name
  }
}ref::/directives/database-mapping.adoc#_declarerelationship[
type User @node(label: ["$context.appId"]) {
    name: String!
}

When running the server with Apollo:

const server = new ApolloServer({
    schema: await neoSchema.getSchema(),
});

await startStandaloneServer(server, {
    context: async ({ req }) => ({ req, appId: "myApp" }),
});

@alias

This directive maps a GraphQL field to a Neo4j property on a node or relationship. It can be used on any fields that are not @cypher or @relationship fields.

For example:

type User {
    id: ID! @id @alias(property: "dbId")
    username: String!
}
type User {
    id: ID! @id
    username: String! @alias(property: "dbUserName")
    livesIn: [City!]! @relationship(direction: OUT, type: "LIVES_IN", properties: "UserLivesInProperties")
}

type City {
    name: String
}

type UserLivesInProperties @relationshipProperties {
    since: DateTime @alias(property: "moveInDate")
}
The property in aliases are automatically escaped (wrapped with backticks ``), so there is no need to add escape characters around them.