GraphQL bindings for Neo4j, generates and runs Cypher
APACHE-2.0 License
= Neo4j-GraphQL Extension :img: docs/img :branch: 3.5
[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 https://grandstack.io[GRANDstack^]
This readme assumes you are somewhat familiar with http://graphql.org/[GraphQL^] and minimally with http://neo4j.com/developer/cypher[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 http://neo4j.com/download[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.
image::{img}/desktop-graphql.jpg[width=600]
=== Use with neo4j-graphql-cli
This extension is utilized, when you use https://www.npmjs.com/package/neo4j-graphql-cli[`neo4j-graphql-cli`^]
This tool
/graphql/
endpoint,== Quickstart
To generate some graph data in Neo4j just run http://localhost:7474/browser?cmd=play&arg=movie%20graph[`:play movie graph`^] in your Neo4j Browser.
=== GraphiQL
The best tool to use is https://electronjs.org/apps/graphiql[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 (https://www.base64encode.org/[base64 encoded^]) username:password
header in the "Edit HTTP Headers" screen.
Authorization
header value.=== 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
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.
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()
.
image::{img}/graphql.schema.jpg[width=600]
Using
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]
filter
argument for more complex filtering with nested predicates, also for relation-fields (see https://www.graph.cool/docs/reference/graphql-api/query-api-nia9nushae[graphcool docs])orderBy
argumentfirst
, offset
allow for paginationNow you can for instance run this query:
image::{img}/graphiql-query1.jpg[]
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 } } } }
This query declares query name and parameters (first line), which are passed separately ("Query Parameters box") as JSON.
And get this result:
image::{img}/graphiql-query2.jpg[]
=== 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.
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.
image::{img}/graphiql-mutation.jpg[]
You can use those mutations also to https://medium.com/@mesirii/better-data-import-with-graphql-548084a35dfd[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.
directors
=== New Neo4j-GraphQL-Java Integration
Currently we're working on a https://github.com/neo4j-graphql/neo4j-graphql-java[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 link:src/main/kotlin/GraphQLResourceExperimental.kt[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:
For more details see the https://github.com/neo4j-graphql/neo4j-graphql-java/[readme of the transpiler repository].
Here is a query example against the movie graph:
image:docs/img/neo4j-graphql-java-experimental.jpg[width:800]
=== Procedures
This library also comes with Cypher Procedures to execute GraphQL from within Neo4j.
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
image::{img}/graphql.execute.jpg[]
== Other Information
Please leave link:/issues[Feedback and Issues^]
You can get quick answers on http://neo4j.com/slack[Neo4j-Users Slack^] in the https://neo4j-users.slack.com/messages/C5ET7S24R[`#neo4j-graphql` channel^]
License: Apache License v2.
This branch for Neo4j {branch}.x
image:https://travis-ci.org/neo4j-contrib/neo4j-graphql.svg?branch={branch}["Build Status", link="https://travis-ci.org/neo4j-contrib/neo4j-graphql"]
== Features
// tag::features[]
[options=header,cols="a,2a,3m"] |===
| 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,
// | directive | directives control cypher prefixes, note that directives have to be set at the first entity |
// | directive - query plan | @profile / @explain
will be returned in extra field extensions
| query UserQuery { User @profile {name} }
// | directive - version | set cypher version to use @version(3.0,3.1,3.2)
| query UserQuery { User @version(3.0) {name} }
|===
[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.
// end::features[]
== Advanced Usage
The extension works with Neo4j 3.x, the code on this branch is for {branch}.
Please consult the https://neo4j.com/docs/operations-manual/current/configuration/file-locations/[Neo4j documentation^] for file locations for the other editions on the different operating systems.
=== Manual Installation
plugins
directory$NEO4J_HOME/conf/neo4j.conf
) to add: +dbms.unmanaged_extension_classes=org.neo4j.graphql=/graphql
,graphql.*
if your config contains this line: +dbms.security.procedures.whitelist=
NOTE: Neo4j Desktop: the configuration is available under Manage -> Settings, the plugins
folder via Open Folder.
If you run Neo4j via Docker:
/plugins
directory and make it available to the container via -v /path/to/plugins:/plugins
+-e NEO4J_dbms_unmanaged__extension__classes=org.neo4j.graphql=/graphql+
.=== Building manually
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.
// Relationship information is collected with direction, type, end-node-labels and degree (to determine single element or collection result).
// Additional labels on a node are added as GraphQLInterface's.
Each relationship-type adds a reference field to the node type, named aType
for A_TYPE
.
// Each relationship-type and end-node label is added as a virtual property to the node type, named TYPE_Label
for outgoing and Label_TYPE
for incoming relationships.
////
You can also use variables or query the schema:
or
and then query for real data
query PersonQuery($name: String!) { Person(name: $name) { name born actedIn { title released tagline } } }
////
=== Procedures
You can even visualize remote graphql schemas, e.g. here from the https://developer.github.com/v4/[GitHub GraphQL API^]. Make sure to generate the https://developer.github.com/v4/guides/forming-calls/#authenticating-with-graphql[Personal Access Token^] to use in your account settings.
image:{img}/graphql.introspect-github.jpg[width=600]
////
== Examples
Some more examples
////
== Resources
=== Neo4j-GraphQL
=== Libraries & Tools
////
=== Similar Projects
////
// * https://github.com/facebook/dataloader // * http://graphql.org/learn/serving-over-http/[Serving over HTTP]
//// echo "Authorization: Basic $(echo -n "neo4j:test" | base64)" "Authorization: Basic bmVvNGo6dGVzdA==" ////
////
== Using an http client (curl)
=== POST Schema IDL
curl -u neo4j: -i -XPOST -d'type Person { name: String, born: Int }' http://localhost:7474/graphql/idl/
{Person=MetaData{type='Person', ids=[], indexed=[], properties={name=PropertyType(name=String, array=false, nonNull=false), born=PropertyType(name=Int, array=false, nonNull=false)}, labels=[], relationships={}}}
=== Query the Schema
curl -u neo4j: -i -XPOST -d'{"query": "query {__schema {types {kind, name, description}}}"}' -H accept:application/json -H content-type:application/json http://localhost:7474/graphql/
=== Get All People
curl -u neo4j: -i -XPOST -d'{"query": "query AllPeopleQuery { Person {name,born} } }"}' -H accept:application/json -H content-type:application/json http://localhost:7474/graphql/
HTTP/1.1 200 OK Date: Mon, 24 Oct 2016 21:40:15 GMT Content-Type: application/json Access-Control-Allow-Origin: * Transfer-Encoding: chunked Server: Jetty(9.2.9.v20150224)
=== Get one Person by name with Parameter
curl -u neo4j: -i -XPOST -d'{"query":"query PersonQuery($name:String!) { Person(name:$name) {name,born} }", "variables":{"name":"Kevin Bacon"}}' -H content-type:application/json http://localhost:7474/graphql/
HTTP/1.1 200 OK Date: Mon, 24 Oct 2016 21:40:38 GMT Content-Type: application/json Access-Control-Allow-Origin: * Transfer-Encoding: chunked Server: Jetty(9.2.9.v20150224)
=== Get one Person by name literal with related movies
curl -u neo4j: -i -XPOST -d'{"query":"query PersonQuery { Person(name:"Tom Hanks") {name, born, actedIn {title, released} } }"}' -H content-type:application/json http://localhost:7474/graphql/ HTTP/1.1 200 OK Date: Tue, 25 Oct 2016 03:17:08 GMT Content-Type: application/json Access-Control-Allow-Origin: * Transfer-Encoding: chunked Server: Jetty(9.2.9.v20150224)
=== Schema first
curl -X POST http://localhost:7474/graphql/idl -d 'type Person { name: String! born: Int movies: [Movie] @relation(name:"ACTED_IN") totalMoviesCount: Int @cypher(statement: "WITH {this} AS this MATCH (this)-[:ACTED_IN]->() RETURN count(*) AS totalMoviesCount") recommendedColleagues: [Person] @cypher(statement: "WITH {this} AS this MATCH (this)-[:ACTED_IN]->()<-[:ACTED_IN]-(other) RETURN other") }
type Movie {
title: String!
released: Int
tagline: String
actors: [Person] @relation(name:"ACTED_IN",direction:IN)
}' -u neo4j:****
////
//// == TODO
(status: {eq/neq:true}, createdAt: { gte: "2016-01-01", lt: "2016-02-01"}, tags: {isNull:false, includes/excludes: "foo"})
extensions
result value for query statistics or query plan, depending on directives given, e.g. contain the generated cypher query as well////
//// == Rewrite
== Open
== Permissions
== 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:
For documentation on each please check the provided description or the documentation of the original procedure in the Neo4j or other manuals.
image::{img}/neo4j-graphql-admin-simple.png[]
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 https://medium.com/@mesirii/using-a-graphql-api-for-database-administration[in this article^].