In this module we take a look at Neo4j, GraphQL, and the Neo4j GraphQL integrations. First, we explore writing GraphQL queries against a demo GraphQL API of movies. Then we use the GraphQL Architect graph app for Neo4j Desktop to see how we can build our own GraphQL APIs using Neo4j GraphQL.
Neo4j is a native graph database with many features and functionality making up the Neo4j Graph Platform. Specifically:
- The Neo4j DBMS
- The property graph data model
- The Cypher query language
- Language drivers using the Bolt protocol for building applications
- Graph analytics with Graph Data Science
- Data visualization with Neo4j Bloom
- Neo4j Aura database-as-a-service
- A GraphQL integration for building GraphQL APIs backed by Neo4j
GraphQL is an API query language and runtime for fulfilling those queries. GraphQL uses a type system to define the data available in the API, including what entities and attributes (types and fields in GraphQL parlance) exist and how types are connected (the data graph). GraphQL operations (queries, mutations, or subscriptions) specify an entry-point and a traversal of the data graph (the selection set) which defines what fields to be returned by the operation.
GraphQL type definitions define the data available in the API. These type definitions are typically defined using the GraphQL Schema Definition Language (SDL), a language agnostic way of expressing the types. However, type definitions can be also be defined programmatically.
Each GraphQL operation is either a Query, Mutation, or Subscription. The fields of the Query, Mutation, and Subscription types define the entry points for an operation. Each operation starts at the field of one of these types.
The selection set specifies the fields to be returned by a GraphQL operation and can be thought of as a traversal through the data graph.
The response to a GraphQL request matches the shape of the selection set.
In GraphQL resolvers are the functions responsible for actually fulfilling the GraphQL operation. In the context of a query, this means fetching data from a data layer.
Some of the benefits of GraphQL include:
- Overfetching - sending less data over the wire
- Underfetching - everything the client needs in a single request
- The GraphQL specification defines exactly what GraphQL is
- Simplify data fetching with component based data interactions
- "Graphs all the way down" - GraphQL can help unify disparate systems and focus API interactions on relationships instead of resources.
- Developer productivity - By reasoning about application data as a graph with a strict type system developers can focus on building applications.
Of course GraphQL is not a silver bullet. It's important to be aware of some of the challenges that come from introducing GraphQL in a system.
- Some well understood practices from REST don't apply
- HTTP status codes
- Error handling
- Caching
- Exposing arbitrary complexity to the client and performance considerations
- The n+1 query problem - the nested nature of GraphQL operations can lead to multiple requests to the data layer(s) to resolve a request
- Query costing and rate limiting
Best practices and tooling have emerged to address all of the above, however it's important to be aware of these challenges.
The fundamental goal of the Neo4j GraphQL integrations is to make it easier to build GraphQL APIs backed by Neo4j.
NOTE: It's important to point out that GraphQL is an API query language and NOT a database query language. The goal of the Neo4j GraphQL integration is to help build the API layer that sits between the client and database, not to execute GraphQL queries directly against the database.
At a high level the goals are:
- Reducing boilerplate
- Developer productivity
- Extensibility
- Performance
GraphQL type definitions can drive the database data model, which means we don't need to maintain two separate schemas.
This includes
- Query & Mutation types (an API entrypoint for each type defined in the schema)
- Ordering
- Pagination
- Complex filtering
- DateTime & Spatial types and filtering
To reduce boilerplate and optimize for performance Neo4j GraphQL automatically generates a single database query for any arbitrary GraphQL request. This means the developer does not need to implement resolvers and each GraphQL operation results in a single roundtrip to the database.
To add custom logic beyond simple CRUD operations we can use the @cypher
GraphQL schema directive to add computed fields bound to a Cypher query to the GraphQL schema.
There are multiple implementations of Neo4j GraphQL, in this workshop we focus on neo4j-graphql.js
We will use neo4j-graphql.js
to build GraphQL APIs in this workshop. neo4j-graphql.js
is a JavaScript library designed to work with any Node.js GraphQL server.
The two main features provided by neo4j-graphql.js
are
- Schema augmentation
- GraphQL to Cypher transpilation
To familiarize yourself with GraphQL and writing GraphQL queries, explore the public movies GraphQL API at https://movies.grandstack.io. Open the URL in a web browser to access GraphQL Playground and explore the DOCS and SCHEMA tab to see the type definitions.
Try writing queries to answer the following questions:
- Find the titles of the first 10 movies, ordered by title.
- Who acted in the movie "Jurassic Park"?
- What are the genres of "Jurassic Park"? What other movies are in those genres?
- What movie has the highest imdbRating?
GraphQL Architect is a Graph App for Neo4j Desktop that allows us to build, query, and deploy GraphQL APIs backed by Neo4j by writing only GraphQL SDL.
Read more about GraphQL Architect in this article.
To install GraphQL Architect, first open Neo4j Desktop then open the Graph Apps Gallery:
The Graph Apps Gallery allows you to install Graph Apps in Neo4j Desktop. Select the "Install" button for GraphQL Architect:
Next, activate the database in Neo4j Desktop you'd like to use with GraphQL Architect, select the "Open" drop-down and choose "GraphQL Architect". You can also launch GraphQL Architect by clicking its icon in the Graph Apps drawer in Neo4j Desktop.
This will launch the GraphQL Architect graph app. On first launch GraphQL Architect will inspect the data in your database and generate GraphQL type definitions for the data model.
Install GraphQL Architect, connect to Neo4j Sandbox, load sample dataset.
- Log in to Neo4j Sandbox and launch a new "Blank" project.
- Launch Neo4j Browser and run the command
:play grandstack
to load the GRANDstack browser guide - Click the query embedded in the GRANDstack browser guide and execute it to load our sample dataset
- Connect this Sandbox instance to Neo4j Desktop by creating a new remote graph.
- Install and launch the GraphQL Architect Graph App.
- Start the local GraphQL server and navigate to the GraphiQL pane in GraphQL Architect.
- Select the "Docs" tab to review the API schema.
- Execute a GraphQL query to find all businesses and any reviews for each business.
Let's use the GraphiQL documentation tab to see how the following are added to the GraphQL schema:
- Query & Mutation fields
- Relationship fields
- Field arguments
- Filter arguments
Write GraphQL mutations to accomplish the following:
- Create a new Business node. You can make up a fake business or use your favorite store.
- Connect this new business to a Category node
- Add a Review for this business.
We can add custom logic to our API by using the @cypher
GraphQL schema directive. Schema directives in GraphQL are used to indicate custom logic and the @cypher
schema directive is used to bind a Cypher query to a computed field in the GraphQL schema.
Here we extend the Business type to include a computed field average_stars
.
extend type Business {
average_reviews: Float @cypher(statement: "MATCH (this)<-[:REVIEWS]-(r:Review) RETURN avg(r.stars)")
}
The @cypher
directive takes a single argument statement
which is a Cypher query. The this
Cypher variable is bound to the current object being resolved.
In this exercise we will add a GraphQL field recommended
to the User type that will be used to find businesses to recommend. We'll use the @cypher
schema directive to map this field to a Cypher query.
- Write a Cypher query to recommended businesses for a user. This could be done by looking at overlapping businesses reviewed, user ratings, graph algorithms using the Graph Data Science library or some other method. Feel free to be creative!
- Add a new field
recommended
to the User GraphQL type definition that returns a list of Business objects that are recommendations for that user. - Add the
@cypher
schema directive to the User type and adapt the Cypher statement so that it returns
Hint: Be sure to use the
this
keyword to refer to the currently resolvedUser
node.
Let's create a new mutation field that does all the operations from the mutation exercise (create business, assign categories, add review) all in one step!
All done - that's it for this module! Let's continue on to Module 2 where we'll take a look at using neo4j-graphql.js in a fullstack application.