Database mapping

This page describes how to use directives for database mapping.

@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")
}

Note that the property in aliases are automatically escaped (wrapped with backticks ``), so there is no need to add escape characters around them.

@plural

This directive redefines how to compose the plural of the type for the generated operations. This is particularly useful for types that are not correctly pluralized or are non-English words. Take this type definition as an example:

type Tech @plural(value: "Techs") {
  name: String
}

This way, instead of the wrongly generated teches, the type is properly written as techs:

{
  techs {
    title
  }
}

The same is applied to other operations such as createTechs. However, keep in mind that database labels are not changed with this directive.

@node

This directive is used to 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
  }
}

Assuming there is a user with the value "username": "arthur" in JWT, the Cypher query looks like:

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

Similarly, context values can be passed directly:

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" }),
});