1 of 87

Building GraphQL APIs With The Neo4j GraphQL Library

& Neo4j Aura

2 of 87

About Me

William Lyon

Developer Relations Engineer, Neo4j

@lyonwj

lyonwj.com

Co-host GraphStuff.FM podcast

3 of 87

What is Neo4j?

Graph Database

  • Database management system (DBMS)
  • Property Graph data model
  • Cypher query language
  • Graph analytics
  • Data visualization
  • Neo4j Aura database-as-a-service
  • GraphQL integration for building GraphQL APIs

4 of 87

The Neo4j Graph Data Platform

4

Analytics &� Data Science �Tooling

Graph �Transactions

Data Orchestration

Development & �Administration

Drivers & APIs

Discovery & Visualization

Graph �Analytics

AI

BUSINESS USERS

DEVELOPERS

ADMINS

DATA�ANALYSTS

DATA�SCIENTISTS

APPLICATIONS

Cloud

Neo4j, Inc. All rights reserved 2021

5 of 87

Building GraphQL APIs With The Neo4j GraphQL Library

Agenda

Modules:

  • Intro to GraphQL
  • Neo4j GraphQL Library Overview
  • Custom Logic With Cypher
  • Authorization

Hands-On exercises using:

  • Neo4j Aura Free Tier
  • CodeSandbox
    • Link in each module to starter code
    • "Fork" sandbox during exercise

Hands-On

Exercise

6 of 87

Resources

  • Slides:
    • dev.neo4j.com/nov-graphql
  • Neo4j GraphQL Library Docs
  • Neo4j GraphQL Library Overview Page
  • Graph Academy Training
  • GRANDstack Starter Project

7 of 87

Introduction To GraphQL

7

8 of 87

What Is GraphQL?

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.

9 of 87

GraphQL Concepts - Type Definitions

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.

10 of 87

GraphQL Concepts - GraphQL Operations

Each GraphQL operation is either a Query, Mutation, or Subscription.

11 of 87

GraphQL Concepts - GraphQL Operations

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.

Entry point & arguments

12 of 87

GraphQL Concepts - Selection Set

The selection set specifies the fields to be returned by a GraphQL operation.

Can be thought of as a traversal through the data graph.

Selection set

13 of 87

GraphQL Concepts - Selection Set

The response to a GraphQL operation matches the shape of the selection set, returning on the data requested.

Selection set

14 of 87

GraphQL Concepts - Resolver Functions

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.

NOTE: The Neo4j GraphQL Library auto-generates resolver functions for us, but this is an important GraphQL concept to understand

15 of 87

Benefits Of GraphQL

  • 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.

16 of 87

GraphQL Challenges

  • 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.

17 of 87

GraphQL Tooling - GraphQL Playground

GraphQL Playground is an in-browser tool for querying and exploring GraphQL APIs.

View API documentation using GraphQL's introspection feature.

18 of 87

GraphQL Tooling - GraphQL Playground

Open movies.neo4j-graphql.com

  • Explore the "Docs" tab to learn more about the API schema
  • Run these GraphQL queries:

{

movies(options: { limit: 10 }) {

title

actors {

name

}

}

}

{

directors(where: {name:"Robert Redford"}) {

name

directed {

title

plot

}

}

}

  • Try modifying the query selection set to return additional fields
    • Try using ctrl+space for auto-complete
    • What can you find?

Hands On

Exercise

19 of 87

Let's Build Our Own GraphQL API!

19

20 of 87

Neo4j Aura Free Tier Setup

Let's create a Neo4j Aura Free instance that we'll use for the rest of the workshop...

Hands-On

Exercise

Once your Neo4j Aura instance is online you'll see the connection string (neo4j+s://xxxxx.databases.neo4j.io)

Be sure to take note of the generated password!

It will then take a few moments for your Neo4j Aura instance to be provisioned.

Sign in to Neo4j Aura:

dev.neo4j.com/aura-login

Select "Create a new database" button.

Choose the "Free" tier.

Enter a name for your Neo4j Aura instance and select "Create database"

Step 1:

Step 2:

Step 3:

21 of 87

The Neo4j GraphQL Library

Overview

21

22 of 87

The Neo4j GraphQL Library

For building Node.js GraphQL APIs with Neo4j.

The fundamental goal of the Neo4j GraphQL Library is to make it easier to build GraphQL APIs backed by Neo4j.

23 of 87

Goals Of The Neo4j GraphQL Library

GraphQL First Development

GraphQL type definitions can drive the database data model, which means we don’t need to maintain two separate schemas for our API and database.

24 of 87

Goals Of The Neo4j GraphQL Library

Auto-generate GraphQL API Operations

With the Neo4j GraphQL Library, GraphQL type definitions provide the starting point for a generated API that includes:

  • Query & Mutation types (an API entrypoint for each type defined in the schema)
  • Ordering
  • Pagination
  • Complex filtering
  • DateTime & Spatial types and filtering

25 of 87

Goals Of The Neo4j GraphQL Library

Generate Cypher From GraphQL Operations

To reduce boilerplate and optimize for performance the Neo4j GraphQL Library 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.

26 of 87

Goals Of The Neo4j GraphQL Library

Extend GraphQL With Cypher

To add custom logic beyond CRUD operations, you can use the @cypher GraphQL schema directive to add computed fields bound to a Cypher query to the GraphQL schema.

27 of 87

Neo4j GraphQL Library Quickstart

28 of 87

Neo4j GraphQL Library Quickstart

Create index.js:

29 of 87

Neo4j GraphQL Library Quickstart

Start GraphQL server:

This will start a local GraphQL API and will also serve the GraphQL Playground IDE for querying the API or exploring documentation using GraphQL’s introspection feature.

30 of 87

Building An Online Bookstore GraphQL API

For the rest of the workshop we will be building

an API for an online bookstore.

First, we need to define our data model.

The graph data modeling process:

  • Identify entities → Nodes
  • What are the attributes of these entities? → Properties
  • How are these entities connected? → Relationships
  • Can you traverse the graph to answer the business requirements of your application?

31 of 87

Setting Up Our Environment

  • Open this Codesandbox
  • Add your Neo4j Aura connection details to the .env file (NEO4J_URI, NEO4J_USER, & NEO4J_PASSWORD environment variables)
    • You will need to sign in to Codesandbox to save your updates

  • In GraphQL Playground (running in Codesandbox), run the following GraphQL query (you'll have an empty result set, but shouldn't see any errors):

Hands-On

Exercise

{

books {

title

}

}

32 of 87

Neo4j Aura Free Tier Setup

Let's create a Neo4j Aura Free instance that we'll use for the rest of the workshop and connect to our GraphQL API in CodeSandbox

Hands-On

Exercise

Update the Codesandbox .env file with your Neo4j credentials:

Once your Neo4j Aura instance is online you'll see the connection string (neo4j+s://xxxxx.databases.neo4j.io)

Be sure to take note of the generated password!

It will then take a few moments for your Neo4j Aura instance to be provisioned.

Sign in to Neo4j Aura:

dev.neo4j.com/neo4j-aura

Select "Create a new database" button.

Choose the "Free" tier.

Enter a name for your Neo4j Aura instance and select "Create database"

Step 1:

Step 2:

Step 3:

Step 4:

33 of 87

Neo4j Sandbox Setup

If you have issues with Neo4j Aura you can also use Neo4j Sandbox

Hands-On

Exercise

Update the Codesandbox .env file with your Neo4j credentials:

Take note of your Neo4j Sandbox Bolt URL and password

Sign in to Neo4j Sandbox:

dev.neo4j.com/sandbox

Select "Blank Sandbox"

Select "Launch Project"

Step 1:

Step 2:

Step 3:

34 of 87

Defining A Property Graph Model With GraphQL

35 of 87

Defining A Property Graph Model With GraphQL

Schema Directives

The @relationship directive is used to define relationships.

DateTime and Point scalar types are available and map to the equivalent native Neo4j database types.

The @timestamp directive is used to indicate the property will be automatically updated when the node is created and updated.

The @id directive marks a field as a unique identifier and enables auto-generation when the node is created.

More on directives in the documentation.

36 of 87

Creating Data - Generated Mutations

mutation {

createBooks(

input: {

isbn: "1492047686"

title: "Graph Algorithms"

price: 37.48

description:

"Practical Examples in Apache Spark and Neo4j"

}

) {

books {

isbn

title

price

description

__typename

}

}

}

37 of 87

Creating Data - Generated Mutations

mutation {

createReviews(

input: {

rating: 5

text: "Best overview of graph data science!"

book: { connect: { where: { node: { title: "Graph Algorithms" } } } }

}

) {

reviews {

rating

text

createdAt

book {

title

}

}

}

}

38 of 87

Creating Data - Nested Mutations

mutation {

createCustomers(

input: {

username: "EmilEifrem7474"

reviews: {

connect: {

where: { node: { text: "Best overview of graph data science!" } }

}

}

orders: {

create: {

node: {

books: {

connect: { where: { node: { title: "Graph Algorithms" } } }

}

shipTo: {

create: {

node: {

address: "111 E 5th Ave, San Mateo, CA 94401"

location: {

latitude: 37.5635980790

longitude: -122.322243272725

}

}

}

}

}

}

}

}

) {

customers {

username

orders {

placedAt

books {

title

}

shipTo {

address

}

}

reviews {

text

rating

book {

title

}

}

}

}

}

39 of 87

Let's Clear Out The Database...

MATCH (a) DETACH DELETE a

Hands-On

Exercise

40 of 87

… and load some sample data via GraphQL

mutation {

createBooks(

input: [

{

isbn: "1492047686"

title: "Graph Algorithms"

price: 37.48

description: "Practical Examples in Apache Spark and Neo4j"

}

{

isbn: "1119387507"

title: "Inspired"

price: 21.38

description: "How to Create Tech Products Customers Love"

}

{

isbn: "190962151X"

title: "Ross Poldark"

price: 15.52

description: "Ross Poldark is the first novel in Winston Graham's sweeping saga of Cornish life in the eighteenth century."

}

]

) {

books {

title

}

}

createCustomers(

input: [

{

username: "EmilEifrem7474"

reviews: {

create: {

node: {

rating: 5

text: "Best overview of graph data science!"

book: { connect: { where: { node: { isbn: "1492047686" } } } }

}

}

}

orders: {

create: {

node: {

books: {

connect: { where: { node: { title: "Graph Algorithms" } } }

}

shipTo: {

create: {

node: {

address: "111 E 5th Ave, San Mateo, CA 94401"

location: {

latitude: 37.5635980790

longitude: -122.322243272725

}

}

}

}

}

}

}

}

{

username: "BookLover123"

reviews: {

create: {

node: {

rating: 4

text: "Beautiful depiction of Cornwall."

book: { connect: { where: { node: { isbn: "190962151X" } } } }

}

}

}

orders: {

create: {

node: {

books: {

connect: [

{ where: { node: { title: "Ross Poldark" } } }

{ where: { node: { isbn: "1119387507" } } }

{ where: { node: { isbn: "1492047686" } } }

]

}

shipTo: {

create: {

node: {

address: "Nordenskiöldsgatan 24, 211 19 Malmö, Sweden"

location: {

latitude: 55.6122270502

longitude: 12.99481772774

}

}

}

}

}

}

}

}

]

) {

customers {

username

}

}

}

Hands-On

Exercise

41 of 87

Querying With GraphQL - Query Fields

By default, each type defined in the GraphQL type definitions will have a GraphQL Query field generated and added to the Query type as the pluralized name of the type (for example the type Movie becomes a Query field movies). Each query field is an entry point into the GraphQL API. Since GraphQL types are mapped to node labels in Neo4j, you can think of the Query field as the starting point for a traversal through the graph.

42 of 87

Querying With GraphQL - Query Fields

The response data matches the shape of our GraphQL query - as we add more fields to the GraphQL selection set those fields are included in the response object.

43 of 87

Querying With GraphQL - Sorting & Pagination

A sorting input type is generated for each type in the GraphQL type definitions, allowing for Query results to be sorted by each field using the options field argument.

Offset-based pagination is available by passing skip and limit values as part of the options argument.

"Count queries" allow us to calculate the total number of pages.

Offset-Based Pagination

44 of 87

Querying With GraphQL - Sorting & Pagination

Cursor-based pagination can be used on relationship fields using Relay-style "Connection" types.

See the documentation for more details.

Cursor-Based Pagination

45 of 87

Querying With GraphQL - Filtering

Query results can be filtered using the where argument. Filter inputs are generated for each field and expose comparison operators specific to the type of the field. For example, for numeric fields filter input operators include equality, greater than (_GT), less than (_LT), etc. String fields expose the common string comparison operators such as _STARTS_WITH, _CONTAINS, _ENDS_WITH, etc.

46 of 87

Querying With GraphQL - Filtering (Nested)

We can also use the where argument in nested selections to filter relationships. Here we are filtering for reviews created after Jan 1, 2021 using the createdAt_GT filter input on the createdAt DateTime type, specifying the date using the ISO format.

47 of 87

Querying With GraphQL - Geo Distance

For Point fields we can filter results by the distance to another point. Here we search for addresses within 1km of a specified point

48 of 87

Querying With GraphQL - Filtering Using Relationships

Let’s look at an example that applies filtering at the root of our query, but using a relationship. Let’s say we want to search for all orders where the shipTo address is within 1km of a certain point. To do that we’ll use the where argument at the root of the query (in the orders Query field), but use a nested input to specify we want to filter using the shipTo relationship and the corresponding Address node.

49 of 87

Exercise: Updating The GraphQL Schema

  • Update schema.graphql adding Author and Subject types to our GraphQL schema
  • Once updated, write GraphQL mutations to add authors and subjects to the graph:

Hands-On

Exercise

Title

Author(s)

Inspired

Marty Cagan

Ross Poldark

Winston Graham

Graph Algorithms

Mark Needham, Amy E. Hodler

Title

Subject(s)

Inspired

Product management, Design

Ross Poldark

Historical fiction, Cornwall

Graph Algorithms

Graph theory, Neo4j

If you get stuck you can find the solutions in the README.md file in this Codesandbox.

50 of 87

Adding Custom Logic

Cypher Schema Directive & Custom Resolvers

50

51 of 87

Setup

  • Let's clear out our database:

MATCH (a) DETACH DELETE a

  • Open this Codesandbox with complete schema
    • Update .env to connect to your Neo4j instance

  • Load sample data using this GraphQL query

Hands-On

Exercise

mutation {

createBooks(

input: [

{

isbn: "1492047686"

title: "Graph Algorithms"

price: 37.48

description: "Practical Examples in Apache Spark and Neo4j"

subjects: {

create: [

{ node: { name: "Graph theory" } }

{ node: { name: "Neo4j" } }

]

}

authors: {

create: [

{ node: { name: "Mark Needham" } }

{ node: { name: "Amy E. Hodler" } }

]

}

}

{

isbn: "1119387507"

title: "Inspired"

price: 21.38

description: "How to Create Tech Products Customers Love"

subjects: {

create: [

{ node: { name: "Product management" } }

{ node: { name: "Design" } }

]

}

authors: { create: { node: { name: "Marty Cagan" } } }

}

{

isbn: "190962151X"

title: "Ross Poldark"

price: 15.52

description: "Ross Poldark is the first novel in Winston Graham's sweeping saga of Cornish life in the eighteenth century."

subjects: {

create: [

{ node: { name: "Historical fiction" } }

{ node: { name: "Cornwall" } }

]

}

authors: { create: { node: { name: "Winston Graham" } } }

}

]

) {

books {

title

}

}

createCustomers(

input: [

{

username: "EmilEifrem7474"

reviews: {

create: {

node: {

rating: 5

text: "Best overview of graph data science!"

book: { connect: { where: { node: { isbn: "1492047686" } } } }

}

}

}

orders: {

create: {

node: {

books: {

connect: { where: { node: { title: "Graph Algorithms" } } }

}

shipTo: {

create: {

node: {

address: "111 E 5th Ave, San Mateo, CA 94401"

location: {

latitude: 37.5635980790

longitude: -122.322243272725

}

}

}

}

}

}

}

}

{

username: "BookLover123"

reviews: {

create: {

node: {

rating: 4

text: "Beautiful depiction of Cornwall."

book: { connect: { where: { node: { isbn: "190962151X" } } } }

}

}

}

orders: {

create: {

node: {

books: {

connect: [

{ where: { node: { title: "Ross Poldark" } } }

{ where: { node: { isbn: "1119387507" } } }

{ where: { node: { isbn: "1492047686" } } }

]

}

shipTo: {

create: {

node: {

address: "Nordenskiöldsgatan 24, 211 19 Malmö, Sweden"

location: {

latitude: 55.6122270502

longitude: 12.99481772774

}

}

}

}

}

}

}

}

]

) {

customers {

username

}

}

}

52 of 87

Adding Custom Logic To The GraphQL API

Custom Resolvers

  • Implement field resolver function with your custom logic
  • Resolver function will be called after initial data is fetched from Neo4j

@cypher GraphQL Schema Directive

  • Add custom Cypher statements to the GraphQL schema
  • Single Cypher query is generated / one round trip to the database

52

52

53 of 87

Cypher GraphQL Schema Directive

Computed Scalar Field

With the @cypher schema directive in the Neo4j GraphQL Library we can add a field subTotal to our Order type that includes the logic for traversing to the associated Book nodes and summing the price property value of each book.

Here we use the extend type syntax of GraphQL SDL but we could also add this field directly to the Order type definition as well.The @cypher directive takes a single argument statement which is the Cypher statement to be executed to resolve the field. This Cypher statement can reference the this variable which is the currently resolved node, in this case the currently resolved Order node.

54 of 87

Cypher GraphQL Schema Directive

Computed Scalar Field

We can now include the subTotal field in our selection set to execute the custom Cypher query...

55 of 87

Cypher GraphQL Schema Directive

Node & Object Fields

In addition to scalar fields we can also use @cypher directive fields on object and object array fields with Cypher queries that return nodes or objects.

Let’s add a recommended field to the Customer type, returning books the customer might be interested in purchasing based on their order history and the order history of other customers in the graph.

56 of 87

Cypher GraphQL Schema Directive

Node & Object Fields

Now we can use this recommended field on the Customer type. Since recommended is an array of Book objects we need to select the nested fields we want to be returned - in this case the title field.

57 of 87

Cypher GraphQL Schema Directive

Field Arguments → Cypher Parameters

Any field arguments declared on a GraphQL field with a Cypher directive are passed through to the Cypher query as Cypher parameters. Let’s say we want the client to be able to specify the number of recommendations returned. We’ll add a field argument limit to the recommended field and reference that in our Cypher query as a Cypher parameter.

58 of 87

Cypher GraphQL Schema Directive

Field Arguments → Cypher Parameters

We set a default value of 3 for this limit argument so that if the value isn’t specified the limit Cypher parameter will still be passed to the Cypher query with a value of 3. The client can now specify the number of recommended books to return

59 of 87

Cypher GraphQL Schema Directive

Node & Object Fields

We can also return a map from our Cypher query when using the @cypher directive on an object or object array GraphQL field. This is useful when we have multiple computed values we want to return or for returning data from an external data layer.

Let’s add weather data for the order addresses so our delivery drivers know what sort of conditions to expect. We’ll query an external API to fetch this data using the apoc.load.json procedure.

First, we’ll add a type to the GraphQL type definitions to represent this object (Weather), then we’ll use the apoc.load.json procedure to fetch data from an external API and return the current conditions, returning a map from our Cypher query that matches the shape of the Weather type.

60 of 87

Cypher GraphQL Schema Directive

Node & Object Fields

Now we can include the currentWeather field on the Address type in our GraphQL queries.

61 of 87

Cypher GraphQL Schema Directive

Custom Query Fields

We can use the @cypher directive on Query fields to compliment the auto-generated Query fields provided by the Neo4j GraphQL Library. Perhaps we want to leverage a full-text index for fuzzy matching for book searches?

First, in Neo4j Browser, create the full-text index:

In Cypher we would search using the index like this:

CALL db.index.fulltext.queryNodes("bookIndex", "garph~")

CALL db.index.fulltext.createNodeIndex("bookIndex", ["Book"],["title", "description"])

62 of 87

Cypher GraphQL Schema Directive

Custom Query Fields

To take advantage of the full text index in our GraphQL API add a bookSearch field to the Query type in our GraphQL type definitions which requires a searchString argument that becomes the full-text search term

63 of 87

Cypher GraphQL Schema Directive

Custom Query Fields

And we now have a new entry-point to our GraphQL API allowing for full-text search of book titles and descriptions.

64 of 87

Cypher GraphQL Schema Directive

Custom Mutation Fields

Similar to adding Query fields, we can use @cypher schema directives to add new Mutation fields. This is useful in cases where we have specific logic we’d like to take into account when creating or updating data. Here we make use of the MERGE Cypher clause to avoid creating duplicate Subject nodes and connecting them to books.

65 of 87

Cypher GraphQL Schema Directive

Custom Mutation Fields

66 of 87

Cypher GraphQL Schema Directive

Custom Resolvers

Combining the power of Cypher and GraphQL is extremely powerful, however there are bound to be some cases where we want to add custom logic using code by implementing resolver functions. This might be where we want to fetch data from another database, API, or system. Let’s consider a contrived example where we compute an estimated delivery date using a custom resolver function.

First, we add an estimatedDelivery field to the Order type, including the @ignore directive which indicates we plan to resolve this field manually and it will not be included in the generated database queries.

Now it’s time to implement our Order.estimatedDelivery resolver function. Our function simply calculates a random date - but the point is that this can be any custom logic we choose to define.

67 of 87

Cypher GraphQL Schema Directive

Custom Resolvers

And now we can reference the estimatedDelivery field in our GraphQL queries. When this field is included in the selection instead of trying to fetch this field from the database, our custom resolver will be executed.

68 of 87

Exercise: Cypher Schema Directive

  • The similar field on the Book type returns recommended books.

  • How could you modify and improve this Cypher query to find similar books?

Hands-On

Exercise

69 of 87

Authorization

Adding Authorization Rules To Your API Using The @auth Directive

69

70 of 87

The @auth Directive

The Neo4j GraphQL Library provides an @auth GraphQL schema directive that enables us to attach authorization rules to our GraphQL type definitions. The @auth directive uses JSON Web Tokens (JWTs) for authentication. Authenticated requests to the GraphQL API will include an authorization header with a Bearer token attached. For example:

POST / HTTP/1.1

authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyb2xlcyI6WyJ1c2VyX2FkbWluIiwicG9zdF9hZG1pbiIsImdyb3VwX2FkbWluIl19.IY0LWqgHcjEtOsOw60mqKazhuRFKroSXFQkpCtWpgQI

content-type: application/json

71 of 87

JSON Web Token (JWT)

JWTs are a standard for representing and cryptographically verifying claims (a JSON payload) securely and are commonly used for authentication and authorization.

72 of 87

The @auth Directive

isAuthenticated

The isAuthenticated rule is the simplest authorization rule we can add. It means that any GraphQL operation that accesses a field or object protected by the isAuthenticated rule must have a valid JWT in the request header.

Let’s make use of the isAuthenticated authorization rule in our bookstore GraphQL API to protect the Subject type. Let’s say we want to make returning a book’s subjects a "premium" feature to encourage users to sign-up for our application. To do this we’ll make the following addition to our GraphQL type definitions, extending the Subject type:

73 of 87

The @auth Directive

isAuthenticated

Unauthenticated

Authenticated

74 of 87

The @auth Directive

Roles

Roles are the next type of authorization rule that we will explore. A JWT payload can include an array of "roles" that describe the permissions associated with the token.

75 of 87

The @auth Directive

Allow

A customer must not be able to view orders placed by other customers. Adding an Allow rule will allow us to protect orders from other nosy customers.

Here we add a rule to the Order type that a customer’s "sub" (the subject) claim in the JWT must match the username of the customer who placed the order.

76 of 87

The @auth Directive

Allow

Authenticated as user EmilEifrem7474

Authenticated as user BookLover123

77 of 87

The @auth Directive

Allow

Of course we will also allow admins to have access to orders, so let’s update the rule to also grant access to any requests with the "admin" role

78 of 87

The @auth Directive

Where

In the previous example the client was required to filter for orders that the customer had placed. We don’t always want to expect the client to include this filtering logic in the GraphQL query. In some cases we simply want to return whatever data the currently authenticated user has access to. For these cases we can use a Where authorization rule to apply a filter to the generated database queries - ensuring only the data the user has access to is returned.

We want a user to only be able to view their own customer information. Here we add a rule to the Customer type that will apply a filter any time the customer type is accessed that filters for the currently authenticated customer by adding a predicate that matches the username property to the sub claim in the JWT.

79 of 87

The @auth Directive

Where

Note that our query doesn’t specify which customer to return - we’re requesting all customers - but we only get back the customer that we have access to.

80 of 87

The @auth Directive

Bind

Bind allows us to specify connections that must exist in the graph when creating or updating data based on claims in the JWT.

We want to add a rule that when creating a review, the review node is connected to the currently authenticated customer - we don’t want customers to be writing reviews on behalf of other users! This rule means the username of the author of a review must match the sub claim in the JWT when creating or updating reviews

81 of 87

The @auth Directive

Bind

If a customer tries to create a review and connect it to a customer other than themselves the mutation will return an error.

82 of 87

The @auth Directive

@cypher Directive Fields

There are two ways to make use of authorization features when using the @cypher schema directive:

  • Apply the authorization rules isAuthenticated and roles using the @auth directive.
  • Reference the JWT payload values in the Cypher query attached to a @cypher schema directive.

Let’s make use of both of those aspects by adding a Query field that returns personalized recommendations for a customer!

83 of 87

The @auth Directive

@cypher Directive Fields

In our Cypher query we’ll have access to a $auth.jwt parameter that represents the payload of the JWT. We’ll use that value to look up the currently authenticated customer by username, then traverse the graph to find relevant recommendations based on their purchase history. We’ll also include the isAuthenticated rule since we only want authenticated customers to use this Query field.

84 of 87

The @auth Directive

@cypher Directive Fields

85 of 87

Exercise: Authorization

  • Open this Codesandbox which includes the authorization rules defined above
  • Using this admin token create a new user and an order for this user (be sure to include at least one book in the order!):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJCb2JMb2JsYXc3Njg3Iiwicm9sZXMiOlsiYWRtaW4iXSwiaWF0IjoxNTE2MjM5MDIyfQ.f2GKIu31gz39fMJwj5_byFCMDPDy3ncdWOIhhqcwBxk

  • Generate a JWT token for your new user using jwt.io
    • Be sure to use this JWT secret when creating the token:

dFt8QaYykR6PauvxcyKVXKauxvQuWQTc

  • Next, use this token to add a review for the book purchased by this user.
  • Finally, write a query to view the customer’s details, including their order history and their reviews.

Hands-On

Exercise

86 of 87

Other

87 of 87

Resources

  • Slides:
    • dev.neo4j.com/nov-graphql
  • Neo4j GraphQL Library Docs
  • Neo4j GraphQL Library Overview Page
  • Graph Academy Training
  • GRANDstack Starter Project