@cypher

The @cypher directive binds a GraphQL field to the results of a Cypher query. This directive can be used both for properties in a type or as top level queries.

Global variables

Global variables are available for use within the Cypher statement and can be applied to the @cypher directive.

Variable Description Example

this

This value is a reference to the currently resolved node, and it can be used to traverse the graph.

{
  Movie {
    title
    actors: ACTED_IN @this {
      role
      actor {
        name
      }
    }
    directors: DIRECTED @this {
      director {
        name
      }
    }
  }
}

auth

This value is represented by the following TypeScript interface definition:

interface Auth {
    isAuthenticated: boolean;
    roles?: string[];
    jwt: any;
}

You can use the JWT in the request to return the value of the currently logged in User:

type User {
    id: String
}

type Query {
    me: User @cypher(
        statement: """
        MATCH (user:User {id: $jwt.sub})
        RETURN user
        """,
        columnName: "user"
    )
}

cypherParams

Use it to inject values into the Cypher query from the GraphQL context function.

Inject into context:

const server = new ApolloServer({
    typeDefs,
});

await startStandaloneServer(server, {
    context: async ({ req }) => ({ cypherParams: { userId: "user-id-01" } }),
});

Use in Cypher query:

type Query {
    userPosts: [Post] @cypher(statement: """
        MATCH (:User {id: $userId})-[:POSTED]->(p:Post)
        RETURN p
    """, columnName: "p")
}

Return values

The return value of Cypher statements must always be of the same type to which the directive is applied.

The variable must also be aliased with a name that is the same as the one passed to columnName. This can be the name of a node, relationship query or an alias in the RETURN statement of the Cypher statement.

Scalar values

Cypher statements must return a value which matches the scalar type to which the directive was applied. For example:

type Query {
    randomNumber: Int @cypher(statement: "RETURN rand() as result", columnName: "result")
}

Object types

When returning an object type, all fields of the type must be available in the Cypher return value. This can be achieved by either returning the entire object from the Cypher query, or returning a map of the fields which are required for the object type. Both approaches are demonstrated below:

type User {
    id
}

type Query {
    users: [User]
        @cypher(
            statement: """
            MATCH (u:User)
            RETURN u
            """,
            columnName: "u"
        )
}
type User {
    id
}

type Query {
    users: [User] @cypher(statement: """
        MATCH (u:User)
        RETURN {
            id: u.id
        } as result
    """, columnName: "result")
}

The downside of the latter approach is that you need to adjust the return object as you change your object type definition.

Input arguments

The @cypher statement can access the query parameters by prepending $ to the parameter name. For example:

type Query {
    name(value: String): String @cypher(statement: "RETURN $value AS res", columnName: "res")
}

The following GraphQL query returns the parameter value:

query {
  name(value: "Jane Smith")
}

Usage examples

The @cypher directive can be used in different contexts, such as the ones described in this section.

On an object type field

In the example below, a field similarMovies is bound to the Movie type, to find other movies with an overlap of actors:

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

type Movie {
    movieId: ID!
    title: String
    description: String
    year: Int
    actors(limit: Int = 10): [Actor!]!
        @relationship(type: "ACTED_IN", direction: IN)
    similarMovies(limit: Int = 10): [Movie]
        @cypher(
            statement: """
            MATCH (this)<-[:ACTED_IN]-(:Actor)-[:ACTED_IN]->(rec:Movie)
            WITH rec, COUNT(*) AS score ORDER BY score DESC
            RETURN rec LIMIT $limit
            """,
            columnName: "rec"
        )
}

On a query type field

The following example demonstrates a query to return all of the actors in the database:

type Actor {
    actorId: ID!
    name: String
}

type Query {
    allActors: [Actor]
        @cypher(
            statement: """
            MATCH (a:Actor)
            RETURN a
            """,
            columnName: "a"
        )
}

On a mutation type field

The following example demonstrates a mutation using a Cypher query to insert a single actor with the specified name argument:

type Actor {
    actorId: ID!
    name: String
}

type Mutation {
    createActor(name: String!): Actor
        @cypher(
            statement: """
            CREATE (a:Actor {name: $name})
            RETURN a
            """,
            columnName: "a"
        )
}