Building GraphQL APIs With The Neo4j GraphQL Library


This workshop will explore how to build GraphQL APIs backed Neo4j, a native graph database. The Neo4j GraphQL Library allows developers to quickly design and implement fully functional GraphQL APIs without writing any resolvers. This workshop will show how to use the Neo4j GraphQL Library to build a Node.js GraphQL API, including adding custom logic and authorization rules.

Table of contents:
- Overview of GraphQL and building GraphQL APIs
- Building Node.js GraphQL APIs backed a native graph database using the Neo4j GraphQL Library
- Adding custom logic to our GraphQL API using the @cypher schema directive and custom resolvers
- Adding authentication and authorization rules to our GraphQL API


Well, hello everyone, thanks for joining today. We are going to be building graphql APIs with the Neo4j graphql library and Neo4j Aura, which is a hosted database as a service. You don't need to set up local environments. We're going to do everything with these hosted sandbox environments. So don't worry about setting up a local environment, but we'll talk about that in a little bit. These slides are linked at slash graphql-training, which I'll drop another link in the chat here. I would recommend pulling up these slides as we work through this. There are several links and code snippets and things like that that you may want to grab from the slides. So I'd recommend pulling up the slides as we go through this and just having them open in a tab. There's also the Discord, which we're linked at the beginning. So I'll monitor both the Zoom chat and Discord as well. So that's either one. If you have questions, please just drop them in the chat, either in Zoom or Discord, and I'll try to monitor both and share links in both. So my name's Will. I work for a company called Neo4j, which is a graph database that we'll be using today. I also co-host a podcast called So if you're interested in graph technology and you like a podcast format, definitely I encourage you to check that out. So here's our rough outline for today. We'll talk a little bit about what is Neo4j. I think since that was pretty much completely new for most folks, we will spend a little bit of time there, but really we'll be focused on graphql and building graphql APIs backed by Neo4j. So we'll talk a little bit about Neo4j, then we'll talk a bit about graphql and how graphql APIs are typically built. Then we'll take a look at the Neo4j graphql library, which is a node.js javascript library for building graphql APIs backed by Neo4j. Then we'll take a look at adding custom logic to our api. And then in the final module, we will take a look at adding authorization rules to our api. We're gonna be building like a bookstore api. So we'll focus on the backend piece of this, not so much on what do I actually do with my graphql api once I have it built. Instead, we're gonna be focused on just the backend piece. So we won't cover integrating, for example, a graphql api into like a react application or something like that. We will be focusing just on the backend piece. So I said earlier that we don't need to worry about setting up a local development environment. We're gonna be using these sandbox cloud services today. So there's two things that we're gonna use. One is Neo4j Aura DB. There's a free tier of Neo4j Aura. So Neo4j Aura is hosted Neo4j instances in the cloud that are private to us. There's a free tier. So we don't have to think about putting in a credit card or anything like that, which is quite nice. So we'll be using that for our database. Everyone will have the opportunity to spin up an Aura instance. And then for running the code, so we're gonna be building a graphql api using javascript. So node.js, graphql api application. And for that, we're gonna use code sandbox. So code sandbox allows us to run code in the browser, including Node code that runs on someone's container somewhere that they're hosting for us, which is quite nice. So we don't have to worry about dealing with local environment development issues. Each one of these modules, I should say the, I guess the last three modules have a link for a like skeleton starter code sandbox with some starter code. So we'll start that way and then work through exercises, editing some code, writing graphql queries, that sort of thing. So you'll see this hands-on exercise icon on some slides. When we have that, we'll stop for a few minutes and we will dig into some exercises. So I'd like for this to be as hands-on as possible. And as folks have questions, please just drop them in the chat, either in Zoom or Discord, and we will dig into your questions. Great. So let's talk a little bit about Neo4j before we dive into building graphql APIs. So I think most folks said that Neo4j was new to them, which is great. I guess maybe what database technologies are folks familiar with? Have most folks used relational databases or maybe Mongo? Let us know in the chat. If you're familiar with relational databases or document databases, graph databases are a similar concept, right? So this is where we are storing, modeling, querying our application data. The difference with a graph database like Neo4j is one of the first differences that you notice is that the data model is not tables, not documents. The data model is a graph. And when we say graph, we're talking about nodes. These are the entities in our data and relationships connect them. Specifically with Neo4j, we use the property graph data model, which we'll talk about a little bit. Basically what that means is that we can store arbitrary key value pair properties on nodes and relationships. With Neo4j, we use a query language called Cypher. You think of Cypher as like SQL, but for graphs. And Cypher is very much focused on pattern matching. So there's an example, Cypher statement kind of in the upper right of the screen there. In this first line where it says match, and then we have this graph pattern. So match is like a kind of like a statement that's saying, find some data in the graph where this pattern exists. And then the pattern, we draw this sort of ASCII art representation of a graph. So we have address in parentheses. Those parentheses sort of like drawing a circle. That's a node. So we're saying find address nodes with an incoming registered address relationship. So you see, we're sort of drawing that arrow in brackets that's connected to an officer node, and then first thing out from that officer to entity nodes, filtering where the address contains New York. So this Cypher statement is saying, find address nodes where the address is in New York, and find officers that have an address in New York, and then what entities are they connected to? This data set comes, or this query comes from the Panama Papers, the Pandora Papers data set, which is a data journalism investigation that used Neo4j to make sense of complex offshore legal entity structures and like offshore banking. So we'll do a little bit with Cypher today. Mostly we're gonna be focused on using graphql and generating Cypher queries from graphql. So we will write a little bit of Cypher when we get to the custom logic section. But if you haven't seen Cypher before, just wanted to share some of the concepts there, this idea of graph pattern matching. So there are lots of interesting implications of using a graph database instead of like a document database or relational database. One is that we can do lots of interesting graph analytics with graph databases because we can model our data differently and that allows us to, I think, write these very interesting queries that are sort of the equivalent of writing lots of joins in SQL just by sort of expressing this graph pattern, which was quite neat. There are also tools for data visualization, which we might touch on a little bit today. And then we have lots of sort of integration. So I guess if we think of the database as being sort of the core, database touches a lot of different pieces of application architecture, right? So database kind of sits at the core of our architecture, but we need to be able to move data back and forth. There are lots of different sort of use cases that we may have for using a database in the first place. So from the spectrum on the right where we're talking about things like analytics and data science use cases, and then over on the left end of the spectrum where we're talking more about like operational transactional applications that we're building. Maybe we're building like a social network or an e-commerce site or something like that where we want more transactional operational workloads. And we're interested in things like building an api layer that's gonna sit between the client and the database and thinking about the tools we're gonna use there. So for our workshop today, we're very much on this left end of the spectrum where we are interested in building the api layer for a client application. And in this case, we're going to focus on doing that in graphql. Cool, so Anthony says familiar with SQL Server, Firebase and Mongo, great, cool. So now you can add Neo4j to that list after today. So here's some resources. The slides are probably the most important one. So there's a link for the slides in the chat, slash graphql dash training. Let me just drop those in the chat one more time. I know sometimes chat kind of disappears and need a pane to scroll through. So that is not a clickable link. Let's try again, slash graphql dash training. Cool, so like I said, we're gonna use these code sandbox environments and there's a specific link for each one. So if you open up the slides, at least have them in a tab to refer to. It'd be helpful just to grab the links and some of the code snippets and things as we work through the exercises. All the code for today is also linked here., graphql dash training dash code. And these are the graphql queries that we're going to work through in some of our examples. So this might be a good one to bring up as well. And then the other things in here. So we're gonna be using the Neo4j graphql library. The documentation is linked here. There's an overview page for the graphql library. It's links to example apps and other resources as well as a self-paced training that goes into a lot more detail than what we'll cover today. So the timing for today, I should have said it's a three hour session and we're 20 minutes in. So that is the plan for today. Like I said, we're gonna focus on the backend piece of this, not so much the front end aspect of this. So if you're interested in seeing how the pieces fit together for like a full stack application, including how the front end fits in, there's a starter project for what's called GRANDstack. That's graphql react apollo Neo4j database. So this GRANDstack starter is a good place to see just sort of how the pieces fit together in the context of a full stack app, but that's not gonna be our focus today. Today we're focused on just the backend piece. Cool, so we talked a bit about Neo4j, but I think maybe let's look at a more hands-on example. I'm gonna bring up this one. Let's look at this example. This should be public. Let's see, I'll share the link. Sorry, can't remember the password here. I think newsgraph is one of the users. Yeah, so this is optional. I'm gonna go through a couple of queries in Neo4j browser here. So if you want to follow along, I'll drop a link in the chat, and then the username is newsgraph, and the password is also newsgraph. So this is Neo4j browser. This is like a query workbench for Neo4j. So I write Cypher queries, and then I get back the results either like in a visual environment, but this is kind of the common development environment for working with Neo4j. I have data loaded in here from the New York Times api. So this is a data set of news articles. So I think it's continuously running to scrape the most shared news articles each day. So we have things like articles. Articles refer to topics, or they're about people and organizations, refer to some geographic region, and they have photos. So I can run db.schema.visualization to see, let's reset our styling here. There we go. So I can run this to see what my data model is. So I can see the nodes that I have and how those nodes are connected. So articles are about organizations or about a geographic region, about people, they have photos and topics. So then I can write Cypher queries. Like let's say I want to find the most recent articles. I can write a Cypher statement using this graph pattern matching concept. So the pattern for this one is quite simple. I'm saying just find nodes with the label article. So when we say article nodes, what we're saying is find nodes that have the label article. So label is the label of the article. So that's the label of the article. Find nodes that have the label article. So label is like a way to group nodes in the database similar to like a table or like a collection from a document database is same conceptual level to think about these, I guess. Then we have these key value pair properties. So each article has a title, the date it was published, the URL for the article, and then kind of a description. So when we're thinking in graphql terms, what we're going to do is map nodes and node labels specifically, I guess, to types in graphql and how they're connected. Of course, we can write much more complex patterns. So for example, we could say, do we have any example queries in here? Let's see. We've already written some interesting ones. No, that's fine. Let's say, let's find articles that have the topic, let's say exercise. My arrow is going the wrong way. That's fine. And again, you can, don't worry about following along. We're going to start our our Aura instance. But if you'd like to, I did link the username and password for this database. Okay, now we're saying, find the topic exercise. So now to our pattern, we've added a few things. We've added this curly brace. So in this curly brace, we're saying, Anthony says he doesn't see the username and password. So yes, it should be in the chat. The username is newsgraph and the password is also newsgraph. I can put that in Discord as well. Oh, yeah. Let me drop this in Discord as well. Browser. Username is newsgraph. And the password is newsgraph. Cool. So that's a read-only database user. We're not going to use this one throughout the workshop, but I do think it's helpful to have some data in Neo4j to play around with. Great. So in this query, we're saying, find the topic with the name exercise. That's what these curly braces indicate. Curly brace and the pattern are referring to property key value pairs. And then traverse along this has topic relationship to find article nodes that are connected to the topic exercise. And that's exactly what we get here. So here's the exercise topic node. Here are all these articles that are connected. And if we double-click on these, we can traverse out. And we can see, OK, here are other topics of this article. So this one is about golf and exercise. Here are other articles about golf. So we can do that visually. Or we can add more complex patterns, more pieces of graph pattern to the pattern that we're matching on. So we can say, maybe we want to know what other topics these articles have. So we just add to our pattern here. For all the articles that have the topic exercise, what are other topics that they have. And see, there's actually quite a mix here. Cool. So that's Cypher and Neo4j browser. I think that is probably a good sort of whirlwind introduction. We can move on to seeing how this fits in the context of graphql. OK, great. So back to our slides here. So let's talk a little bit about graphql so we're all on the same page. I think some folks have indicated they've done some work with graphql. So some of this may be familiar for folks. Some of this is new. That's great, too. But let's talk a little bit about graphql, separate from working with a database, separate from Neo4j, sort of graphql concepts on their own. So fundamentally, graphql is an api query language and a runtime, I guess, for building and fulfilling api requests. With graphql, we use a type system to define what data is available in the api. So we're talking about what entities, the attributes, and how they're connected. So we define with graphql type definitions what the types are and what fields exist on those types, if they're nullable, if they're non-nullable. And then we can also have relationship fields on a type that refer to other types. That's where the graph piece of graphql comes in. We're talking about how these objects are connected. Then at query time, the client is free to ask for sort of a traversal through this data graph and exactly what pieces of the data they want to be returned at query time. So our graphql operations are going to specify an entry point and then a traversal through the data graph. These entry points, these are fields on special types called query or mutation or subscription. We're not going to talk about subscriptions today. So a couple of important concepts, especially for building the graphql server piece of this and important concepts. So the first one is this concept of graphql type definitions. So type definitions define the data that is available in the api. We typically use what's called the schema definition language or SDL to define these types. You can define your types programmatically depending on what graphql server implementation you're using. But SDL is nice because it's language agnostic. So it doesn't matter if we're building a graphql server in Java or javascript or Python. The SDL is going to be shared because it's not specific to a certain programming language. We also will use directives. So here you can see we're defining some types and we're defining fields that exist on these types. So we have movies, we have genres, we have actors. Movies have a title, it's a string, a year, it's an integer and so on. And then we're saying how these types are related to each other. So movies we're saying has a genres field that is an object array field of genres. When we get to using the Neo4j graphql library and we're thinking of how do we represent the property graph model that we're looking at in Neo4j and graphql type definitions, we use directives like annotations to the schema. So here we've added this relationship directive that allows us to specify the type and direction in the property graph model, since in the property graph model we have a direction and a named type for each relationship. But we'll cover that when we get there. Okay, so that's type definitions. Another important concept is this idea of graphql operations. So like a query is really an operation where we're sending this document, what is the document? The document is the graphql query, essentially, or the operation. So a query is either a query, which is going to be starting with a field on the query type or a mutation or a subscription. And we start at the root level, the entry point, I think about it for the api, which is going to be a field on, in this case, the query type. And that field can actually take arguments. So here we are searching for movies where the title is A River Runs Through It. And then the next piece of our graphql query is the selection set. And so the selection set is this nested structure that I think is important for specifying two things. One is it's specifying this traversal through the data graph. So we're starting from this movie, A River Runs Through It, and then we're traversing to actors, to genres, to directors. And then for the directors, we're also traversing out to see other movies that those directors directed. So who directed A River Runs Through It? What other movies did they direct? That's what this piece of the selection set is saying. It's in this sort of nested structure. Fields in our selection set can also take arguments optionally as well. So here we're only grabbing the first two actors that were in this movie, and then we're only grabbing three movies that the directors of A River Runs Through It also directed. And then you can see when our data comes back, the shape of the data. So in this case, it's a JSON document, exactly matches the shape of our query. So the client knows exactly what data they're getting back. So that is how we write queries and operations. How do we sort of implement the back end logic for a graphql server? Well, this is where resolver functions come in. Resolver functions, these are functions that actually have the logic for fetching data from the data layer. So querying the database, we can even query other APIs. So graphql is data layer agnostic. We can really build graphql APIs on top of any data layer. The example that we have here, this is looking at an application for conference. So we have sessions. Sessions are in a room. Sessions have a theme. We have recommended sessions. So if you like the session, here are other sessions you may be interested in, this kind of thing. And we're using this sort of like ORM api here, assuming that we have that available for querying our database. So in this case, we would start off searching for a session with some query string. And we go to the database and say, find all the sessions by the search term, and that would come back to our graphql server. And then we would sort of iterate through that selection set and see, oh, well, the user also asked for what room these sessions are in. So the room resolver would then go back to the database for each session that matched our search string. So here's the room that it's in and then do a similar thing for theme and for recommended. So you can see that we're doing these in sort of a nested fashion, right? So it's possible that for a graphql operation, we may make multiple round trips to our data layer. This is called the in plus one query problem, which is a common thing that comes up in graphql, which is that we have the possibility to be making multiple round trips to the data layer for a single graphql request. And this can be problematic for performance reasons, right? Like it's incurring some overhead to go to the database, to go to the data layer each time. It would be best if we could in one operation go to the database and say, here's all the data that I need for this graphql operation. And that comes back. So this in plus one query problem, this is something that comes up. There are different ways to address this. There's the data loader pattern, which allows us to do some batching and caching of objects. What we're going to do today with the Neo4j graphql library is see that we can actually generate a single database query. So in our case, this will be a single Cypher query at the root level for any arbitrary graphql request. So what that means is that we won't actually be implementing resolvers. Instead, we're going to let the Neo4j graphql library take care of that for us. And that gives us a couple of advantages here. One is we don't have to write this kind of boilerplate syntax for fetching data from the database. Instead, the library is going to take care of that for us. And then we also don't have to worry about the in plus one query problem because this means that our database query is going to be generated for us at the root level, which is quite nice. So you may have seen the benefits of using graphql. This idea of addressing overfetching and underfetching where we're not sending too much data over the network. We're getting exactly the data that the client needs to render a view. We don't have to make multiple requests like we may in a REST api. If we're accessing different resources, we can just add more things to our selection set to give us the data that we need in our application. And then some of the challenges that come up, we talked about the in plus one query problem. In my mind, I think the way to think of most of these challenges of graphql are that a lot of the practices that come from the REST world don't cleanly apply in the graphql world. So things like error handling is a bit different. We can have errors at different levels in our selection set. So we need to think of error handling a bit differently. Similarly, graphql queries can be kind of arbitrary. So you can't just cache a certain endpoint or a certain URL like we may be able to in the REST world. And then also things like how do we handle query costing and rate limiting, again, related to this idea of arbitrary complexity of graphql queries. So these are some of the challenges that come up. There's tooling and best practices that address all of these, but I think these are just important things to be aware of that may come up. So we're going to use graphql Playground today. graphql Playground is an in-browser tool for querying and introspecting graphql APIs. You may have seen GraphiQL or apollo Studio has something called Explorer, I think, that's built into apollo Studio. Actually, apollo, the linking out to apollo Studio is now, I think, the default with apollo Server. These tools are similar. I like to use graphql Playground because we can just sort of host graphql Playground at our graphql endpoint. So that's what we're going to use today. And we'll take a look at Playground in a minute. Actually, right now, we will. So for this hands-on exercise piece, what I would like everyone to do is go to and you will see graphql Playground at that URL. And let's all take a few minutes to explore the Docs tab, to see what the schema looks like, and then write some graphql queries just to get familiar with the way that, for example, our limit arguments work. So here we're looking for the first 10 movies, grabbing some information, and then we're also filtering for directors. So I'm going to go to I'll drop a link to that both in Zoom and in Discord. So you should see something like this. So in the right, we have the Docs tab. So this allows us to inspect the api. We can see the query entry points. We can see the fields that are available on each type. And we can write some graphql queries. So note that it's quite nice because of this idea of introspection, we can do control space to get this autocomplete suggestions so we can add fields. So here's now we're adding the bio, the biography of each director, which is quite nice. So anyway, let's take a few minutes. Everyone can write some graphql queries and familiarize yourself with graphql Playground if you haven't seen it before, looking in the Docs tab and using control space for autocomplete, add some fields to these queries and copy and paste these. I'll share the queries actually in Discord. So there's the first query in Discord. And I'll drop the link to this other one in Discord as well. So you can just copy and paste those. Great. So we'll pause here for just a couple of minutes to give everyone a chance to get familiar with graphql Playground and this api. And then we'll move on to the next query. So here's the first query in Discord. And I'll drop the link to this other one in Discord as well. So you can just copy and paste those. So everyone can write some queries and familiarize themselves with graphql Playground and this api. And then we will pick up again. If you have any questions or issues, definitely just let us know in the chat. We'll pause for just a couple of minutes. And then a couple of minutes. Thanks for watching. Thanks for watching. Thanks for watching. Okay, so hopefully everyone had a chance to play around with this api, see what data is available. So we haven't talked about how this api is implemented. Our friend says options for sorting like by last name, descending or ascending. Yeah. So let's take a look at our sorting options here. So let's do movies, limit 10, and let's bring back a title and year. So to see options we have for things like sorting, there's a couple of ways to do this, right? So we could look in the docs tab at the movies, query fields, and we can see the arguments are where, so this is going to be for filtering. We can filter based on fields or our options input takes a limit and an offset. So that's like basic pagination and then sorting. We can sort by, so in this case we're sorting movies, right? So we can sort by things like title and then either an ascending or descending order. So we could say something like sort by title, descending. And we have, I guess we have some characters with accents and things like that so they show up at the end of the alphabet. So ascending would give us, yeah, quote characters show up first. We can also do something like sort by year, something like that. So 1902 is the oldest movie that we have. Cool. Anyone want to come up with any interesting queries that they want to share in the chat? Otherwise we can move on. Okay, great. Let's move on here. Let's make this a bit bigger. There we go. Cool. So let's move on and talk about building our own graphql api. So we're going to use Neo4j Aura for this. So everyone should follow these steps to spin up a Neo4j Aura instance if you haven't yet. Basically, what we'll do, and I'll walk through this, is we'll go to slash Neo4j Aura is the link. So I'll drop that in the chat and zoom and discord. So Aura is a managed cloud service for these hosted Neo4j instances. There's a free tier, which was really nice so we can spin up these Aura instances to work with Neo4j for like hobby projects or development, which is quite nice for what we're doing today. And then there's professional tier and enterprise tier when we're ready to build a production application. So let's do the free tier. So we'll need to sign into Aura. So you can sign in with Google or create a username and password. So I'll sign in with Google. And the first time you sign in, I think you're have a bit more of a guided workflow so you may see something a bit different that is prompting you to launch a database but what you want to do is create a database, and specifically, we want the AuraDB free instance we don't want to put in a credit card or pay for this or anything but you can see if we choose professional, we have different ways we can scale the database and things like that. So for today we want the free tier so these the free tier databases these are available for us to use these you stay around in perpetuity, I guess they don't go away. There's just a limit on the size of the graph so these instances are have constrained resources so we can't do massive amounts of data but for hobby projects and we're going to do today that's perfectly fine. We're going to add a name, we'll call this about graphql workshop, we choose different regions. Whatever it recommends is fine. That works, and then we have an option for starting with a movies data set already loaded or a blank database we're going to add all the data we're working with, with graphql mutations today so we'll just create our own data in a blank database in a click, create database, and the first thing that happens is asks me, or tells me rather my password so I copy that password I'm going to save that somewhere else. We're going to come back to using the password in a moment so definitely be sure to copy that and save that somewhere. And then you will see this message that we're spinning up your database, and I'll take a few minutes so let's pause again for a couple of minutes and give everyone a chance to sign into aura and provision and start a free tier instance. Be sure to copy your password and save that somewhere, since we will need that later. And then you'll see in the console application that your database is starting up, and then you should be good to start moving on and building our graphql api so we'll stop here for just a couple of minutes to give everyone a chance to go through that if you are stuck on anything or have any issues definitely let us know in the chat either on zoom or discord, but we'll start again in a couple of minutes. Thanks for watching. you you you you you you Okay, was everyone able to sign into aura and create a database really start the process of spinning it up. He says yes. Okay, great. Let me know in the chat if you got stuck on anything. But let's move on. We'll talk a little bit about the new take graphql library what what this does for us and then we will get more hands on with some code and build a graphql api. First of all, then you take graphql library, this is a no JS javascript library that makes it easier to build graphql api is backed by near for J. So, like, at a fundamental level we're talking about building this api layer that sits between the client and the database, where we may have things like our authorization, we may have some custom logic. We don't want to just expose our database to the world right there, there's still this need for the api layer so we're not talking about just sending graphql directly to the database so that's at a fundamental level, what we're doing. At a more specific level, the goals of the new take graphql library are support this idea of graphql first development where we're using our graphql type definitions to drive the database data model. And then also the, the schema for our api so we're not maintaining two separate schemas one for the database one for the api. We can of course configure the api. So that we're not simply just exposing our database, we do this through graphql schema directives we talked about this relationship directive which we'll see in a minute but they're actually quite a few directives we can use to do things like configure So we can configure what we're exposing through our graphql api, we can add custom logic with cipher we can add authorization rules, and so on all through this idea of directives. The other thing that the new to graphql library does for us is basically takes our graphql type definitions where you define what data, we have that we want to work with, and then generates a graphql api that has crowd operations so create read update for each type that we have defined so in graphql parlance these are things like generating the query and mutation types to the entry points for the api, adding arguments for things like ordering and pagination complex filtering. Also adding support for working with things like the daytime like the temporal types and the spatial types that are available in the database. And then also some things for aggregations and more complex operations. All those are generated for us, we don't have to add those explicitly to our schema. So query time for any arbitrary graphql operation that we send a single database query is generated for us by the new to graphql library so that means we don't have to write these resolver functions. The new to graphql library takes care of generating the database for us so we don't really need to think about that. Think about in this case writing cipher queries to build our graphql api. We don't need to do that. We only need to think about cipher when we get to thinking about things like adding custom logic. For developer productivity right so we can get a graphql api up and running back by database, basically just by writing some type definitions. And then the performance aspect so this solves the n plus one query problem for us by generating a database query and sending it to the database to figure out how to optimize and graph databases like Neo4j are really good at traversing the data graph. That's what graph databases are optimized for so oftentimes in graphql, we end up creating these queries have a very nested structure very nested selection set. And so graph databases are very good at traversing that graph in a database implementation terms what's going on there. There's something called index free adjacency, which means that in Neo4j in the database we can traverse from one node to any other node that node is connected to so traversing along relationships. We can do that without using an index so in a relational database to do the equivalent of that which would be a join. We're using an index to see where to tables overlaps like a set, set operation, and that has to use an index, which means that that slows down as the size of those tables grows. But in Neo4j, the traversal so the equivalent of a join. We're basically just chasing pointers going to an offset, which is something that computers are very fast at so it's like more of a constant time operation that's only dependent on the number of relationships that we're traversing not on the overall size of the data. So as we add more data to the graph, our traversals from one node to another. Stay the same performance for this so that's under the hood what's what's going on. And that's why graph databases are really good for traversing relationships. Okay, so that that's one of the goals of the Neo4j graphql library. The other is to expose custom logic through cipher so we said that the library generates crud operations. Based on the type definitions we don't have to write any cipher any data fetching logic, we just have to basically write our type definitions. And that's great for basic crud operations but what what about when we have custom logic. Well, and for that, we can use cipher statements in our graphql type definitions, using a cipher schema directive so schema directives these are like annotations in our graphql api and our type definitions that are basically indicating that some custom logic should occur so it's saying that there's some custom logic that needs to happen server side allows us to basically extend things in graphql so this cipher schema directive. This is like an annotation that we would add to our type definitions it takes a single argument, which is a cipher statement so in this example, we're adding a recommended field. I think this is on a on the business type so this is from an application that has businesses and user reviews of businesses. Think of something like I'm searching for restaurants and I want recommended restaurants so I know a restaurant that I like your other businesses other restaurants that you may like as well. So the cipher statement here is finding users who have reviewed the business that we're currently resolving, and then looking at other businesses that those users have reviewed and using the number of overlaps as as a score for the recommendation So this is a, let's call it a collaborative filtering recommendation query, where we're using information from other users in the network to generate personalized recommendations, this is a, a fairly basic example of collaborative filtering, I think the idea applies for more advanced cases but this this kind of traversal is very expressible in cipher in graph databases. So now we're able to add this recommended field to our business type to the client of our api. They don't know that this is a custom field with this logic defined by cipher they just see a recommended field that is giving them an object array business objects. So I think this is, this is my favorite feature of the new photographic library this is super powerful because what this means is that we can use any functionality that we have in cipher, we can expose that into our graphical api, which is super powerful. You'll notice, I guess, one thing that is interesting which we have a this keyword here this is a referring to the currently resolved business object in this case. So that's kind of like a special variable that gets injected into the cipher statement. So, let's look at how we would get started with Neo4j graphql library. We don't need to follow along with this we're going to jump into a code sandbox example in a minute but I want to just go through the concepts. So, we will install Neo4j graphql library and its peer dependencies, which is the graphql JS reference implementation, and the Neo4j driver. The, this is the Neo4j javascript driver since we're building a node.js api. The database drivers they allow us to create a connection to the database whether it's running locally or running in the cloud somewhere. All we're going to do with it is just create that connection and then pass that driver off to the Neo4j graphql library. So in this case we're using apollo server, which is quite nice and simple for creating graphql servers but we could use any graphql server, any javascript graphql server implementation with the Neo4j graphql library. Here's kind of the minimal viable snippet. Let's get into what's going on here. So the first thing we do is import our dependencies so graphql the driver and apollo server, then we define some type definitions. Just looking at movies and genres and notice here that we're using this relationship schema directive so we said that in the property graph model. The property relationship has a direction and a named type graphql doesn't have that same concept for relationship fields, so we need to be able to encode that data because we're driving the database model from these type definitions so we need to be able to specify that somewhere so we do that with this relationship schema directive to specify the type and the direction. Then we create a connection to our database using the Neo4j javascript driver. There's a connection URI, which this is the default for if we're running locally with like Neo4j desktop or Docker, or something like that. And then there's a few different ways to authenticate we're just using a username and password. Then we create an instance of Neo4j graphql class passing off our type definitions and the driver. And then that gives us a executable schema object that we can then hand off to apollo server which is then going to serve our graphql api. We're not writing any resolvers you don't have to write any data fetching logic. Basically all we're doing is writing type definitions, handing that off to the Neo4j graphql library, get back an executable schema object and pass that off to apollo server. If we were to do this then we would have, we would have a graphql endpoint running locally so that's the basic idea of what we're doing. You can see that most of the interesting aspect here is going to be in writing our type definitions, configuring those with schema directives, handing that off to the Neo4j graphql library. Great. Well, let's get hands on with an example in code sandbox. What we're going to do for the rest of the workshops we've got about an hour and 45 minutes left. So what we're going to do for the rest of the workshop is build the graphql api for an online bookstore. So, the first thing that we would typically go through this graph data modeling process. Now, someone has already done this for us in this case we're going to be starting off with an existing schema. But I think it's helpful to talk about how we get to the, the types, how we get to the data model that we ended up with, and I think of this, this graph data modeling process is kind of this iterative process where the first thing we do is identify what are the entities in, in our business requirements essentially so we start off with some business requirements that are like I need to, as a user, search for books by like a search string or search for books by subject. I need, as a user, I want to be able to add books to an order. As a user, I want to be able to submit a review of a book, these sorts of things are our business requirements, we go through identify the entities. Think about what are the properties that we're storing so books have an ISDN which is basically like an ID field they have a title. How are these entities connected those become relationships, and then sort of draw out the graph model. And then the next part is to think of the questions that we have from our business requirements so maybe I need to view all of the books in an order for a specific customer and I think well can I traverse the graph to answer that question well can I go from a customer to an order from the order note I can traverse out to the books contained in the order. So yes, I can answer that question by traversing the graph this way I go through each of those for my business requirements. And then, if I can answer all of the questions I want to answer that I'm done my my graph model is ready to go if not that I may need to iterate on that a bit so I think I think of this as a very iterative process. So this is the data model that we are going to use today so we have books. Books have title price and description books are about a subject. We have authors are authors of books we have orders that contain books and ship to an address. Customers can place an order and customers can also write reviews that are connected to a book. So this, this data model by the way we use this tool called arrows. arrows dot app. I'll drop a link to in the chat and discord. To basically diagram our data model so fundamentally arrows is a graph diagramming tool, but it's very convenient allows us to express the property graph model so for example here's this isn't exactly what we're working with this from a similar project but it's very similar. So we can drag out nodes so maybe user rotary view, and then a review needs to be connected to a book. So we can update our model this way, and then we can check this in to version control we can export say like the, the JSON for for this and check that into version control, which is quite nice. This one, did we lose the label book. We can also export graphical type definitions. Although it doesn't think I have a label on book. For some reason, it's there. Let's delete our. Oh I had a duplicate. Book. So, we go through this data modeling process in arrows, and we can export these as need a, at least one property so book. Let's give it a title. There we go. So now we're generating graphical type definitions that we can just copy and paste and hand those off to the new FJ graphical library from just this diagram so this is really nice. To quickly go from diagramming our data model to generating graphical type definitions. Basically copy and paste into some template code and we have a graphical api, which is super powerful. Okay, so the next thing we want to do is set up our code sandbox environment so we're going to start with this specific code sandbox so there's there are three of these for the remaining modules so we'll have a starting point for each one of these And what we'll have to do is, everyone will open the code sandbox, and then fork it, so you'll need to sign in to code sandbox. If you're not already signed in but it's free to sign in with something like think you can use GitHub or email. And then that makes the code sandbox private to you so you can only you can edit it. And what we're going to do is configure our initial code sandbox environment to point to our Neo4j aura instance. So we're going to open the code sandbox link here, which I will put in the chat dot com slash graphical dash SB one sandbox one and you'll see something like this. The first thing that we want to do is click this fork button so you can see here, you look in this long URL this is just pulling in some code from GitHub, so all the code for the examples will look at is on GitHub. But we're going to fork this. And then that's the point we need to sign in it looks like I'm already signed in so when you click fork it'll ask you to sign in, and then give you a new code sandbox, which only you can edit. And we have code editor and then we have the web app and in this case graphical playground running on the right here and the terminal. So we're just going to edit all of our code in code sandbox in the browser here, but we have a basic starting template. Let's look at what's going on here. So this, this will be in structure pretty similar to the getting started example that we looked at with a couple of things added so we're pulling in your geographical library apollo server, and the Neo4j javascript driver. And then a few utilities. We are loading some environment variables from this file. So these, yours may be blank or you may have old values here, we're going to replace these with the values for our aura instance. Let's go ahead and do that so I'm going to go back to the aura console. And I'm going to grab the connection URI. And then my password was generated. When I started the instance and we said we needed to save that somewhere. So we'll paste that in. And then when I save this will go file, save. When I save that it will restart my application, because we're using node mon, and it will now be connected to my Neo4j or instance, which I started up in the previous module. Okay, so we're reading some environment variables, then we're reading this schema graph QL file into our type that so schema graph QL. So we look at this this matches the data model that we were looking at a moment ago with customers and orders and books and reviews. Then we hand off those type definitions. When we instantiate the Neo4j graph QL class, we create a driver Neo4j driver instance. And this is where we're reading those environment variables so the values that we're setting here in this .env file are being pulled in here as environment variables to specify our connection to Neo4j. Then we hand off that executable schema object to apollo server starts our graph QL api, which if we look in the right side of the screen so you can. If you need more screen real estate you can copy this URL and open that in a new tab. So, this will be private, this will be a different URL for your code sandbox, but that can be nice. I'm going to do everything from here though because I want to see the terminal the console results logged here. So if everything is working if everything is wired up we should be able to. So for example, click on the Docs tab here and see our entry points so we have query fields for orders, orders count, this is like an aggregation customers addresses and then we have all of these mutation operations for creating updating and deleting orders and books and such. So if everything is wired up correctly we should be able to run a query, how about give me all books and title of each book. And so I run that of course I get back a empty array, because I don't have any data in the database. But I can see that I didn't get an error so actually was able to query the database, and if I look in the console here, I can see a few things log so it's telling me that, okay there's an incoming graph QL request here's the query, no variables. There's no JSON web token. We will definitely use JSON tokens when we get into the auth section. And then here is the generated cipher query so the new graph QL library has taken this graph QL request and generated this cipher query and sent that to the database. Nothing comes back because we have no books, but this is neat because I can add other fields so that's also, I don't know say bring back the price of the book and I can see now that the cipher query includes the price property as well. If I wanted to add more complex operations do I have. What else do I have for book do I have any reviews. I can search for reviews for all the books. And of course, I'm still getting an empty array, but the generated cipher query now includes this graph pattern for finding any reviews connected to the book so the cipher query is generated specific to the graph QL operation, which is quite nice means we didn't have to write any data fetching logic. Okay, so we're going to use this as kind of a skeleton starting point for adding some more things to our api. So, let's pause here for a couple of moments, give everyone a chance to open the code sandbox link. That's this link here slash graphical dash SB one, which should be in the chat both on zoom and discord, and then fork the code sandbox and update the dot EMV file with your connection credentials for your aura instance. And then that will restart your graph QL api application and code sandbox and write a simple query just this books title one is good, and you should get back an empty array. You shouldn't see any errors. So you see an area we knew that something went wrong. But that's the basic setup so let's pause for a few minutes, you're going to chance to go through that. If you get any errors or have any issues, definitely let us know in the chat. And we will get that figured out because we do want to be connected to our Neo4j aura instance going forward because we're next we're going to see how to add some data with graphical mutations, and then we'll need to add some things to our type definitions that we left out so we'll see how to add some types and fields in our graph QL api. Okay, how is everyone doing. Does anyone have their code sandbox setup working connected to their aura instance. Is anyone getting some errors. If not, then I think we can move on. I don't, I don't see anyone complaining in the chat that they had errors so I'm gonna assume that we all got our code sandbox up and running and we're connected to our aura instance, and we are ready to move on. So this is yeah so this, this is the steps that we want to go through if we, if we didn't provision our instance. Previously, we definitely want to grab the connection string from the aura console. Here, so this is the connection string for our database. And then, when we first spin up or first provision the aura instance gives us the password, which we're going to take both of those, put those in this.env file in our code sandbox to get our graphical api connected to our aura instance. Cool, so hopefully that's where everyone was able to get to. So let's talk a bit about the type definitions that we're working with so if you look at the schema graph ql file in our code sandbox that's this file here. You can see that these type definitions correspond to the graph data model that we came up with earlier. When we went through this graph data modeling process of thinking about what are the entities, how are they connected, the sort of thing. There's a couple of interesting things that I want to point out one is the relationship graphical schema directive which which we mentioned before, we used to specify the direction, and the type of the relationship. You can also see some other directives that we're using. So the timestamp directive, which we have on the order place that field. And that is of type date time. So, first of all, we're using date time. We also are using this point type. These are added by the new photograph ql library to expose of temporal and spatial types that are available in the database of point date time local date time dates, these kinds of things. The timestamp, though, is a directive that we can use to indicate that we want this value to be automatically generated for us in the server. So, when we create an order, this is saying, set the place that property be the current date time. You don't want the client to be responsible for that you don't want the client to say yes this is this is the current time that can be a pain for the clients to also be a security vulnerabilities one just to set that on the server. The ID directive is similar that's saying, well, this is the field we want to use to identify uniqueness but also we want a UUID to be generated for us automatically when this order object is created so generate a random unique ID set that as the order ID value. The client doesn't need to be responsible for that. So, these are some examples of the way that we're using directives to configure the schema that we're creating. A few more directives if we look in the documentation, drop a link to this in the chat also this is the documentation page for the new to graphical library which, which is helpful to have up as we're working through this, but you can see here there You can see here there's quite a few directives that we use for configuring our schema so for example, the auth directive, which will be using quite a bit in a moment allows us to add authorization rules to our schema. Okay, so that's directives. Let's take a look at adding some data to our database using Graphical mutations. I'm going to switch to code sandbox here. And hopefully that's big enough to see. I'm going to do everything in code sandbox here so that we can see the generated ciphering that is useful to sort of reason about. Okay, so what do we want to do we want to create some data in our database using graphical mutation. So first of all, if you look at the docs tab. So far the examples we've been using have all been query operations but we need to start with creating some data so we can see we have create, delete, update mutation generated for each type that we have defined. Here's the one for orders or takes create orders takes an input of order, create input type, and we can see takes things like the shipping costs, the customer books, which can be used to connect in this case to existing books or to create new books so you can see actually some fairly sophisticated things we can do here if we think about these different operations that we have available in this input object but let's let's start simple let's let's start with creating a single book. So we're going to look at the mutations, create books, so this book create input is what we're going to be working with so we'll say mutation. Create books an input object, actually, this is an array of input objects we could create multiple books in one operation, but we'll just do one so book is going to have an ISBN number. We'll just make this up to ISBN this is like the, I don't know, like the ID of the book in some international format. So let's create our book graph algorithms that is available for 3748 has a description examples in near for J and Apache Spark. And then, for our selection sets. title is being price. Right, basically the fields that we specify and then we also have this info object, which will use to tell us the number of nodes were created, if any. Okay, great. So let's run this. And says yep, we have now a book called graph algorithms. We created one node. If we scroll down in our console we can see the generated cipher query, which is neat so it's doing a great statement, setting some property values. Now we could open this up in your browser to verify that we actually did create some data. So let's, let's go ahead and do that. If I go back to aura. I can go to open with, and I can either open your browser, which we saw before or near to bloom bloom is like a no code visual exploration tool. So bloom is nice. If I want to visually explore graph data without writing any cipher. But let's do browser for now. And then I will need will close this so it starts with a helpful guide will close that. And then I need to put in my password which I saved over here somewhere. Okay, so I'm in. And I can write a cipher query that says match on everything, return everything. So this says find, find all nodes. Previously we had like a, like, colon article or something in here that was the label. This a refers to the variable that we can use to refer to any nodes that we match on this pattern later on. So, this just says find all nodes in the database and return them and says, Yep, you have one book for the properties of the book so that's what we created here. So we've created a single node that's, that's fine, but we have a much more rich data model to work with. So let's look at creating a review. So let's, let's run another mutation operation. I'll say mutation. And this is going to be create reviews, which is going to take input object. Oh, my container has hibernated. Let's restart it. Do I need to restart this browser. Okay, so I just need to restart our container. Yeah, so we're writing this create reviews input so review takes a rating let's give this book, five stars. This is really the best overview of graph data science really is a great book. So that's the review, which will create the review node, but we need to connect this review node to the book know that we created earlier so we have a book field. So we have two options here we can do a create operation so we can create a new book, or we can connect to an existing book. So this is a review for the graph algorithms book so we're going to connect to an existing book. So we have a where that we can use to specify some filtering so we should probably use the ISP in here, since that's like the canonical ID that refers to the book but I can't remember what I typed I just made something up so let's just use title. But note it's interesting all of the options that we have here for filtering we can basically do any filtering on any of these fields of our book type, but what we want to do is filter for where the title is graph algo rhythms. So this is going to create a review node, set the text and the rating and then create a relationship to the existing book, and then our selection set, we'll bring back any reviews that we created how about text rating. And then also we want to make sure that it goes for the right book. And then we also have this info object which we can use to return the number of relationships created so we'll run this. Some properties from our review says yep connected to the graph algorithms book. If we take a look now in your browser so we can either write a new Cypher query just double click on this to traverse out. So we have now a review node that is connected to the graph algorithms book, and it has a rating text and a created at timestamp, and remember, we didn't have to specify that created at value in our graphical mutation. And this was set by the server because we use that timestamp graphql schema directive. That value is set for us. Cool. We can also create more complex structure so these mutations have these nested create connect operations so we can actually create more complex sub graphs, let's try running this one so instead of typing this one out I'm just going to do this. What am I missing, I'm missing I'm missing the selection site. And then this piece. Okay, so this is actually a fairly complex operation so what are we doing so now we're starting from create customers. So we're creating a new customer. And then, for the reviews for this customer, we're going to connect we're going to create a new customer node and connect relationships to reviews as customer has written in this case. Where the text says best overview of graph data science, which should match on this note we should, we should be using an ID or something for the review but this works. Okay, then we're going to create orders for the customer. And then we're going to add the graph algorithms book to that order for the ship to which this is the relationship to the address so if we look at our type definitions for order. We see that order has books has a customer, and it has a ship to relationship field, which is connected to a single address. So this address node has address and location, which is a point, which we're specifying here in the input for creating our address node, which will then be connected to the order which is connected to the customers you can see this. So this is the structure that we're creating in a single graphical operation so let's run this. And again, we could we could look at the generated cipher query here. If we wanted to ends up being fairly complex again because we're, we're creating several nodes and relationships, and we can find this in the browser so now in your browser we can traverse out to see. Yep, here's this user who wrote a review of this book they also placed this order. And the order has a auto generated order ID. This is because we use the ID schema directive to say hey we want to auto generate this. And then we have the place that timestamp which is also auto generated for us. And we can see our shipping to this address. That contains this book. Cool, so that is this concept of nested mutation operations where we can actually create lots of things in a single graphical mutation can create nodes we can create relationships, and so on. Great, so if you were if you were following along with that, we're going to clear out our database. There's a mutation here in this slide paste this actually let me do this now. What we want to do is delete all the data in our database. So if you didn't run any of these mutations. You don't need to worry about deleting the data, but then we're going to run this mutation, I have a nitro on discord so I can't paste but I think I can send this as a snippet. Let me do this, I guess I'll do this in two. Let me send one for the selection set. There we go. So, if you have the slides open. This is slide 50, which is this mutation operation I also pasted it in discord. So, what we want to do first is open up Neo4j browser. So, in the aura console. You should see your Neo4j instance listed here. Go to open with Neo4j browser will be prompted for your password here. And then we're going to run this Cypher query, so match. Discord to match a detach. Delete a. Make sure you're in the right Neo4j instance because this will delete all data in the database. And once we do that, then we want to run this big long mutation which is going to create some sample data for us to work through the next section. So I'm going to go to Neo4j browser. Say match a detach delete. The reason I say detach. So, this is matching on all nodes, and then I could say delete a and that would delete the nodes, but I can't have relationships without the nodes so the detach is just acknowledging that I want also want to delete all the relationships connected to all the nodes that I'm deleting as well. Okay, so I've deleted five nodes says no I don't have any data in my database. And I'm going to Copy this mutation and go into graphql playground here. Paste this and run it. And we've created three books and some customers. You can look at this in the database. This is what we created three books, some orders, some customers, some reviews, some addresses, and so on. So basically some sample data that we want to work with. So we'll pause here, give everyone a couple of minutes to clear out their Neo4j instance and then copy and paste this mutation and verify that you have created some data in Neo4j. Okay, so this will be the sample data that we'll use to take a look at some of the features that are added for querying with the Neo4j graphql library and then also, we will then take a look next at adding custom logic. Before we get to custom logic, the next exercise will be on adding some missing types. So if you're running ahead of us a bit, you can take a look at the next exercise if you want to see how we would update the graphql type definitions to add authors and subjects, which we did not include in our initial schema. Let's pause here for the top of the hour, two minutes, if you need to take a break or want to make sure that you've got this mutation working. So if you have any errors or have any issues, definitely let us know in the chat. Otherwise, we will get going in two minutes. Okay, so we're going to take a look at some of the features that are added for querying with Neo4j. So we'll take a look at some of the features that are added for querying with Neo4j. Let's pause here for the top of the hour, two minutes, if you need to take a break or want to make sure that you've got this mutation working. Okay, was everyone able to run that mutation to create data in the database? Give a shout in the chat if you weren't, if you had any issues with that. Otherwise, we will move on to take a look at how we can query this data that we've now created. Okay. Let's look at how we query this data. So we looked at the mutations that are generated for us. We saw this concept of these nested mutation operations where we can create nodes, connect to existing nodes, create relationships. Now let's take a look at the query side of this. So we've defined our type definitions. We didn't define explicitly the things like the ordering and pagination and filtering all these things. We didn't define those ourselves in our type definitions that was added to the api for us by the Neo4j graphql library. So let's take a look at some features of the things that were generated for us that enable us to query this data. By default, every type that we've defined in our graphql type definitions have a pluralized top level query field. So type book becomes books and order becomes orders. Just to refer, just to indicate that that is an object array field. So each one of these then becomes a query entry point to our api. So one way to think of this is the query field, the entry point to the api, that becomes the starting point for a traversal through the data graph. And of course, as we add more fields to our selection sets, the database query that is generated behind the scenes takes that into account to resolve all the data for us that we've requested. For sorting and pagination in the options input, or in the options arguments, we have input options for sorting by fields of the type in ascending and descending order. We can do offset based pagination using limit and offset. So give me the first 10, skip 10 for the second page, skip 20 to the next page and so on. And this is where the count queries. So we saw there's a books entry point. There's also books count, which can tell us the total number of books, so we can use that to calculate our slices for offset based pagination. So we know how many total books there are, we know we're doing them in slices of 10, so we know how many total pages we're going to have. We can also do cursor based pagination with the Neo4j graphql library using relay connection types. So here, for example, we are searching for a specific order. And then there's a books relationship field. There's also a books connection field that's available. The books connection will give us the relay spec connection types, which give us things like page info for example to get a cursor to allow us to do cursor based pagination for iterating through these connections. We then use this edges and node relay style syntax for grabbing the actual nodes. So in this case for grabbing the actual books that are connected to this order. So this query is saying, find a specific order, and then use cursor based pagination to paginate through all of the books in the order. And if we have an order that has a lot of books, this may be useful. So different options for pagination. For filtering, there's a where argument that is included. This is both at the query field and also nested within the selection set we can also apply this filtering, the filters that are available for each field will depend on the type of the field. So for string fields, we have common string comparison operators so things like starts with contains ends with this sort of thing for numeric fields you have greater than less than those sorts of things. So here we're searching for all books that have a price less than 20 I guess this would be $20. So I mentioned that the where arguments can be used at the roots query field or also nested in the selection set. It's important to note that the filter is applied at the level where the where argument is used. So for example in in this example we're searching for books, where the price is less than 20. And then for those books or the price is less than 20, we're grabbing all reviews that were created after a certain date. So we're filtering the books, and then we're filtering the reviews we're not sort of saying, only show me books that have a review created after the state. This filter for the date is being applied to the reviews of the already filtered books, if that makes sense so the filter is applied at the level in the selection set where I use. We can filter based on geo distance. So, on our address nodes we have a location property that is a point. So latitude and longitude point for any point fields. And there's a filter generated that allows us to search by radius distance within points so here we're saying find all addresses that are within one kilometer of this point, this is somewhere in the area in California and we get an address in San Mateo, which is the only one that's within a kilometer of wherever this point is. So we just said on the previous slide that the filters can be nested but they're applied at the level where we use the filter argument where we use the where argument essentially. So what if we want to what if we want the filter to be applied, sort of higher up. So in this case, we want to filter orders so we want the filter to be applied at the root level. So what we want to filter on is orders that have an address node, where the, the location of the address node is within one kilometer of this point. So in that, we use filtering through our relationships so we create kind of a nested structure in our where argument so here we're saying, find orders, where the ship to location distance is less than one kilometer from this point so you can see we're nesting the ship to filter in this where argument and we end up with only one order that was sent to an address within one kilometer of this point. Great, so that is a look at some of the generated query semantics that are generated as part of the Neo4j graphql library, we want to do next is a hands on exercise to update our graphql schema so we left a few things out, we need to update the schema for graphql files we need to update our type definitions to include author type and to include subject type. And then once we've updated those types in the schema is also think of the relationships that we need to add. Then we want to run some graphql mutations that are going to add the authors and the subjects to our database based on the table below so we have three books. We have the authors here of the books, and we have the subjects for each book as well. So let's pause, maybe five minutes or so. And let's pick up again at 16 after the hour so in the code sandbox that we have open update schema graphql file to add the author and subject type also think of the relationship directives that you'll want to use to connect author and subject to books. And then see if you can write the graphql mutations to add authors and subjects based on the table here to the graph. If you get stuck, the solutions are linked in the readme file of this other code sandbox but let's, let's pause for five minutes, and then we'll come back and take a look at the solutions. If you get stuck if you have any issues or questions, feel free to ask in the chat. Thanks for watching. Thanks for watching. Thanks for watching. Thanks for watching. Thanks for watching. Thanks for watching. Thanks for watching. Thanks for watching. Okay, so that was five minutes, I think. Did anyone, anyone get through this was anyone able to update the graphql type definitions to include author and subject, and then craft the mutations to add those to the graph. I'm able to get that done. Okay, well let's take a look at the solution so we have got about 40 minutes left, and we have a section on custom logic in the authorization section, which I want to make sure we cover so maybe let's take a look at the solutions for this fairly quickly and then move on here. So, my container has been hibernated, I'll refresh that yeah this happens. So this happens sometimes with code sandbox that will go into hibernation, we need to give it a restart. So, overall code sandbox has been pretty good for these sorts of workshops I think it's, it was a trade off right between these using online tools like this or getting your local development environment setup which can can sometimes be a challenge in these sort of online workshops but I like code sandbox. Like I said, we need two types. So we're going to add an author. Author has, I think just what like a name and subject, which also probably just has a name will make these fields required to be available that's what this exclamation point means that to create an author node, it needs to have a name to get a subject node it needs to have a name. We need a relationship field so an author, an author can have written one or more books. So we'll make that a object array field. And then we want to use our relationship directive. So, the author. Let's say author wrote a book, wrote, and then the direction. So, we're going from the author to the book author wrote book. So in this case from the author, the direction is going to be out. And then similar for subject subject will also have call that field books and relationship type. Should we call this one, a book is about a subject. In this case, we're going to go from the book node the book will be about a subject. So, for the subject type the direction will be in. And then we also on the book type, we want to be able to go from the book to the author and the book to the subject so we'll say authors I think so a book can have multiple authors, so this I think should also be an array field. The relationship type, which we said was wrote. So from the point of view of the book, the direction is going to be in because we're going to go from the author in to the book. Similar for subjects so a book can have zero or more subjects really relationship directive. So the relationship we said for this one was about our double quote there so direction. This case is going to be going out for saying the book is about subjects. So he's dropping Yeah, thanks for joining. There's a recording I think that will be sent out. And, yeah, thanks for joining. Okay, so we'll save this. I did a command as we can also go to appear file save and see this triggers a restart of our graphql api application. And I think we need to refresh this browser to get graphql playground to reload the type defs. But now if we look in the docs, we should have authors, we have authors and subjects. Cool. Okay, so we had this table. And to write graphql mutations to add the authors to our books, so let's do that so we have a book called inspired that was written by Marty Kagan, so let's take a look at how to do that. So that's going to be this going to be a mutation. And we need to decide what way we want to start there are a couple of ways we could approach this we could do update books and search for the book, and then Or do a create operation rather from the book to create a new author, or we could do create authors. Maybe let's start with that so we'll say create authors author has a name that Marty Kagan, and then Marty Kagan wrote a book, we already have in the database, so this is going to be a connect, not a create. So we're going to connect where the node so you may notice that we have to say connect where the node. If we had properties on the relationship so we haven't talked about this yet but so far we've been storing properties on the nodes. If we had properties on relationships, we would have another option here to filter based on the edge object, which would, which is basically which is anyway that's why we have the node here seems like an extra layer but it's simply because we don't have the edge filter. So we don't have any relationship properties. Anyway, we want to filter it where the title is inspired. And let's get some more real estate, so we can see what this says, and we'll bring back authors name and books and the title of the book. We also want the info to tell us if we create any nodes. Okay, let's purify this and look at this. So what are we doing so we're saying create authors. So we're going to create an author, set the name to Marty Kagan, then for the books relationship field. We're going to connect to existing books, where the title of the book is inspired. So let's run that. And we say we've created Marty Kagan and came to inspired we created one node, you can verify this, if we go to Neo4j, let's look for author. Here's Marty Kagan and double click to traverse out here at this book inspired, which is all about product management. Okay, great. So that was updating our schema, adding authors and subjects. I'm going to skip going through the rest of the authors and the subjects so we can move on and have time to cover the remaining section. But you can imagine how we use those nested mutation operations to create authors and subjects. You can find the solutions linked in the sandbox to in the, in the readme. If you want to check that out. Cool, so let's move on. So we've talked about writing our type definitions that then drive the database drive the graphical schema that's generated that gives us the crud operations for creating data. How about custom logic, how does that fit in. That's what we're going to talk about in this section. So, I'm going to do some setup here. You can follow along. If you like, but you don't need to. Let's clear out our database. So because we were going through that exercise and we have somewhat different schemas I'm going to do another match a detach. Delete a let's delete everything in the database. And then we have a new code sandbox. Let's copy the link. Drop the link in the chat. So this is a new code sandbox which means we'll need to update our dot EMV file again. So I'm just going to copy that from this code sandbox. I'm going to click that link that I pasted in the chat. And then we want to fork this code sandbox. So we get one that is private to us. And I'm going to paste my credentials into that dot EMV file and save that so our api application will restart. So you'll notice that this schema now includes a bunch of other things. It includes The author. Yeah, so author subject. We also have these cipher directive fields now where we've added some custom logic we even have a new weather type and if we look in index JS we see we've actually like implemented a resolver here. So this this code sandbox we're going to talk about in the context of adding custom logic. So you want to follow along with that code sandbox, you can see some updated code, but to finish the setup we need to run this mutation to create some data based on the correctful schema so that included authors and subjects. So that is running to save some data in our aura instance. So you can see we're creating the authors for each book handful of books and then creating customers and some orders I guess is a couple of patient operations here. So, let's talk about custom logic so we get a lot that is sort of generated for us for free essentially by the Neo4j graphql library, based on our type definitions. There are two ways though to add custom logic. So if we have business logic beyond just our basic create read update delete functionality. So the two approaches to that one is the cipher graphql schema directive. So, this is where we're adding custom logic by adding cipher statements to the graphql schema or implementing custom resolvers. So we can implement custom resolver functions with any logic. We can call out to Neo4j and call it to another database we can basically do whatever we want in a custom resolver. So there's there's some trade offs here so if we use the cipher schema directive. The benefit we have here is that even though we're adding a custom cipher statement. That statement is still included as a sub query in our single generated cipher statement. So we still are only sending one query to the database. So just one round trip to the database. Our custom statements are integrated into that one generated query. So we get to take advantage of the performance benefits, even though we're adding custom logic we're not sort of going down that path of multiple round trips to the data layer with this going down the path of the N plus one query problem. If we implement custom resolvers well those custom resolvers those are still called in a nested fashion so we may end up making multiple round trips to our data layer. In that case, so something to be aware of there, and of course, including a customer solver. It's a bit more work than just attaching a cipher statement in our type definitions. So we saw an example earlier of the cipher schema directive I think it was looking at a business recommendation with like a collaborative filtering for users who reviewed this business what are other businesses those users are reviewing. If you like this business that might be a good way to find recommendations. So let's look at the different ways that we can use this cipher statement so and these snippets all come from the code that's in the code sandbox so that code sandbox thing for this module includes all these examples so you can, you can run these and see how this works. So here we are adding a field to order reason this extends syntax in graphql SDL, which we can add these as fields on the type definition I like to use this extend functionality because then I can sort of have my basic type definitions in one place some logic somewhere else. Add them together, but it's just, just a syntactic sugar. So here we're adding a subtotal field to the order type. So, we may, we may calculate this by looking at the order traversing to all the books that are contained in the order, and then summing up the price of each book, of course, this could be a bit more complicated we may have discounts and things like that to take into account but there's no way that we can calculate subtotal. So here we're using the cipher directive to compute a scalar field so we're calculating a float. Note the use of the this is a variable that's injected to refer to the currently resolved order. And so to the clients of our graphql api this just looks like another field. Right, it's not obvious that this is actually being computed. Let's, let's look at this one as an example. This is interesting. So in Oh, we got an error on this. Oh, maybe I didn't paste in the right credentials for my .env file, perhaps. Is that what happened? Copy, paste. Clearance again. Work that time. Yeah, so I think, I think I didn't have the right the right credentials for my database but okay so what do we want to do we want to look at orders. And for orders, orders have what an order ID and a subtotal. So let's run this. So here's all of our order IDs and the subtotal. Now if we look at the generated cipher query so I'm going to scroll way down in the terminal output. So if I look at the generated cipher query for that I can see, okay we're matching an order or returning order ID. And then for the subtotal field. Here's our cipher query that we attached to the schema, where we're traversing to all the books in the order and summing up the price to get some total. So that's what we mean when we say that the cipher statement that we're attaching with the cipher directive that that is sort of injected as a sub query into the one generated cipher statement so we're still making just one request to the database. Okay, so that's using cipher schema directive on a scalar field we can also do this to node and object fields and object array fields. So here, we're adding a recommended field to the customer type. So this is an array of books. So we're looking at the orders that this customer is placed so starting from this is the currently resolved customer traversing to orders this customer has placed to books that are in the order. And then looking at other orders that contain these books that other customers have placed. So this is saying, for all the books that I've ordered what customers are also ordering the same books that I like. So basically finding similar customers right by the same books, similar preferences. Then in the next statement, the next match line here, we're going from those other customers. What are orders they're placing with other books that I have not ordered. So, what customers are ordering books that I'm also ordering what other books they ordering those might be good recommendations for me again this is another way to express this idea of collaborative filtering where I'm using the preferences of other users in the network to personalize recommendations for a specific user, which we can express in cipher so now we have this recommended field on customers. And again we can we can query this I'll probably skip querying this one in the interest of time here but now we have this recommended field that gives us books we can select into books and continue on like a nested structure as well. Any field arguments that we define in our type definitions for the cipher directed fields are passed to the cipher statement as cipher parameters, which is quite nice. So for example, on our recommended field, we want to control the number of recommended books, we could add a limit field argument here. And then that's available to us in the cipher statements as a cipher parameter we use the, the dollar sign to indicate that we want to use a cipher parameter. And that name matches. Whatever is included in our cipher and our graphical argument sorry there's some loud noises going on. Okay, so in this this is quite nice. It is a good idea, I think, typically to use default values here so here we're setting a default value for three. That means will this limit cipher parameter will always have a value so we don't need to think about how to handle that in the case if we don't have a value that the user specifies so I guess we could also you'd also make it a required inputs but I like to set a default value that makes it easy for the clients, if they don't care, they can just use the default value. But then we don't have to. In our cipher statement handle the case where no value is provided. Anyway, So here, we're overriding that default value by saying no we only want one recommended book. Okay, so we've used the cipher directive for scalar fields. We've used it for returning nodes. We can also use cipher directive fields to return an object. In cipher, I think we call these maps but an object a map dictionary. Everyone think about it. Key value pairs. So we can write a cipher statement that returns an object, and then map that to a type that we find in our schema. So, for example, in this case, we have address nodes. It may be helpful for our delivery drivers for them to be able to see the current weather at these addresses where they're going to deliver things. So here we're using cipher to call out to a weather api. We're using APOC. APOC is the standard library for cipher that adds some additional functionality. So one thing we can do with APOC is call out to other APIs. So here it's APOC load JSON, call it to a JSON api to then work with the JSON data that comes back from this weather api. But note that we have the location latitude and longitude available to us because this, which is the currently resolved address, has location property. Then we return this object. So temperature, wind speed, wind direction, precipitation, and summary that matches this weather type that we've added to our schema. So this is good for this case where we may be calling out to another api to fetch some data, and we just define some types that don't necessarily need to exist in the database. So we don't have weather nodes in the database, but that's okay. We're projecting those from a cipher statement. And again, to the client of the api, of our graphql api, there's no indication that this is calling out to another system, that it doesn't actually exist in the database, whatever, like they don't even know that's coming from a database. It's just another object field, current weather. So, so far, we have been using these cipher directive fields on types. So we've been adding fields to types, we can also use them for top level queries and mutation. So for fields on the query and mutation types. So why would we do this? Well, one case might be to expose fuzzy matching through like a full text index, or some custom query logic, so we can create a full text index in the database with this statement, which I'm going to skip running in the interest of time Then we can query the full text index and specifically indicate we want to use fuzzy matching. That's the tilde at the end here. So we're searching for books that have either a title or description that contains graph, but always misspelled graph. But we still want to return useful results to the user. So that's what the tilde indicates. This, by the way, this is a query syntax supported by Lucene. Lucene is a full text engine that is the backing for full text indexes in Neo4j. So that's the Lucene syntax. So any sort of Lucene syntax we could use here. Okay, so that's how we queried in the database though. How would we expose this through our graphql api? Well, here we're adding a book search field to the query type. This book search field takes a search string field argument. We're passing that search string into our cypher statement to search the book index. We're adding the tilde to indicate we want fuzzy matching. So we've now added a full text backed fuzzy matching field for searching for books. So now if we search for books and we even misspell graph, we'll still get some useful results. So that's one case where we may want to use cypher directive for custom query fields. We can do something similar for mutations. Maybe we have some custom mutation logic that we have. We have some very specific structures we want to create, or we have some specific way that we want to load the data. We can create mutation fields where the logic is defined using cypher as well. Okay, so those are the different ways we can use the cypher directive. So custom mutation, custom query fields, scalar fields, returning nodes, and projecting objects from cypher statements that don't actually or may not actually exist in the database. Another way that we can add custom logic is by implementing resolvers. So resolvers are generated for us by the Neo4j graphql library. We haven't had to write any resolvers yet. We have all of this, including custom functionality in our graphql api, which is pretty neat and super powerful. Usually we're writing resolvers that specify the data fetching logic. We can override the resolvers that are generated, or if we have data that is not actually in the database, we want to call it some other system, whatever the case may be, we can implement resolver functions. So here we're adding a field to the order type called estimated delivery. It's a date time. So maybe we have like a logistic system or something like that that we're calling out to that's going to tell us if we have this in stock and when we think we're actually going to be able to ship it. In our case, we're just calculating a date at random in this function, but imagine we're doing something more complicated. Here we're just calculating date kind of at random. When we do this, though, we need to add the ignore directive to our schema to indicate that we should not just try to fetch this field from the database that do not include the estimated delivery field in our generated Cypher statements, because that is not data in the database. Instead, actually call this estimated delivery resolver to get the data for this field. And so this becomes then another field available on our order type. Okay, so we have another exercise. I'm going to skip this one will leave this for you do on your own time since we have only about 10 minutes left. But if you look at the code in the code sandbox that is linked, you'll see a similar field on the book type in the exercise here is think of other ways in Cypher that we could calculate similar books, try to modify that see see what the results look like based on that. But we'll leave that for a home exercise, I guess. Okay, so we have 10 minutes left for the final module which is on authorization. There is a code sandbox specific for this one, let me, let me go ahead and bring that one up I think will probably mostly just talk through the examples here. Let me copy this copy link. So here's the link to this one, which I will drop in the chat. This shared this link earlier. You also find all of the queries that we're running here as well in this gist. Okay, so let's talk through authorization. There is a another schema directive available in the neutral graphical library that is super powerful, called the off graphical schema directive off allows us to define authorization rules to protect types and fields in our schema. We use JSON web tokens to verify claims and match those two rules that we've defined in our schema. If you're not familiar with JSON web tokens it's basically a way to cryptographically sign a JSON payload, and then it's encoded and into the string. So you can encode these so you can take any JSON web token. There's this online tool JW TIO paste in there, you can see the payload. And then there's a separate key that either a secret string or like a public private key pair that's used to verify that the token was signed. In that case like signed by an application that is verifying those claims either by using a shared secret, or by signing it with the private key and we're going to use the public key to verify that it is a valid token and that the claims or the payload So in this case, we have a sub claim that's the subscriber, which is like the user user ID is typically what's used there. So, this token is verifying that the user is Bob Loblaw 7687, and this user has an admin role for this application. So we, we can add basically any sort of JSON key value pairs to the payload of a JWT there are some conventions, such as sub is commonly used for subscriber roles scope things like this. Okay, so let's look at some of the rules that we can define using this off schema directive so the simplest one is is authenticated. And this simply means that in order to access the field or type the graphql request must have a valid JWT in the request header. So here's an example where we've added is authenticated to subject so maybe, I don't know maybe we have like a public search for our books, and then you can view like the title and author but if you want to be the subject you need to sign into our application, something like that. So we get an error if we request subjects, and we are not passing a token, but here we've added an authorization header in graphql playground with a bearer token this is the JWT. And the graphic library will validate that the token is valid by using either a shared secrets or a private key. And then we will get the data that we expect. The next type of rule we can create is based on roles. So we saw that token before that had a roles claim and it was an array and included admins this is an admin user. So we can also specify the operations for each one of these auth rules so we're saying this rule is going to apply to create update and delete operations on the book type. So this, this rule will not be applied to read. So anyone can read but to create update or delete a book, you need to have an admin role. Another rule we can define is allow. Allow will compare values in the database to values in the payload of the JSON web token so here we're saying that to have access to an order. We're not applying any operations here so this applies to all operations for the order including reading that the customer username of the order needs to match the sub or the subscriber claim in the JWT so this says only users can only read their own orders or create or update their own orders. So here we are authenticated as this user MLI from 7474, and we're saying, show me all of the orders for this user. But now if I remove that filter, and I say show me all orders well I'm trying to request orders that I don't have access to so I'm going to get an error. So we can combine multiple rules so this is an or operation so we're saying, only customers have access to their own orders, or if you're an admin. If you have an admin role then you also have access to orders. So there's a problem, sort of with this one, where we're authenticated, we're asking for all orders. We still get an error, because we're not filtering for the orders for the currently authenticated user so there may be some cases where we don't really want the client application to be responsible for adding these filters to filter for only the currently authenticated user. So that's where the where rule comes in so where is similar to allow and it allows us to specify rules matching values in the database to values in the Jason web token. So the difference to allow is that where will automatically add those predicates to the generated cipher query, so that the client can see to request data, but that predicate is always going to be added so they're only going to get back data that they have access to. So now we're adding a where rule to the customer type that says, only the currently authenticated customer can access the customer type so you can only access your own customer data. We're authenticated as a specific user by passing this authorization header and passing this bearer token, we're asking for all customers their username and their orders, but if you look in the results were only getting back customer information for the currently authenticated user, because we've applied that where rule is another rule that we can create this will also match data in the database to data in the JWT. And in this case, we are saying that to create or update a review, the author username of the review to know we are like traversing the graph a little bit in this role we're going from the review to the traversing the roads relationship to the customer, and the username of that customer who wrote the review in the database that username needs to match the subscriber claim of the JWT so only users can create or update their own reviews, which makes sense. And bind means that this will be applied during mutation so we try to author a review, and we try to create connect that review node to a user other than the user or the customer that we're currently authenticated as the beginner. We can use authorization functionality with cipher directive field so we can define is authenticated rules for cipher directive fields and then also in any cipher directive field. When we've configured authorization, we have access to all of the claims in the decoded token, which is really neat under the auth cipher parameters there's an auth cipher parameter so say auth.JWT.sub in this case, right here on the first line of our cipher statements to refer to the currently authenticated user so we can grab their username from the token, look that customer up in the database, and then here we're generating book recommendations for the current user so again, here we're looking at what books is this user purchased, what are the subjects of those books, what are other books that have those similar subjects so recommending books based on similar subjects. So this is nice because we can just have a books for current user query field, we don't have to like filter in that graphql query to specify the currently authenticated user that's grabbed from the claims in the token from this authorization header, and we get back recommended books. Cool, so we're over time, there's one more exercise, which is to create a new user using the admin bearer token, then create a JWT for the new user signed with a secret, and then to add some data from the perspective of that user. So, I will leave that for a home exercise since we are out of time the solution, I think, is included in this gist that I linked. Yeah, down at the end here. Cool so thanks everyone for joining and and for sticking through to the end here. Thanks a lot. We have a lot of other interesting things that we can do with near to graphql library that we didn't have time to cover today like working with unions and interfaces. We have an OGM package as well, that allows us to have a programmatic api that uses graphql type definitions, which can be kind of interesting. We talked a bit about relationship properties but we didn't actually do anything with them. And so and so, lots of other interesting things the. These are links to the documentation for each one of these topics if you want to dive in more. Here's this list of resources again the slides. Everyone should have the code is online so you can find that. If you want to go in more depth the Graph Academy training is a like self paced training you can do on your own you get a certificate. When you're done. It's similar content to what we covered today, but in a bit more depth uses the same bookstore api application. Well, again we're over time so I think we will stop here. The I'm doing another workshop through galaxy on Tuesday. If you're interested in that let's take a look. Graphical galaxy. Where are the workshops. So, this is this one building graph guys with neutral graphql library we've done that one on I think it's Tuesday, Tuesday, December 7 at 1800 CET. We're going to do a workshop with looking at full stack graphql applications using Neo4j Aura next.js and Bercel so today we focus just on kind of building the back end piece, and the database and the api layer on Tuesday we're going to see how to use Neo4j next.js to build really a full stack application that includes the back end piece but also the front end and then how do we use Bercel and Neo4j Aura to deploy our application so we'll do some things and react. So if you're interested. Feel free to join on Tuesday. Otherwise, that is all I have today so thanks so much everyone for joining.
175 min
03 Dec, 2021

Watch more workshops on topic

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career