neo4j-graphql / neo4j-graphql

GraphQL bindings for Neo4j, generates and runs Cypher

Home Page:https://grandstack.io/docs/neo4j-graphql-plugin.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Neo4j-GraphQL Extension

neo4j graphql logo
Note
The plugin is considered end-of-life. It will not be updated for Neo4j 4.0, we recommend to move a middleware based solution using neo4j-graphql-js or neo4j-graphql-java. After a lot of feedback we think separating the GraphQL API from the core database is the better architectural setup.

This is a GraphQL-Endpoint extension for Neo4j. It is part of the GRANDstack

This readme assumes you are somewhat familiar with GraphQL and minimally with Cypher.

Based on your GraphQL schema, it translates GraphQL Queries and Mutations into Cypher statements and executes them on Neo4j.

It offers both an HTTP API, as well as, Neo4j Cypher Procedures to execute and manage your GraphQL API.

Installation

Download and install Neo4j Desktop

Neo4j Desktop provides a quick install button for neo4j-graphql.

After creating your database you can find it under "Manage" in the "Plugins" tab for a single click install.

desktop graphql

Use with neo4j-graphql-cli

This extension is utilized, when you use neo4j-graphql-cli

This tool

  1. launches a Neo4j Sandbox with your GraphQL schema

  2. provides the /graphql/ endpoint,

  3. a Neo4j server,

  4. an hosted GraphiQL for it.

npm install -g neo4j-graphql-cli
neo4j-graphql movies-schema.graphql

Quickstart

To generate some graph data in Neo4j just run :play movie graph in your Neo4j Browser.

GraphiQL

The best tool to use is GraphiQL the GraphQL UI. Get and install it.

Enter your GraphQL URL, like http://localhost:7474/graphql/ (note the trailing slash).

If your Neo4j Server runs with authentication enabled, add the appropriate Basic-Auth (base64 encoded) username:password header in the "Edit HTTP Headers" screen.

Command to generate the Authorization header value.
echo "Basic $(echo -n "neo4j:<password>" | base64)"

Uploading a GraphQL Schema

Here is a small example schema for the movie data. Just a Movie with actors, and a Person with movies.

Simple properties are mapped directly while the relationships are mapped to fields movies and actors

Movies Schema
type Movie  {
  title: String!
  released: Int
  actors: [Person] @relation(name:"ACTED_IN",direction:IN)
}
type Person {
  name: String!
  born: Int
  movies: [Movie] @relation(name:"ACTED_IN")
}

You can POST a GraphQL schema to the /graphql/idl/ endpoint or run the CALL graphql.idl('schema-text') procedure.

The payload is parsed and stored in Neo4j and used subsequently as the backing GraphQL schema for validating and executing queries.

CALL graphql.idl('
type Movie  {
  title: String!
  released: Int
  tagline: String
  actors: [Person] @relation(name:"ACTED_IN",direction:IN)
}
type Person {
  name: String!
  born: Int
  movies: [Movie] @relation(name:"ACTED_IN")
}
')

You should then be able to see your schema in the Docs section of GraphiQL.

This also gives you auto-completion, validation and hints when writing queries.

With graphql.reset() you can trigger the reset of you schema. But it also updates automatically if changed on other cluster members. Latest after 10 seconds.

To visualize your GraphQL schema in Neo4j Browser use: call graphql.schema().

graphql.schema

Using

RETURN graphql.getIdl()

you’ll get back a string representation of the currently used schema.

Auto-Generated Query Types

From that schema, the plugin automatically generate Query Types for each of the declared types.

e.g. Movie(title,released,first,offset,_id,orderBy, filter): [User]

  • Each field of the entity is available as query argument, with an equality check (plural for list-contains)

  • We also provide a filter argument for more complex filtering with nested predicates, also for relation-fields (see graphcool docs)

  • For ordered results there is a orderBy argument

  • And first, offset allow for pagination

Now you can for instance run this query:

Simple query example
{ Person(name:"Kevin Bacon") {
    name
    born
    movies {
      title
      released
      tagline
    }
  }
}
graphiql query1
Advanced query example
query Nineties($released: Int, $letter: String)
{ Movie(released: $released,
        filter: {title_starts_with: $letter,
                 actors_some: { name_contains: $letter}}) {
    title
    released
    actors(first: 3) {
      name
      born
      movies(first: 1, orderBy: title_desc) {
        title
        released
      }
    }
  }
}

# query variables
{ "released":1995, "letter":"A"}

This query declares query name and parameters (first line), which are passed separately ("Query Parameters box") as JSON.

And get this result:

graphiql query2

Auto-Generated Mutations

Additionally Mutations for each type are created, which return update statistics.

e.g. for the Movie type:

  • createMovie(title: ID!, released: Int) : String

  • mergeMovie(title: ID!, released: Int) : String

  • updateMovie(title: ID!, released: Int) : String

  • deleteMovie(title: ID!) : String

and for it’s relationships:

  • addMovieActors(title: ID!, actors:[ID]!) : String

  • deleteMovieActors(title: ID!, actors:[ID]!) : String

Those mutations then allow you to create and update your data with GraphQL.

Single Mutation
mutation {
    createPerson(name:"Chadwick Boseman", born: 1977)
}
Mutation Result
{ "data": {
    "createPerson": "Nodes created: 1\nProperties set: 2\nLabels added: 1\n"
  }
}
Several Mutations at once
mutation {
    pp: createMovie(title:"Black Panther", released: 2018)
    lw: createPerson(name:"Letitia Wright", born: 1993)
    cast: addMovieActors(title: "Black Panther",
          actors:["Chadwick Boseman","Letitia Wright"])
}

If multiple mutations are sent as part of the same request, they will be executed in the same transaction (meaning if one of them fails they will all fail). If the same mutation is called multiple times, you need to use alias prefixes to avoid clashes in the returned data, which is keyed on mutation names.

graphiql mutation

You can use those mutations also to load data from CSV or JSON.

Directives

Directives like @directiveName(param:value) can be used to augment the schema with additional meta-information that we use for processing.

You have already seen the @relation(name:"ACTED_IN", direction:"IN") directive to map entity references to graph relationships.

The @cypher directive is a powerful way of declaring computed fields, query types and mutations with a Cypher statement.

For instance, directors
type Movie {
  ...
  directors: [Person] @cypher(statement:"MATCH (this)<-[:DIRECTED]-(d) RETURN d")
}
Register Top-Level Schema Types
schema {
   query: QueryType
   mutation: MutationType
}
A custom query
type QueryType {
  ...
  coActors(name:ID!): [Person] @cypher(statement:"MATCH (p:Person {name:$name})-[:ACTED_IN]->()<-[:ACTED_IN]-(co) RETURN distinct co")
}
A custom mutation
type MutationType {
  ...
  rateMovie(user:ID!, movie:ID!, rating:Int!): Int
  @cypher(statement:"MATCH (p:Person {name:$user}),(m:Movie {title:$movie}) MERGE (p)-[r:RATED]->(m) SET r.rating=$rating RETURN r.rating")
}
Full enhanced Schema
type Movie  {
  title: String!
  released: Int
  actors: [Person] @relation(name:"ACTED_IN",direction:IN)
  directors: [Person] @cypher(statement:"MATCH (this)<-[:DIRECTED]-(d) RETURN d")
}
type Person {
  name: String!
  born: Int
  movies: [Movie] @relation(name:"ACTED_IN")
}
schema {
   query: QueryType
   mutation: MutationType
}
type QueryType {
  coActors(name:ID!): [Person] @cypher(statement:"MATCH (p:Person {name:$name})-[:ACTED_IN]->()<-[:ACTED_IN]-(co) RETURN distinct co")
}
type MutationType {
  rateMovie(user:ID!, movie:ID!, rating:Int!): Int
  @cypher(statement:"MATCH (p:Person {name:$user}),(m:Movie {title:$movie}) MERGE (p)-[r:RATED]->(m) SET r.rating=$rating RETURN r.rating")
}

New Neo4j-GraphQL-Java Integration

Currently we’re working on a independent transpiler (neo4j-graphql-java) of GraphQL to Cypher which can also be used for your own GraphQL servers or middleware on the JVM.

This takes a given GraphQL schema, augments it and then uses that schema to generate Cypher queries from incoming GraphQL queries.

There are small examples of writing GraphQL servers in the repository, but we also wanted to make the new implementation available for testing.

That’s why we integrated the new transpiler at the URL: http://localhost:7474/graphql/experimental/ in this plugin, so that you can test it out. It uses the schema of the main implementation.

Currently supported features are:

  • parse SDL schema

  • resolve query fields via result types

  • handle arguments as equality comparisons for top level and nested fields

  • handle relationships via @relation directive on schema fields

  • @relation directive on types for rich relationships (from, to fields for start & end node)

  • filter for top-level query-fields

  • handle first, offset arguments

  • argument types: string, int, float, array

  • request parameter support

  • parametrization for cypher query

  • aliases

  • inline and named fragments

  • auto-generate query fields for all objects

  • @cypher directive for fields to compute field values, support arguments

  • auto-generate mutation fields for all objects to create, update, delete

  • @cypher directive for top level queries and mutations, supports arguments

For more details see the readme of the transpiler repository.

Here is a query example against the movie graph:

{
  person(born:1950) {
    name, born
    movies(first: 4) {
      title
      actors {
        name
      }
    }
  }
}

width:800

Procedures

This library also comes with Cypher Procedures to execute GraphQL from within Neo4j.

Simple Procedure Query
CALL graphql.query('{ Person(born: 1961) { name, born } }')
Advanced Procedure Query with parameters and post-processing
WITH 'query ($year:Long,$limit:Int) { Movie(released: $year, first:$limit) { title, actors {name} } }' as query

CALL graphql.query(query,{year:1995,limit:5}) YIELD result

UNWIND result.Movie as movie
RETURN movie.title, [a IN movie.actors | a.name] as actors
graphql.execute
Update with Mutation
CALL graphql.execute('mutation { createMovie(title:"The Shape of Water", released:2018)}')

Other Information

Please leave Feedback and Issues

You can get quick answers on Neo4j-Users Slack in the #neo4j-graphql channel

License: Apache License v2.

This branch for Neo4j 3.5.x

Build Status

Features

name information example

entities

each node label represented as entity

{ Person {name,born} }

multi entities

multiple entities per query turned into UNION

{ Person {name,born} Movie {title,released} }

property fields

via sampling property names and types are determined

{ Movie {title, released} }

field parameters

all properties can be used as filtering (exact/list) input parameters, will be turned into Cypher parameters

{ Movie(title:"The Matrix") {released,tagline} }

query parameters

passed through as Cypher parameters

query MovieByParameter ($title: String!) { Person(name:$name) {name,born} }

filter arguments

nested input types for arbitrary filtering on query types and fields

{ Company(filter: { AND: { name_contains: "Ne", country_in ["SE"]}}) { name } }

filter arguments for relations

filtering on relation fields, suffixes ("",not,some, none, single, every)

{ Company(filter: { employees_none { name_contains: "Jan"}, employees_some: { gender_in : [female]}, company_not: null }) { name } }

relationships

via a @relationship annotated field, optional direction

type Person { name: String, movies : Movie @relation(name:"ACTED_IN", direction:OUT) }

ordering

via an extra orderBy parameter

query PersonSortQuery { Person(orderBy:[name_desc,born_desc]) {name,born}}

pagination

via first and offset parameters

query PagedPeople { Person(first:10, offset:20) {name,born}}

schema first IDL support

define schema via IDL

:POST /graphql/idl "type Person {name: String!, born: Int}"

Mutations

create/delete mutations inferred from the schema

createMovie(title:ID!, released:Int) updateMovie(title:ID!, released:Int) deleteMovie(title:ID!)

createMoviePersons(title:ID!,persons:[ID!])
deleteMoviePersons(title:ID!,persons:[ID!])

Cypher queries

@cypher directive on fields and types, parameter support

actors : Int @cypher(statement:"RETURN size( (this)< -[:ACTED_IN]-() )")

Cypher updates

Custom mutations by executing @cypher directives

createPerson(name: String) : Person @cypher(statement:"CREATE (p:Person {name:{name}}) RETURN p")

extensions

extra information returned

fields are: columns, query, warnings, plan, type READ_ONLY/READ_WRITE,

Note
@cypher directives can have a passThrough:true argument, that gives sole responsibility for the nested query result for this field to your Cypher query. You will have to provide all data/structure required by client queries. Otherwise, we assume if you return object-types that you will return the appropriate nodes from your statement.

Advanced Usage

The extension works with Neo4j 3.x, the code on this branch is for 3.5.

Please consult the Neo4j documentation for file locations for the other editions on the different operating systems.

Manual Installation

  1. Download the appropriate neo4j-graphql release for your version.

  2. Copy the jar-file into Neo4j’s plugins directory

  3. Edit the Neo4j settings ($NEO4J_HOME/conf/neo4j.conf) to add:
    dbms.unmanaged_extension_classes=org.neo4j.graphql=/graphql

  4. You might need to add ,graphql.* if your config contains this line:
    dbms.security.procedures.whitelist=

  5. (Re)start your Neo4j server

Note
Neo4j Desktop: the configuration is available under Manage → Settings, the plugins folder via Open Folder.
Note

If you run Neo4j via Docker:

  • put the jar-file into a /plugins directory and make it available to the container via -v /path/to/plugins:/plugins

  • also add to your environment: -e NEO4J_dbms_unmanaged__extension__classes=org.neo4j.graphql=/graphql.

Building manually

git clone https://github.com/neo4j-graphql/neo4j-graphql
cd neo4j-graphql
git checkout {branch}
mvn clean package
cp target/neo4j-graphql-*.jar $NEO4J_HOME/plugins
echo 'dbms.unmanaged_extension_classes=org.neo4j.graphql=/graphql' >> $NEO4J_HOME/conf/neo4j.conf
$NEO4J_HOME/bin/neo4j restart
Note
You might need to add ,graphql.* if your config contains this line: dbms.security.procedures.whitelist=

Schema from Graph

If you didn’t provide a GraphQL schema, we try to derive one from the existing graph data. From sampling the data we add a type for each Node-Label with all the properties and their types found as fields.

Each relationship-type adds a reference field to the node type, named aType for A_TYPE.

Procedures

You can even visualize remote graphql schemas, e.g. here from the GitHub GraphQL API. Make sure to generate the Personal Access Token to use in your account settings.

call graphql.introspect("https://api.github.com/graphql",{Authorization:"bearer d8xxxxxxxxxxxxxxxxxxxxxxx"})

graphql.introspect github

Resources

Neo4j-GraphQL

Libraries & Tools

Neo4j Admin API

The project also contains an experimental endpoint to expose procedures deployed into Neo4j (built-in and external) as a GraphQL admin API endpoint.

If you access /graphql/admin in GraphiQL or GraphQL Playground, you should see those separated into queries and mutations in the schema.

You have to explicitely allow procedures to be exposed, via the config setting graphql.admin.procedures.(read/write) with either Neo4j procedure syntax or admin-endpoint field names. By setting it to:

graphql.admin.procedures.read=db.*,dbms.components,dbms.queryJ*
graphql.admin.procedures.write=db.create*,dbIndexExplicitFor*

For documentation on each please check the provided description or the documentation of the original procedure in the Neo4j or other manuals.

neo4j graphql admin simple

You will have to provide the appropriate user credentials as HTTP Basic-Auth headers, the procedures are executed under the priviledges of that user.

You can read more about it in this article.

About

GraphQL bindings for Neo4j, generates and runs Cypher

https://grandstack.io/docs/neo4j-graphql-plugin.html

License:Apache License 2.0


Languages

Language:Kotlin 83.8%Language:Java 16.2%