Fullstack GraphQL Book Club - Chapter 4
Using The Neo4j GraphQL Integrations
Each week we'll review a chapter from the Fullstack GraphQL Applications With GRANDstack Essential Excerpts ebook and work through the exercises. Download the free ebook to follow along: bit.ly/fullstack-graphql-ebook. This week we jump into Chapter 4, showing how to use the Neo4j GraphQL integrations for building and querying a GraphQL API backed by Neo4j.
Links And Resources#
hi everybody welcome to the neo4j stream my name is will i see uday in the chat from australia great thanks for joining cool so today we are picking up from where we left off last time in our book club series so we've been working through the chapters of the full stack graphql applications with grand stack book specifically we've been working with this ebook version which is an excerpt of three chapters we're using this one because it is free for anyone to download uh i'll drop the link in the chat here so anyone can download this you get a pdf version not sure if there's an epub version at least a pdf version of three chapters of the manning book full stack graphql and so that's what we've been working through so chapter one was looking at what is the grand stack what are the components how do they fit together uh chapter two we skip in this excerpt but that's okay because uh in the stream last time we combined it with chapter three so chapter two looks at how do we use apollo server to build graphql apis chapter 2 uses just kind of like fake hard-coded data but in the stream last time we combined that concept of building graphical apis with apollo server we combine that with the idea of how we use the javascript client drivers and cipher in neo4j to build i guess we can call it a naive implementation of a graphql api and we'll talk a bit about why it's naive and how we can improve that today but basically that's what we did last time showing how we can use neo4j cipher the javascript driver for neo4j and apollo server to build a graphql api so that's what we did last time today we are going to jump into chapter four chapter four this is i think my favorite chapter so far in the book because in this one we're jumping into the neo4j graphql integrations that are really powerful that show us how we can build graphql apis backed by neo4j writing as little boilerplate code as is necessary so we'll talk about what features the new graphql integrations have um why we can use them and then we'll take a look at how we can use them to build graphql apis including adding custom logic which is really important okay so thiago says hi from canada great thanks for joining cool okay so let's just go ahead and jump in let's switch to vs code so here's where we left off last time so we have this database in neo4j here it is let's fire this up in browser so we can just take a look at the data model so we'll say call db.schema.visualization this will show us theta model so we've got users that have written reviews reviews are connected to businesses and businesses belong in a category so i can say for example show me the coffee category okay i've got two coffee businesses what other categories are they in are there overlapping categories what reviews have been written and what users have written those reviews and what other businesses have those users reviewed and so on so that's the data set we have that we're working with same one we were working with last time and in the book this is the data set we use throughout the book you can access it by running colon play grand stack in the in the fj browser and it gives you this interactive guide so this query you can click on to run this query which will load some data from this csv file so that's the data set we're working with and if you want to run that and load it just run colon play grand stack okay so back to our code here so we were using let's make this a bit bigger there we go so we're using uh the neo4j javascript driver so we pulled that in we're also using apollo server and this gql template tag so the first thing we we did was create a driver instance to connect to neo4j in this case this is my local database so this is the default connection string when i'm running my database locally and then username and password that i set when i created the database then we define our graphql type definitions and we wrap these in the gql template tag the reason we do that is the gql tag can basically parse this into an ast but also gives us the benefit of syntax highlighting in nvs code which is nice so we created these type definitions did we do this in the first chapter i think maybe or maybe we did it last time but basically we started with a graph model and let's see if we do we still have that graph model loaded let's see if we can find that think so yes this is using the arrows tool here it is so this is a diagramming tool called arrows which i like to use for starting off any graph data modeling project so we went from our business our sort of written business requirements which are we're going to build this business reviews web application we want to be able to search for businesses by category we want to write a review we want to see personalized recommendations those sorts of things so we we created the graph data model based on those business requirements and then we converted those or converted that graph data model to graphql type definitions that shows what entities are we interested in what fields do they have and then importantly how are those entities connected so here a business is connected to category nodes that's where the graph part of graphql comes in we've described a data graph here we can traverse from businesses to categories to other businesses to reviews to users and so on now we have a special type in graphql called the query type this is the one of the entry points for the api we also have mutations and subscriptions which which we didn't get into last time but the query type in this case is the entry point so we start at business search or current user uh we implemented then the resolvers so this is where the logic lives for actually fetching this data so the resolvers are responsible for resolving this data from uh from the data layer in this case that means querying our neo4j database so we wrote a cipher query to match businesses that contain some search string and then there's a bit of boilerplate here to create a session for the driver to to make the query we have to pull the result out that we're interested in massage that a little bit to the format that's expected for apollo server and then we had then had to do the same now basically for every sort of nested resolver so in this case business search gives us a list of businesses but then if we want to say grab the categories for a business so if we look at the business type here so the business type um well it has it has these uh scalar field resolvers so like business id name address okay so those those will come back in our first database query we can just use the default resolvers so in graphql the default resolvers oops ctrl z that the default resolvers are basically just looking up uh by key in the object that comes back from the previous resolver so with business search we return this javascript object from the database here which is the business node so it's going to have the name address location business id that sort of thing so that's fine we don't have to implement those that's just like fetching fetching an entry in the business object that comes back from neo4j but for these re these reviews and categories fields so the object fields these are object list fields that we need to make then a separate request to the database to find the categories so we had to start implementing the categories resolver so here we say okay we know what business we've resolved so far let's traverse out to the category and return those categories again sort of massaging that that result data that comes back from neo4j into the format that apollo server expects so let's let's run this oh right and then so that's our resolvers and then we create an apollo server instance by passing in the type definitions and resolvers so apollo server basically can mash together type definitions and resolvers and create what's called a graphql executable schema and then apollo server does two things one it handles the networking requests so it handles incoming graphql requests and then it can execute those against our executable schema the other thing we need to pass to the constructor here where we're instantiating an apollo server instance is uh we need to inject this neo4j driver instance into the context object so we said the context object is where we put things like database connections if we have like a data abstraction layer or some other some api we can put those in the context object and then that context object here is available inside each resolver so when we're implementing resolver functions inside the context object we can find the connection to our database and then we start up apollo server and started listening for incoming requests so let's see if this works it should this is where we left off last time so localhost 4000 if we go there we'll host 4 thousand we see graphql playground zoom in a bit we can look at the docs so business search we get back business okay this tells us how we can traverse through our data graph using graphql queries great so let's do a business search uh and the search string we'll just leave blank which should give us all businesses that should return us yep here's all the businesses we have and we can traverse to the categories for each business yep cool so here's all the business we have in the data set and all of the categories mccool says hi guys hi thanks for joining where are you dialing in from today okay so what we want to do now is well let's look at some of the some of the problems here some issues that we saw well one is this idea of making multiple requests to the database for each graphql request so here we're searching for all the businesses give me the name of the business but then also give me the categories of each business so in this case we get back the public library but then we want to know okay what are the categories for the library well the way we've implemented our resolvers here that means we have to call the categories resolver on the business type so this is going to be another request to the database we've got another business ninja mics okay what categories are connected to ninja mics that's another request of the database so we're making an additional request to the database for each one of these businesses we have what i don't know like 12 here so that means we're making 13 round-trip requests to the database just to render this pretty simple graphql request you can imagine as these get more complicated and as we're returning more results we can start to really have performance problems i mean we're not seeing it here because this is just connecting to a database locally and these queries are pretty simple but imagine if we had to make a network request from our api server to our database for each one of these queries that that time is going to start to add up so this is known as the n plus one query problem where we want to avoid making multiple round trips to the database with each request it would be much nicer if instead if instead that we could generate a single database query for each of our graphql requests so that's one problem here with this sort of naive approach the other problem or i should say another problem that we notice is there's a lot of kind of boring boilerplate code that i have to write here so i have to write the cipher query that's you know just sort of a fairly straightforward cipher query traversal once we figure out cipher there's um you know just sort of a standard here of hey here's a search for a business find the categories and so on and then we kind of have to massage the results here um you know a lot of boilerplate so that's not really fun to write it would be nice if we could just abstract away that boilerplate code and instead just worry about you know the interesting things the custom logic that we want to add to our application basically the the place that we have a specific value that we're interested in in our application things that we want to add rather than just a lot of this standard boilerplate stuff so those are two big problems that i see with our um with our sort of naive implementation that we've done so far so in the book we're going to learn how to address some of these problems how to address that in plus one problem how to avoid writing all of this boilerplate data fetching logic in our graphql api and we're going to do that by using the neo4j graphql integration so let's jump to chapter 4 in the book and again if you if you haven't downloaded it i pasted a link in the chat i'll do it again for anyone who's joined just now and again you can download this version of the ebook for free just go to that url and you can download the pdf that we are working through cool so in chapter four we introduced this concept of the neo4j graphql integrations maybe i can zoom in a little bit so that's easier to read and there are a few implementations of the nifty graphical integrations there's your graphql js which is what we're going to use today and then there's a java version for the jvm and also database plugin but we're interested in working with apollo server and the node.js ecosystem today so we're looking at neo4j graphql js so what is nifty graphql js well it's this library that we use alongside any node.js graphql implementation so like apollo server or yoga or we can use it just with the bare graphql js reference implementation so basically any any of the graphql implementations for javascript for the node ecosystem where we're building a client that sits between or we're building an api rather that sits between the clients and the database right so that's that's a fundamentally important part of the neo4j graphql integrations is we're not really talking about making the database speak graphql and and sending queries to the database we still want to be able to build this api layer that sits between the client and the database where we can implement things like authorization at the application level caching these sorts of things we don't necessarily want our client to be querying the database directly in most cases okay and so we talked about uh some of the the things we want to overcome uh this idea of the n plus one problem and uh poor performance both in making multiple round trips to the database that introduce a lot of latency and also poorly performing queries so we want to get past that and then also this idea of boilerplate so having to write a lot of boilerplate just to fetch some data we haven't even talked about creating data or doing the more complex things like filtering that sort of thing so that's where the concept of database integrations in general for graphql comes in so there are a few database integrations for graphql out there that take care of things like generating database queries from arbitrary graphql requests or helping with data modeling given a database can we generate a graphql schema for it or can we use graphql type definitions to drive what the database data model should be that sort of thing and then we look at uft graphql js specifically so there's sort of two things uh two two specific areas i guess we can say that you create graphical js and and this is true of all of the the neutrographic integrations but there's two like main areas that we're focusing on here in this library one is the schema augmentation aspect so as the developer we write our type definitions so something something like this and these can either be generated from an existing neo4j database or if we're starting a new project we can write these in graphql sdl and then use that to then drive the data model in the database but however we define those type definitions then the schema augmentation process adds query mutation types so adds entry points to create read update and delete data based on the types that we've defined but also adds arguments for things like filtering ordering pagination all of the relevant input types to our schema and then also allows us to use any of the native database types so things like the temporal date date time types and also the spatial types that are available in neo4j so the point latitude longitude xyz the 3d point type and then functions associated with that like the distance function then the other area of features in this library are what we'll call graphql transpilation where at query time we take an arbitrary graphql request and transpile that to cipher so basically we generate a single database query for any arbitrary graphql query graphical operation really either query or mutation and this is really really important because that takes care of the n plus one query problem so instead of making multiple round trips to the database we generate a single database query so we have a big performance advantage there also we can then let the cipher execution engine generate the query plan so because cipher is a declarative query language the execution engine the database is going to figure out what is the optimal series of database operations to execute that query so that gives us a performance improvement as well and then that means we don't have to implement these resolver functions so we don't have to write a lot of these sort of boring boilerplate data fetching queries uh instead all of that is generated for us so we get a huge developer productivity boost here when we use libraries like this uh okay so i think next we take a look at how to use this library i'll skip through this because this is loading the the data set that we've already loaded we look at how to install this so we use npm to install this library and then taking a look at some code here so this make augmented schema this is an important function because this allows us to pass our graphql type definitions so like these type definitions we can pass those to make augmented schema and we get back this executable schema object so without writing any resolvers those are generated and attached to this schema object which we can then pass to apollo server so if we look at our example from last time we don't need any of this code we don't need these resolvers um actually let's go ahead and see if we can get this working so i'm going to get rid of our resolvers just delete those and let's see we'll need to do an npm install i know we don't need the dash dash save anymore but uh old habit i guess uh i always have to add that okay so we'll install ufc graphql js so then up here we can say make augmented schema require neofj graphql js and after our type definitions we can say schema equals make augmented schema pass in the type defs and then here in our our call to apollo server well we don't have resolvers anymore so let's delete that and instead of passing our type definitions to apollo server we're going to pass this schema object that we created here so schema this is now a graphql schema object an executable graphql schema that has now had resolvers generated through this make augmented schema process and attached and we can just serve that with apollo server uft graphql gs follows the convention that we will have a driver instance injected into the context so we should be good there okay so let's start node index.js and let's refresh playground to pick up our new type definitions now if we go to docs we can see that we have a lot of things now generated in our api that we didn't before so in addition to our current user in business search actually let's take those out of our type definitions since we actually have that functionality implemented for us and let's restart here we go so i have uh entry points for searching for users reviews businesses category if i look at business i have a business filter so i have some filtering functionality that's generated and then i have connections to reviews and categories and so on i also have mutations generated here for all create update and delete operations so i can do something like this show me all of the businesses and we can filter for name contains so we're seeing some of the generated filtering functionality that's available so let's say or the name contains library it should show me all the libraries oh you have to get an error there something it's not right there it's okay okay let's see i think we have a graphql version issue going on here let's take a look let's try this let's delete our node modules and package lock do another install so the problem here is i think sometimes we end up with multiple versions of graphql installed which we want to avoid so let's specifically install graphql 14 7 0 version we want delete our node modules and package lock try that again and let's do an index js restart let's try this again okay so give me all the business's name and address and what we're doing we're doing a filter where name contains library there we go okay so the problem there was we had multiple versions of graphql installed i think just because of the order in which i was installing things um but here we're we're explicitly saying the version of graphql that we want to include great okay so that's some of the generated filtering functionality um but i guess the the takeaway here the interesting thing here is we didn't have to write any resolvers and now we have data fetching queries generated for us and also the schema augmented that includes this ordering filtering and pagination so we can we can let's bring in all of the businesses again and we can say like order by name descending and we have pagination so we can say give me only the first two offset two give me the next two and so on um okay cool so those are some of the things that are generated for us uh let's skip ahead in the chapter here a little bit uh so we talk about configurations we can exclude certain types and so on we can disable mutations for example if we just want to generate a read-only api we then start taking a look at using some of the features here so basic queries took a look at that i'll skip that section ordering and pagination we saw briefly nested queries cool so previously we were querying for businesses and categories so something like this so now show me categories and the name of the category and if i run that i get an error so what's going on there well the reason we can't immediately go from business to category is that in order to build up the the cipher query to do this traversal we need a little bit more information we need to know the relationship type so in our property graph model that neo4j uses every relationship has a type and direction and that's what we're missing here so we need to include in our type definitions the type of the relationship to follow and the direction and to do that let's see where is this included in the book ah here here we go this section on the at relation directive so schema directives which is what this is here this at relation uh directive these are graphql's built-in extension mechanism so schema directives allow us to say hey there should be some custom logic that happens here in the graphql server and the neo4j graphql.js library includes a few schema directives actually if we take a look at the documentation uh by the way this is the documentation for your graphql integrations which i dropped there but let's take a look specifically i'm interested in all of the schema directives can you go search for that there we go graphical schema directives so there's quite a few schema directives that are available and they allow us to one attach additional information to the schema so in the case of the at relation directive it allows us to include the name and direction of the relationship but then we can also do things like indicate which field should be the id if it's not clear which field should have an index created for it the cipher directive for implementing custom logic which we'll take a look at in a moment and then authorization directives as well for implementing authorization and some other features okay so our type definitions then are missing the at relation schema directive so in our model we have a relationship called in category so if we go back here to arrows i guess we have it we said that a business is connected to a category with this in category relationship so we're going to say the name is in category and the direction is out in this case because it's going out from the business into the category so let's restart and let's run our query again and cool this time we get categories so now we're able to generate that cipher query to fetch the business and the categories and now if i go on to the category type and if i add the at relation directive so name in category direction and direction this time is in and if we restart our server and if i now add so i traverse from the business to the category and then i can traverse out from those categories to see okay what are other businesses in this category now that will be added to the query as well um oh it's kind of boring for these ones because there's only one business in the category graph database and only one in the category library so let's pull those out since that's kind of boring and look at it for all of them so here's breakfast we have a couple of breakfast restaurants and so on and now i can add up reviews and for the review i can get the text and the rating so building up more complex traversals oh right so reviews we need to do the same thing for reviews so business to reviews at relation the name is i think we said review so review reviews a business the direction in this case is in because it's going from the oh no uh yeah in because it's going from the review to the business and oh and when you do the same rating what happened there so doesn't like that we don't have a value for rating because every every rating should have a value let's look in the database and see what's going on there so if we look at reviews oh it's called stars so stars and it's a float so instead of rating this should be stars and this should be a float and then if we restart now and run this again in playgrounds our query field rating on type review right because we changed it to stars cool now we get all of the categories for businesses all the other businesses in that category and all of the reviews uh cool question from the chat sorry i just joined thanks for joining what library do you use for connecting apollo and neo4j we are using the unifi graphql js library which i will drop a link to in the chat right here um okay oh let's talk about debug mode so we said we're generating these queries um you said we're generating these queries but what queries are we generating so let's run let's let's switch to a more simple query so just businesses and names how do we enable is that how we enable debug mode what i want to do is enable debug mode debug config oops what did we do debug logit um oh capital uh we set a environment variable debug we set that environment variable and then start our library execute a query there we go then we have now enabled debug mode so set the environment variable debug and now when we start our graphql server debug mode will log the generated cipher query as well as the any parameters that are passed along with this query to the database as well so in this case our graphql query is saying just find all businesses and give me the name of the business and so you can see that's the database query that we send here but we can do things like say okay only show me the first business and now we can see okay well now we've added a limit and we've passed first so limit first one and as we start to add up more complex traversal so now find the categories now we can see the generated queries get a bit more complicated so now we're saying traverse from the business to the category and then because with graphql we only want to return the fields that are requested in the selection set so our cipher query projects those out and says only return in this case the name field on categories and this is cool because as we build up our more complex selection sets we can see our queries getting more complex to handle that information as well so we don't have to write those by hand instead this library takes care generating those for us which is really nice um okay cool so back to the book so we talked about basic queries ordering uh we looked at nested queries uh filtering we saw a little bit earlier how to do filtering so we have this generated filter argument we can even do nested filters which is pretty neat so let's copy this query let's bring this down so there we go so we can read that so what this query is saying is find businesses and then filter where the name contains brew so like a brewery or a coffee shop i guess and then filter where it has a review the stars is greater than or equal to 4.75 so show me a business that has a review at least one because it's reviews some that has a rating of 4.75 or greater and it shows me zutan brew and kettle house these filters are really nice we can build up ands and ores so lots of more complex filtering there as well and we can filter at different levels in the selection set so here with this nested filter because we're saying filter on the reviews fields that's then applying that to the business so find where the reviews stars is 4.75 but apply that filter to the business level if we include filters further down in the selection set so here we're filtering at the reviews field so we're still going to get all the businesses copy bad copy paste okay so now we're getting all of the breakfast and coffee businesses but only the reviews that we're going to show are relevant to breakfast sandwich and turns out there's only one so this fill this filter is not applied to the parent selection it's not applied to the business this filter because we specified it nested in the selection set only applies to the reviews that are returned so notice we do return market on front and zutan brew but they don't have any business because they are coffee or breakfast businesses but they don't have any reviews that mention breakfast sandwich so in this in our application this is a good way to know like okay cool if i want a breakfast sandwich i'm gonna go to ninja mike's not to these other team okay so lots of cool things we can do there with filters we can also work with the the temporal fields i'm so working with date time let's skip that section we can also work with spatial data so point types distance filters so find things that are near to me find things within in this example this is what looking for things uh 1.5 kilometers from the neo4j office so i want to find a place to go to lunch i can use this sort of logic okay let's skip to adding custom logic because i think this is a really important feature to talk about so so far what we've seen in our api have been just the generated features so generated filters the the generated uh crud operations for the create read update delete we haven't talked about mutations but we saw that they're there we saw that we can create and update data as well without writing any resolvers but what if we have some custom logic that we want to implement what if we want to recommend businesses based on uh some logic uh something like that uh well there's a few ways that we can implement custom logic using neo4j graphql js and the nutrigraphical integrations the first is with the cipher directive so i mentioned this idea of schema directives before and we had to add some schema directives to our schema to specify the type and direction of our relationships the cipher directive allows us to attach a cipher query to a field so here we're going to add an average stars field this is going to be a float and we're going to add a cipher statement here that says match this so this is a special keyword in these cipher statements that we use in the cipher directive that refers to the currently resolved object so when we're resolving a business this here this refers to the business that we're currently resolving and what i want to do is compute the average stars so for all the reviews connected to this business what is the average of the stars field of those reviews so we'll traverse the review and we'll return average r dot stars so we'll save that and restart our server and now you can say business give me name and average stars and we get an error because it says unknown function epoc cipher run first column so uh one thing this is this is mentioned in the book but we skipped over it but the neofj graphql js library depends on the apoc standard library for neo4j so you can install this in neo4j desktop just by going to the manage and then the plugins section and clicking install if you're using any of the cloud neo4j services like neo4j aura or sandbox then apoc is installed by default because it's it's the standard library and actually in future versions of neo4j apoc is installed by default so if you see that error that just means that you haven't installed the apoc standard library yet let's try that again and now we get back this average stars field along with all of our business names so if we look at the generated query for that one we can see that here here's the query that we added in the schema with that cipher schema directive we can see that's attached as kind of a sub query so even though we're adding custom cipher queries basically we're adding computed fields in our graphql schema even though we're adding those we can still generate a single cipher query to send a single round chip request to the database so that's pretty neat um because it allows us to define custom logic using the power of cipher but still take advantage of the n plus one take advantage of the lack of the n plus one query problem i guess so generate a single database query let the cipher execution engine figure out how to optimize it so that's still going to be really really fast and it gives us the flexibility now of using cipher to add our custom logic so i think this is a really cool feature i think it's a really good balance of having all of this generated functionality for us as part of this schema augmentation process not having to implement any resolvers but then still having the the ability to implement custom logic using the cipher directive when we want to add custom logic so that's really neat we can also use these cipher directive fields for root query and mutation fields so if we have a custom mutation that we want to define in cipher we can do that if we have a custom query type and we want to use a full text index or something like that or some more complicated initial matching for finding the initial set of nodes to traverse from we can do that with cipher as well so really powerful we can also implement resolvers manually so if we can't express in cypher the custom functionality that we want we can just implement a resolver function and attach that to the schema so here we implement a fake wait time function for a business which is just a random number but you can imagine here maybe we call out to some other service that has information about the wait time for the restaurants um something like that so uh in general i think the goal with your graphql has been to implement a balance between generated functionality but also have it be as extensible as possible for adding custom logic so i think in in my mind i think cipher the at cipher directive is really my favorite and most powerful feature okay then we have a section that talks about inferring a graphql schema from an existing database so in this example we wrote these graphql type definitions by hand because we were starting from business requirements for our application we we drew out a data model and then translated that data model diagram to type definitions but what if we have an existing database it would be nice that we can just generate the type definitions from the existing database and it turns out we can do that there is a infer schema export in neo4j graphql.js that we just created driver instance and point infer schema at that database and it will generate graphql type definitions for us so for example we can save those to a file and then pass them to the make augmented schema process to create a new graphql api for us so we certainly can basically start from an existing neofj database and get a graphql api with full crud operations on top of that database without really writing much code other than uh generating the schema using this in first schema function so that is really powerful as well okay cool let's um let's take a look at the exercises and see if we can complete these based on a skimming of the book so far i think we'll be able to get through it okay so number one query the graphql api we created in this chapter using graphql playgrounds okay to find first off which users have reviewed the business named hanabi okay so we want to do a filter where the business name oops where the business name contains let's let's make this a little bigger give a little give ourselves a little room here where the name contains hanabi okay cool so there's a business named hinabi it has average stars of five because it is the best ramen restaurants uh in burlingame if you're familiar with that area the hanabi is just a little walk down from the near fj office in the bay area anyway we want to know what users have reviewed this business okay cool so we can say reviews because a business is connected to a review so we want to go from the business to the review and then from the review we want to go to the user and get the name and i think we need to add another relation directive from the review to the user yes we're missing that so relation name what did we say rights a user writes a review is that what we said yeah user writes review so the direction in this case is going to be coming in because this is a field on the review type so it's coming in from the user to the review so the direction is in is that the only one that we need business to review review the user let's restart that try again uh review dot user non-nullable fields i didn't like that um why not ah so we didn't we didn't actually use we didn't use this model exactly in the database a slightly different model it says user wrote the review that writes wrote okay restart that there we go okay so which users have reviewed the business named hanabi uh just our user jenny and she must have given it five stars because it has a five star yep let's give it five stars didn't write a text description for the review okay cool find any reviews that contain the word comfortable what businesses are they reviewing well we can start here at the review do a filter now where the text of the review contains the word comfortable and we can return the stars and text of the review and then i want to know what uh stars text so i want to go from the review to the business i want to see the name of the business but i don't have that in the schema but we can add that so go from a review to a business every review should be connected to a business so we can make that a non-nullable business object and we need to add our relation directive so the name of the relationship is reviews so review reviews of business so the direction is going to be out because it's going from the review out to the business and let's restart our server and if i now i have the business field so i can say business name and i can see that this review is about the public library so not many comfortable places to sit and read at the library okay cool next one which users have given no five star reviews this will be a good one so user let's say bring back the name and let's bring back the reviews and the stars so here's all of the users and all of their reviews are we missing a relation directive yes we're missing one here uh so user or we say wrote a review so the direction user wrote review direction is going to be out restart the server okay cool so here's all the users and the reviews that they've written because this is such a small data set we can we can look at this and we can see okay only bob has not written a so five that's the answer we want to get but how do we write a graphql query to find that well we saw this nested filtering that we could do here so for example here we're searching for businesses that had her at least one review of 4.75 so i think i think this sort of syntax is what we want to use if we go digging in the documentation filtering with graphql and the nested filter i think we can also look at all of the filtering that's generated in this table that will tell us as well we also have you can also try this in the documentation we have these graphical components embedded to experiment with that which is quite nice but let's go back to playground so user we want to filter here so i mean i can i can apply a filter here that says only filter where stars um let's say greater than right so only show me the five star reviews is kind of kind of what we're getting at but that's that's not quite what i want because i don't want to apply the filter at the reviews level i want to plot apply it at the root at the user level so i know i want the filter up here and i know i want it to apply to reviews and i want to filter where there are there are no so i think i want reviews none where stars is greater than or equal to 5.0 let's try that and it gives me bob yeah cool so this is a good one this this i think shows the power of these nested filters um because i i could apply this deeper down in the selection set as well right i could say even show me reviews where maybe like my friends or co-occurrences of reviews uh that sort of thing question from the chat do we do these streams often yeah so um i do the stream every thursday at 3 p.m pacific um if you look in the twitch calendar i won't i won't open up twitch now or i'll i'll see like a a mirror of myself but if you look at the twitch calendar you can see some of the other folks at neo4j that do streams i think lou does one on mondays and adam does one uh as well on different topics minor minor usually focused around using graphql and neo4j but lou and adam switch around on different things as well i think adam's been working on building react applications with typescript and neo4j uh and lou's been digging deep into a wine dataset showing how we can do some analytics there uh oh yeah and uh floran i think has recently started streaming as well he's been doing a series on go so yeah so anyway um there's quite a few streams i think on the the neo4j channel you can also watch on youtube on the ufc youtube channel and all the recordings end up on youtube as well cool so um yeah so there's a question can we get the latest review and we could if we if we captured that do we have i don't think we actually have the time of the review a review just has stars and text so if we had the timestamp that the review was created we could then say here order by like created descending or something like that so because we don't have that data in the database i mean we can order by like stars descending let me take out the filter so we can verify that's what's going on but yeah the the generated filter functionality uh or i'm sorry the generated ordering functionality basically allows us to order results by any any field new day says can i create a graphql directive to add filters instead of me typing it i'm lazy as it comes a um directive to add filters do you mean a client directive or do you mean like add something in the schema that always gets applied let me know and when i see your answer we'll take a look at that but i'm going to move on to the next question here which is add a cipher directive field to the category type that computes the number of businesses in each category how many businesses are in the coffee category yeah so this is going to be kind of similar to similar to this where'd it go to our average stars right so we're going to add a cipher directive feel let's put it at the end to category um does it say what we're supposed to call it uh it computes the number of businesses now let's just say num business businesses you guys spelled that correctly uh this is gonna be an integer so add cipher takes a statement and we're gonna say match uh this business in category so if you look at our data model businesses are connected to category with this in category relationship so i want to this is the pattern i want to look for be business uh and then i want to return the count of the business and i can alias it to whatever it doesn't really matter and let's restart uh and what was the question how many businesses are in the coffee category so let's category um so i saw we could do the filters and we can also just specify an argument if we want to do an exact match so if we want to just do an exact match we can just use the name argument instead of the filter argument and i want number of businesses it says there's two if we go back to our generated query so here's the query that gets generated and again we can see here's the cipher query that we wrote in the cipher schema directive that gets included as a subquery cool next one oh uday says in the schema so can i create a graphql directive to add filters instead of typing it in the schema so you want to add something in the schema so that on temporal types search by ascending date by default that is a good question in um schema well i guess you could implement you could implement you you could implement your own directive um to do that i i guess that that's kind of similar it reminds me of the approach that uh ian took when he built the graphql auth what if you call that deep off see if we can find that yeah here it is so uh ian cleats wrote a package called neutral graphical deep off i'll drop a link to this in the chat and what this does is it it implements a directive called deep off that takes a path where really what the path is are filter arguments to add to the generated query his use case here for doing this was adding more complex authorization rules so if you want to say like okay a user can only see himself and users that he is friends with you can specify this in the schema as an argument to this deep auth directive and then at query time these filters will be added to the graphql query which will then get added to the generated cipher query so uday for your question about a directive that's adding filters or ordering i think this is kind of how you would do it if you take a look at how ian implemented the not sure where this lives is there a directive visitor here not sure if you take a look at ian's code though we'll give you um give you an idea of how he implemented that directive because the same sort of same sort of idea here where you you just want to specify filters or ordering arguments in any arguments really that you want to just be added to the cipher query and the graphql query um by default cool yeah so i dig into that um but anyway but to answer your question like nothing nothing out of the box uh but you can definitely uh implement something that does that okie doke last question create a neo4j sandbox instance at sandbox.newsday.com let's drop that in the chat great resource that sandbox uh choosing from any of the pre-populated data sets well let's go do that uh ufc.com sandbox so sandbox the neo4j sandbox i should say lets us spin up neo4j instances that are private to us but are hosted in the cloud with existing data loaded in them so you can choose from any of these there's lots of different kinds there's lots of different use cases different data sets here you can even pull in your own twitter data if you connect with your twitter account it will sort of crawl your your twitter follower network and and tweets and stuff uh well let's do legis graph i haven't looked at this one in a long time this this is a bit outdated now um because i don't think we update this regularly but this is a data set of us congress and like the bills and bills that they voted on the committees they serve on and so on so we just had an election in the u.s so sure why not let's let's look at some congressional data okay what was the question uh create a sandbox instance choosing any of the pre-populated data sets using infer schema from neutral graph dljs create a graphql api for this neo4j sandbox instance without manually writing graphql type definitions what data can you query using graphql okay cool this is a good one so so here's the case where we have an existing neo4j database and we want to generate a graphql api for it we want to use the in first schema functionality let's look at that in the documentation there's a guide in for graphql schema that tells us how to do that so we create a driver instance that connects to the database we run this infer schema function and then we get back type definitions and then we pass those to make augmented schema which is then going to generate our graphql api um okay that is how we do it looks like our sandbox instance is still firing up here what's going on there okay it was running so let's open up neo4j browser and um see what we're working with here what's going on something is a little off with our sandbox instance let's try connecting directly to http port so not sure what the problem is there but if we connect directly no it doesn't like that all right let's let's try this again let's terminate this one start a new project let's just graph watch project so the sandbox is uh behind the scenes is really quite neat this is a a project that my team built and maintains and what's going on here there's a uh all these run on aws in a uh elastic container services cluster uh so each time that you spin up one of these it's spinning up a container that is private to you so that's why you have to log in to sandbox is because all of these are private to a single user so if you see the connection details this is private just for your instance okay cool so that worked i don't know what the problem was there but now we've got some data so we do db schema visualization we can see let's make this a little bigger we can see that we have legislators that are elected to a congressional body so this can either be the the senate or the house they represent a state remember a political party uh they serve on a committee and they sponsor and vote on bills bills go to committees bills deal with the subject and are proposed during a congressional session cool so this is legisgraph this is a fun project that um actually i think this was a friend george and i did this several years ago it's just kind of a fun project i think we built it for a meet up demo or something like that this is a fun one so anyway what we want to do is build a graphql api so we can query this and we want to use this in first schema functionality to do that well i'm going to cheat a little bit because i wrote this integration for sandbox a while ago that you may not know is out there but it's pretty neat so if we go to connect via drivers this will show us uh some code snippets for how we can use various drivers for neo4j to connect to the sandbox instance and run a query we also have the graphql tab which shows how to connect to the database infer the schema and start a graphql api using makeup and its schema so we basically just create a driver instance run infer schema and then pass that to make augmented schema and serve our schema through apollo server we can also click this deploy to code sandbox button so code sandbox if you haven't seen this before code sandbox allows us to run javascript code either for the the browser or server side code in case of server-side code it runs in a container somewhere that code sandbox hosts for us and this is kind of neat because what it what it does first is it first runs in first schema so first it runs this infer uh let's make this a bit bigger here we go first it runs infer.js which uh runs in first schema and then it rewrites uh this file the schema.graphql file so now this is this is run so now schema.graphql these are the generated type definitions from that sandbox instance and then it runs index.js which reads those type definitions from the file system and passes those to make augmented schema so this this sandbox button is a really cool way to get a graphql api on top of any of the sandbox instances um sort of for free yap says he needs to go to sleep yeah i hope to see you again yeah well thanks for joining uh we'll catch you next week or you can catch the recordings on the youtube channel so let's copy this url so we don't have the code sandbox chrome around that and see what we can query so we've got some entry points um let's take a look at one specific state so a state has have a name doesn't have a name it has a code i think this is the two digit code let's look at california and all of the legislators for california so we've got kamala harris who that's now vice president-elect but at this time so i think this is pulling in from the um at least five maybe six years ago uh so this is a bit outdated but if we pull in represents member of elected to elected to is connected to bodies so that's what we want and then body has a type so this should show us yep that kamala harris is elected to the senate for california and other members in the house and so on so we can also traverse to see maybe bills that they voted on so let's let's look at just the first three bills for each legislator to see the title and do we see how they voted enacted bill active deals with oh deals with we can look at the categories of each bill so karen bass voted oh actually i think i know what's going on so there's two there's two types of voted here there's voted on rel which has the vote and then the bill and the official title so this is an interesting quirk of the generated schema so if we go back to the code sandbox and look at schema.graphql so in first schema is useful often as maybe like a starting point but it's not always going to be maybe the the ending version of the schema that we want to work with because if we see here we have the type voted on uh and then we also have voted on connecting a legislator to a bill so we have voted on as this relationship field and then voted on rel which gives me a list of voted on types so what's the difference between the type voted on and this relationship field here voted on well in the case where we have relationships that have properties so in the property graph model let's go back here look at reviews this could be maybe a date time on the reviews relationship so here we're storing a property on the relationship it's called create it's a date time stamp but how do we then model that in graphql well what we do is we then promote reviews to its own type that then has a from a to field and the properties of the relationship modeled as a type and that's what we've done here in our inferred schema so i think our inferred schema process says hey well some of these voted on relationships have a vote which is like either yay or nay or whatever but then some of them don't have a vote so in the case where they have a vote property then we promote that to its own type voted on so that you can query the properties of that relationship there's there's a bit more in the documentation about this um in the let's see relationship types relationship with properties so in here and i think also in the graphql schema design guide i think also has some information about that cool so if we go back to our query uh was it here now we can see some additional votes so here karen bass voted no on this bill whatever it is to amend title 28 to improve fairness and class irrigation class action litigation and so on cool so anyway the point of that exercise was just to show us how to use infer schema functionality and that's available that buttons available in all of the sandbox instances and you can you can also edit these code sandbox things so you can you can then fork these and let's say i wanted to add a field that's like i don't know most common topics or something and this was going to be a list of subjects i could then write a cipher directive that then has the logic of whatever it is for matching from the legislator defines what the most common bills that they've sponsored and uh that sort of thing cool so that gets us through the exercises um so that finishes up chapter four uh so we will uh call it good for the day again let me post a link to download the free three chapter excerpt of the book that we've been following along drop that link in the chat here anyone can download that for free if you want to follow along you can find the recordings for the previous streams that we've done on the neofj youtube channel i do this stream every thursday at 3 p.m pacific next week so this is this is i think the last one of the book club session of the free book maybe we'll pick up uh some of the chapters that are in the full version of the book uh since we only covered less than half of what's in the book the full version of the book but uh we won't we'll take a bit of a break from that so i think next week we'll start something new um not quite sure what that'll be have some have some ideas but uh tune in and we'll do something fun at least so thanks for joining today and hope to see you next thursday at 3 p.m pacific for the next stream working with graphql and neo4j so thanks for joining goodbye
Subscribe To Will's Newsletter
Want to know when the next blog post or video is published? Subscribe now!