Following a previous, more introductory blog post on Neo4j and two of their GraphQL projects this post shows how to use Neo4j with GraphQL based on neo4j-graphql
. Besides the easy installation, configuration and schema introspection on the Neo4j side it will also show how to access this Neo4j GraphQL server from external clients, especially from NodeJS
.
Neo4j Configuration
As discussed in the mentioned blog post neo4j-graphql
is the database extension (making the Neo4j server itself the GraphQL server), not to be confused with neo4j-graphql-js
(which is to be used with an external GraphQL server).
Versions
neo4j-graphql
: neo4j-graphql-3.3.0.1.jar
neo4j-apoc-procedure
(optional): apoc-3.3.0.2-all.jar
Neo4j
: Neo4j 3.3.3
Setup
Exact setup instructions with basic Dockerfiles can be found in the previous blog post. If you want to make use of the apoc
utilities make sure to COPY
these as well (as shown in the neo4j-graphql-js
Dockerfile
, however they are not mandatory for neo4j-graphql
).
This post will use the Northwind Database in the Neo4j Web UI Import version (the full example, e.g. also including employees can be found here). To follow the exact examples here go to the beautiful Neo4j web interface and hit :play northwind-graph
and follow the instructions (you will have to click a few import and index creation queries and then you are done).
After that, just to be sure, restart your Neo4j instance so that on startup neo4j-graphql
can auto-create the GraphQL schema (alternatively you can as well POST
your own schema to /graphql/id/
).
Schema
After the restart go back to the Web UI, enter call db.schema
and you get the following simplified Northwind Database:
There are also various other ways to examine the contents, structure and metadata of your currently mounted Neo4j database instance:
call apoc.meta.schema
call apoc.meta.data
call apoc.meta.graph
call apoc.meta.subGraph({labels:["Product", "Order"]})
To see the corresponding GraphQL
schema (also from the Neo4j Web UI):
call graphql.schema()
call graphql.introspect("http://localhost:7474/graphql/",{Authorization:"Basic bmVvNGo...="})
# Authorization Header
# "Basic <neo4j_user_name>:<password>" (base64 encoded)
Details on how the db schema
is transformed to a GraphQL schema
can be found here.
Access
Now that we know what the schema looks like let's see how we can access the Neo4j GraphQL endpoint from external clients. As kind of a "teaser": Start with simple queries ... and do be amazed that also complex queries are served to you as simple queries.
GraphiQL
The most simple way is to use GraphiQL
. Setup instructions related to neo4j-graphql
can be found here. If you are using the NodeJS
based neo4j-graphql-js
anyway you could also use the instructions in the previous blog post for setting up an GraphiQL endpoint (for the Neo4j based GraphQL Server) within neo4j-graphql-js
.
A simple query might be something like this:
It just gives you a list of productNames
and categoryNames
... yet at the same time within one GraphQL request (and one query to the database ... multiple entities per query are turned into a UNION
).
More complex examples could look like this:
# Cypher Query:
MATCH (p:Product {productName: "Chang"})<-[:ORDERS]-(c:Order {customerID: "SAVEA"}) RETURN c.orderID
# Cypher Result:
"c.orderID"
"11030"
"10722"
"10714"
"10440"
"10393"
# GraphiQL Query:
{
Product(productName:"Chang") {
orders(customerID:"SAVEA") {
orderID
}
}
}
# GraphiQL Result:
"data": { "Product": [{ "orders": [ {"orderID": "11030"},{"orderID": "10722"},{"orderID": "10714"},{"orderID": "10440"},{"orderID": "10393"}]}]}
# Cypher Query:
MATCH (c:Customer)-[:PURCHASED]->(:Order)-[:ORDERS]->(p:Product)-[:PART_OF]->(:Category {categoryName: "Beverages"}) RETURN c.contactName, p.productName
# Cypher Result:
"c.contactName" "p.productName"
"Michael Holz" "Lakkalikööri"
"Yvonne Moncada" "Lakkalikööri"
... ...
# GraphiQL Query:
{
Category(categoryName: "Beverages") {
partOf {
productName
orders {
purchased {
contactName
}
}
}
}
}
# GraphiQL Result:
"data": {
"Category": [
{
"partOf": [
{
"productName": "Lakkalikööri",
"orders": [{
"purchased": {
"contactName": "Michael Holz"
}
},{
"purchased": {
"contactName": "Yvonne Moncada"
}
},...
Of course GraphiQL
, as nice and useful as it is, is only for manual developing and testing purposes. So the next section will cover some more code-based options.
Curl
What else first than a plane curl
command ... Be sure to have a host-mapping
of your Neo4j container port
if you want access it from outside the Docker Host
(if access happens from another container the ports EXPOSED
by Neo4j are sufficient if the other container is able to reach and resolve the Neo4j
container):
# Manual, interacitve access from outside Docker
curl -X POST -H "Content-Type: application/json" -d '{"query": "{ Product {productName}}"}' http://<docker_host_ip>:7474/graphql/ -u neo4j
# Enter password
# Result
{"data":{"Product":[{"productName":"Chai"},{"productName":"Chang"}, ...
# Access from another container with Authorization Header
# "Basic <neo4j_user_name>:<password>" (base64 encoded)
curl -X POST -H "Content-Type: application/json" -H 'Authorization:Basic bmVvNGo...=' -d '{"query": "{ Product {productName}}"}' http://<container_name>:7474/graphql/
NodeJS - Server-Side
For accessing the Neo4j GraphQL server within a NodeJS
application you could just use a simple HTTP
request or the request npm module
.
However, there is already the dedicated request module graphql-request
for GraphQL endpoints. For an Express
based application bootstrapped via express-generator
the basic code snippet in routes/index.js
could look like this:
const express = require('express');
const router = express.Router();
const { GraphQLClient } = require('graphql-request');
const client = new GraphQLClient('http://<container_name>:7474/graphql/', {
headers: {
Authorization: 'Basic bmVvNGo...=',
},
})
const query = `{
Customer {
contactName
}
}`
// For variables : https://www.npmjs.com/package/graphql-request#using-variables
router.get('/api', (req, res, next) => {
client.request(query)
.then(data => res.send(data))
.catch(err => {
console.log(err.response.errors) // GraphQL response errors
console.log(err.response.data) // Response data if available
return next(err);
});
});
module.exports = router;
In a containerized environment the NodeJS
container must be able to reach and resolve the Neo4J
container (e.g. via links
or by being on the same docker network
in a docker swarm
).
NodeJS - Client-Side
For a simple XHR
based access example, just modify views/index.pug
like this
extends layout
block content
h1= title
p Welcome to #{title}
div
input(type="button", value="GraphQL Test", onclick="graphQLTest()")
script.
const graphQLTest = () => {
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.open('POST', 'http://<docker_host_ip>:7474/graphql/');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('Authorization', 'Basic bmVvNGo...=');
xhr.onload = function () {
alert(JSON.stringify(xhr.response.data.Supplier[0]));
}
xhr.send(JSON.stringify({query: `{
Supplier(supplierID: "1") {
_id
supplierID
country
contactTitle
address
city
phone
contactName
postalCode
companyName
}
}`}));
};
This of course would mean that the Neo4j
database container is exposed to the world (in a dockerized setup by a port host mapping
) and that the credentials are passed down to the browser on the client side. So this xhr
example is rather only for demonstration purposes than for production use (unless you use a proxy in front of Neo4j and something like csurf
to sign your request on the client side and handle db authentication on the server side).
But as GraphQL
has been primarily created as a lean query language for clients there also dedicated tools available: E.g. Apollo Client, Relay, urql, and vue-apollo. You can find good comparisons of some here (for Apollo
vs. urql
) and here (for Apollo
vs. Relay
).
Other Tools & Examples
As just mentioned there are other, more dedicated clients as well. A good overview can be found here and here. For complete fullstack
examples have a look at GraphQL Boilerplates
.
Summary
Only two lines for setup on the Neo4j side (copy the .jar
and register it) and then: One endpoint, (m)any data response(s), and one roundtrip to the db.
And keep in mind: If the auto-created schema does not fit your needs you can post your own schema. Or use neo4j-graphql
and its auto-created schema side-by-side with neo4j-graphql-js
and use neo4j-graphql-js
for the custom schema.
Further Information
Neo4j GraphQL Setup:
https://github.com/neo4j-graphql/neo4j-graphql/releases
https://hub.docker.com/_/neo4j/
(optionally):
https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases
GraphiQL:
https://github.com/graphql/graphiql
https://github.com/neo4j-graphql/neo4j-graphql#graphiql
graphql-request:
https://www.npmjs.com/package/graphql-request
GraphQL Client Examples:
http://graphql.org/graphql-js/graphql-clients/
https://blog.grandstack.io/a-look-at-graphql-clients-for-react-apps-512c2396cb53
https://blog.graph.cool/relay-vs-apollo-comparing-graphql-clients-for-react-apps-b40af58c1534
https://github.com/graphql-boilerplates
https://www.apollographql.com/client
https://facebook.github.io/relay/
https://github.com/FormidableLabs/urql
https://github.com/Akryum/vue-apollo
neo4j-graphql vs. neo4j-graphql-js:
https://daten-und-bass.io/blog/getting-started-with-neo4j-and-graphql/