GraphQL Pagination Support in Spring for GraphQL

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
paging graphql paging graphql what's up everyone Dan Vega here spring developer Advocate at VMware if you haven't guessed we're talking about pagination in spring for graphql today now I've done a video on this in the past I will leave a link to that in the description below but at the time spring for graphql didn't support pagination so we had to kind of roll our own and we did it worked right we had to set up a controller method and take in some arguments like limit an offset and we were able to use spring data's pagination support and kind of wire this together but there's a big problem here when you kind of roll your own that means that you can roll your own in different services so if we have a bunch of services in our organization and we're not all on the same page we may have implemented pagination differently in different services and this is not good right we want a consistent Manner and this is where specifications come in in this case we're talking about the graphql cursor connection specification this is a specification that kind of lays out how you can provide pagination support in your graphql apis now hurry over on graphql.org there are some different approaches to pagination they explain them here so we have plurals pagination and edges and so on so there's end of list counts a whole bunch of things going on um one of the things you'll see here is pagination and edges we could do something like hey give me the first two and here's the offset or give me the first two after whatever the specific ID we want to kind of use as a cursor right and this is a really good explanation here in general we found that cursor base pagination is the most powerful of those designs so you can go through and read this I'm not going to read it all to you but what we end up with is the ability to get back things like this so now we have a specification here we're saying hey when you call pagination here's what you're going to pass here's what you're going to get back things like edges so edges are going to have nodes that node is the actual thing that you want back so in this case a friend whatever fields are on friend those are the things you'll get back but you also get back page info things like hey this is the first page how many counts like Etc the page info right and then you get cursor specific information which cursor are we on so this is the specification that spring for graphql has implemented and I'm really happy to see this because even before I knew this was a specification I was using this on my own website and I was really hoping that spring for graphql would pick this up because I'm used to using something like this so that's another thing familiarity if you move from one graphql implementation to another this should look familiar you've probably used it somewhere else so this is what I'm excited about pagination support in spring for graphql using the cursor connection specification so how do we get started with this well the good news is it's available in spring for a graphql 1.2 so if you're using the latest version of 3.1.1 at the time of this recording pagination support is available now for this demo I'm going to choose 3.2.0 the snapshot again this has nothing to do with the pagination support in spring for graphql and everything to do with something new in Spring data to be able to kind of support this and we'll talk about that later I don't want to get hung up on that now so I'm going to go ahead and choose 3.2 we're going to use Maven we're going to use Java I'm going to say that this is dev.dan Vega we're going to call this sessions again I've kind of used this domain model we're building out this application that manages events conferences sessions speakers that kind of thing we're going to choose Java 20 and then we need to go ahead and pick some dependencies so I'm going to choose web we're going to choose spring data jpa I'm going to need a database so I'm going to pick postgres I'm also going to choose that Docker compose support if you don't want to do this if you don't have Docker don't worry about it get rid of postgres get rid of Docker compose just use H2 it's the same thing that we're going to use today so um okay and finally we'll just need spring for graphql and that is it now there are a few other dependencies that we'll add to the project as we go along to kind of build this out everything that we're going to talk about today is going to be in the repository listed in the description below we are going to have to build out some of this application first so none of it's there for you we're gonna have to build out the graphql schema the Java entities on the spring data GPA side you know get all of that stuff working so if none of that interests you and you're like Dan I already know how to build out jpa stuff I'll leave some links in the description some time stamps and you can basically jump to okay the app's been built let's talk about the graphql pagination stuff but for those interested I thought we'd just go ahead and build this out together so with that let's go ahead and generate this project effect this is going to download a zip you can open it up in whatever ID text editor your most productive in I'm going to open it up in IntelliJ Ultimate Edition and I will see you in there let's go all right quickly before we jump into Intel jail I just want to take a look at the data model it's going to be very simple I've thought about making this a little bit more complex and how I might model this in the real world but I decided to just kind of keep this simple so we have an event has a whole bunch of information about an event which has a collection of sessions a session has ID title descriptions tags so spring framework spring boot Spring Security etc those are stored in a table as well in the tag table we have the level like beginner intermediate Advanced this ties back to the event and then this ties to a speaker where we store things like name Title Company gender country email phone number and Twitter so overall pretty basic this is what we're going to run with so back in the IDE here I'm going to start I'm going to refactor this and name this application I'm going to click OK and once I'm done there I'm going to open up Source main resources graphql and we're going to create a new file in here called schema.graphql graphqls and we're going to start with our schema here so this is basically what our schema is going to look like we're going to paste in some code here and talk about it so I have some queries I want to get all the events I want to get one events I want to get all the speakers I want to get one speaker the event has all the information we just saw in that graphic the session has again those things as well has a tag we have a speaker and then we have an enum for gender male female non-binary so that is our kind of data model the first thing we gotta do is fix this we have a date and a URL and I don't we are representing date here um but we are going to need to fix this it's not going to like that until we kind of correct those custom scholar types so the way that I'm going to do that is I'm going to create a new uh package here we're going to call this config and inside this config I'm going to create a new Java class we'll call this graphql config and this is going to be a configuration class and what we'll need to do here is just go ahead and declare those custom scholar types so how do we do that by going over to our palm.xml and we need to declare that custom dependency so that is um graphql Java extended Scholars and we're going to bring in a specific version so let's go ahead and reload that and with that in place we can go in and write our custom config so again just to save some time going through this first part I'm just going to paste some code in we've talked about this in previous videos I'll leave links to those in the description below if you wonder where these custom scholar types are coming from so we have our custom scholar types we have our schema we have a schema folks we are good to go now we need to build out the Java side of this right we're using spring data jpa we need entities for things like events sessions speakers tags all that fun stuff so let's go ahead and do that I'm going to start with event so I'm going to create a new package in here called event and we can go ahead and create a new Java class we'll call this event this is going to be an entity in the world of spring data jpa let's go ahead and just start with the fields in here so I'm going to start with this I'm not going to do any associations yet so let's go ahead and create our public norg Constructor so event that will satisfy jpa and then we'll need a Constructor for all of our arcs okay and then um uh did we not bring this in all right and then we'll need to go ahead and have some Getters and Setters and let's go ahead and create those and then we'll need a two string so let's go ahead and generate that okay so we have our simple data Carrier class for our events we've set the column definition to text for description in case we want to have um you know some much longer text in there um but overall this looks pretty good so next thing we'll need for an event is an event repository so I'm going to go ahead and create a new class we'll call this event repository this is going to be an interface again we're using spring data so we're going to extend the list credit repository the type is event and the ID type is going to be an integer so that's all we need for that to round this out we are going to create a controller I'm going to call this event controller again this is a controller and the graphql world so we mark it with ADD controller annotation and now what we need to do is we need to get access to that repository so I'm going to say private final event repository event repository let's add that through Constructor injection and we need two queer mappings here right we need one for oops at query mapping we are going to actually we don't even need that so we're going to return a list of events and we're going to call this events because that's what we called it in our schema and all we're going to do here is return the event repositories dot find all method so we'll need one more so query mapping and this is going to return an optional event and we're going to call this event and this is going to take in an argument so argument integer ID and based on that ID we can now use the repository to say find by ID and we can pass in that ID so um what doesn't what don't we like about this uh make event return yeah I thought that's what we did huh all right we must add the wrong import so um those are query mappings that will satisfy these two queries uh these two Fields here and we have our event uh so we're good to go there now we need to set up a session so we have again back in our data model we have uh event and that ties to a collection of sessions so let's start with session and then we'll make that Association work so we're going to go here we're going to create a new package let's call this session and inside of session we are going to create a session class so let's say new Java class session and this is going to be an entity um let's go ahead and copy all this fun stuff uh so let's start here again the associations we're going to leave out we don't have a tag yet so I'm going to wait on that we need a level though a level is really just going to be an enum so let's say um let's go ahead and generate create a class I want to create an enum here and we're going to create that in this package and we're going to call it level and this will be beginner intermediate and advanced right okay so that's good for our level um we need our ID so that looks good so cool so we're also going to have in here um what tags and what is the other one I can't remember uh oh yeah event so we'll come back to this and while I'm here let's do this so we're gonna fix this later but this is going to have sessions um cool so we'll come back to that we're going to create an org Constructor here so public session that is going to satisfy GPA let's say Constructor let's say uh Getters and setters all right yep and then finally a two string great okay so we have session set up again we need a session repository so I'm going to create a new Java class here we're going to call this session repository this is an interface we're going to extend list crud repository for session and integer okay so we have that we need a session controller right did we say oh we did not set that up so we may want let's come back to speakers but I want to say sessions is going to return a collection of session id id will return a session right so those are there we need a session controller so let's say session controller this is a controller we will need that repository so session Repository let's get that through Constructor injection we'll need a couple of query mappings this is going to return a list of sessions we'll call this sessions and in here we will return the sessionrepositories dot find all so similarly to the last one we need a query mapping for an optional of session so we'll call this session and we'll take in an argument of integer ID and we'll return the sessionrepository dot find by ID and we'll pass in the ID so we have our session controller we have our session repository we have a session and we have a level so far so good let's just do one more here and let me open up this schema again so we're gonna need a tag let's go ahead and do that now so this is going to go in session we're going to create a new class this is going to be an entity we're going to call it tag this is going to be an at entity let me just double check that I make entity yes I did make session and entity so we need an at ID this is going to be a private integer ID private string name and that's all we need let's do a noar Constructor for tag and in all args Constructor and then we just need some Getters and setters and our trusty two string okay so that is our tag we'll come back to the associations on that in a second we'll kind of wrap all those up together um so now going back to here we have our tag we just need our speaker and um so gender I was going to make an enum and oh yeah I guess this would be an enumen Java too so let's set up speaker and gender and I think just these things around speaker right speaker repository speaker controller so let's start off by creating a new package here called speaker all right we're going to create a new Java class in here called speaker this is of course going to be an entity as well speaker is going to consist of a bunch more things so I'm going to copy those and let's paste those in so this is an at ID and then we need a gender here so I'm going to create a enum in here and this is going to be male female non-binary right oops okay so there's our gender and then we just need a public no ARG Constructor we need a Constructor yay uh now I know the um I know I'm gonna get some comments on this because I always do why not use lombokden again a lot of these tutorials are geared towards beginners if you've never used long box then you have to like set it up you have to add that dependency you have to make sure that annotation processing is on and your idea and I don't use the same ID as everyone so nothing against lombok I just don't like to bring in the extra dependency and setup if I don't need to in a beginner type tutorial and I like using records I love records can't use them with data jpa so here we are okay so I have my speaker um I think that's good now I'm going to go ahead and create a new repository so speaker repository this is an interface you know the story by now extends list credit repository speaker integer and we are done with that we need to create a controller so I'm going to say speaker controller and this is going to be a class this is a at controller annotation and this should be looking just like the rest of them right so private final speaker right we need speaker repository speaker repository let's get that through Constructor injection and we'll do the same song and dance we've done so we need a list of speakers [Music] all right so we have our speaker controller speaker up pository speaker and gender okay so I think that is a lot of the boilerplate set up I think that is going to give us a lot of what we need um before I go in and configure any properties let's go ahead and try and set these associations up so the first thing I'll start with is an event so I need a list of sessions so I'm going to say that this is a private set of session call the sessions we'll say this is a new hash set right okay so now we need to make this Association we're going to say this is a one-to-many uh we're just gonna go ahead and Cascade uh all and uh this is going to be mapped by event right all right so now on the event side um on the session side I'm going to go to session and I need an event so I'm going to say private event event and this is going to be a many to one so um what do we not see oh this is not getting imported so import yes that's the one I want okay um on this side am I not importing that okay [Music] um semicolon all right so I think that Association looks okay now we need some I think we missed something over here so we need some tags so same thing we're going to say that we need a set of tags because we're not gonna have duplicates here so we'll just say this is a set this is going to be one two one too many and we don't need to Define that so on the tag side yeah I don't want to I don't care about having this bi-directional so I'm just going to set this as a one to many here so that's why we don't need to go into the level of detail that we did over here with event by setting the map by because again we don't want this bi-directional I just want a set of tags I don't I don't need a tag to tell me like every session that has it so at least for now um so that's a good start what did I was I missing here so everything looks good there um I think from a speaker standpoint we would probably have an event tied to a speaker right um but yeah we'll get to that in a little bit uh or maybe not at all actually um we do have the ability to get a list of speakers and we can get a single speaker so that is our updated schema okay so now I'm going to go into application.properties we're going to set a couple things so I want to set some spring data jpa properties generate ddl equals true we're going to create and drop on restart and I just want to show some SQL um and then I think that's it so our database again if we were paying attention closely at the beginning is postgres we also have this Docker compose file so it's going to spin up a postgres instance for us um yeah so I think things are good let's see if we can start the application without problem I don't know let's set a database of uh let's say sessions demo all right let's see if this works yep would help if Docker was running all right so our application started up um no let's see so we have some tables being created event session tags speaker tag that seems like everything we need um and then we have our graphql point we started on 8080 and everything seems to be working cool so that was awesome um we were able to get this up and running now we just need some data in here right I need some data so I'm going to do a little cheating here and I don't want to just hand write all this data in right so I'm going to grab a dependency again I've talked about this on the channel in the past so I'm going to gloss over this a little bit but this is a package called Data Faker it allows me to go ahead and create a bunch of fake data because in this world like I needed an event and a bunch of sessions and a bunch of speakers and I'm not going to go through and hand create these I'll create these by hand Dan so we're gonna generate a bunch of data so I'm going to create a new package called Data I'm going to create a new Java class in here called Data loader and let's go ahead and paste some code in here and see if we can get this to work we need our faker okay so we'll fix some errors as we're going but here's our data loader we're implementing the command line Runner we are bringing in things like the event repository the speaker repository the session repository and here is that Faker class and again this allows us to just basically fake a bunch of data so now we're saying hey we need to create a new event I'm going to go ahead and import that this is a new event spring one at VMware Explorer here's all the data for that event I'm going to go ahead and save that event so we're going to persist that off to the database now I want to create a list of speakers um so I'm basically saying from 1 to 20 go ahead and create a new speaker it's going to have some fake name title company I didn't do a random I probably should do a random here we'll fix that later but we need to enter gender country email address phone number and username once we're done there we're going to save all of our speakers we're going to create a couple tags so spring framework spring boot then I'm going to create a list of sessions here and basically from 1 to 100 we're going to create a list of sessions so this should be importing why are we not importing so level right there and then something's going on here so we'll need to fix this um oh I don't think we added the event did we yeah I thought we did so let's go into session ID title description level ID title description level okay that looks good and then tags which is a one to many and then the event which is many to one okay what is this yeah cannot resolve Constructor ah so we probably generated this before we had that yeah so let's just generate this Constructor again now with everything and that should fix that okay so sessionrepository dot save oh I'm saving all my sessions and then I believe I had this to get an actual random speaker I don't think we need that for now so this class looks like it works we marked it with ack components so it'll get picked up again it implements command line Runner so it's available after the application context is ready so this should go ahead and run and insert a whole bunch of data so let's go ahead and restart this if everything works we'll see a bunch of commands from us logging the spring the jpa stuff so yeah we saw a bunch of inserts but then something happened unable to find dev.npaga sessions tag with ID of one okay so let's go back to our data loader and I'm just guessing but let me just look to be sure um unable to find tag with one so this is saying zero or one uh why are we oh no that's those two set of all right so I'm having an issue with that tag Association I will fix it in the final repository but for now I'm just going to remove it uh doesn't don't really need tags as part of the pagination and sorting anyway so let's go ahead and run this now and see if we can't get this going so we go through and there we go so we insert into session so we have a hundred sessions we have um hopefully some speakers and yes we do and then we have an event so cool so we have all the data that we need if we go back to our application up properties we'll want to enable graphical so let's enable this so we can say true that will allow us to restart this one more time head back over to the browser and now I want to go to localhost 8080 slash graphical okay this was from a previous one let's just go ahead and remove that and let's do that so we can run a query for events we get all the information we need from there there's all of the data now let's go ahead and back to our schema and this is where things are going to get fun so in the past what I've done is okay for an event I just want to get all the sessions right so I would say sessions and this is going to be a collection of session and that's all there is to it right now I can get all 100 sessions for that event and be happy so let's see if that works so now in here I can say sessions I need to refresh this that's okay and now sessions is there and from sessions I want the ID the title and the description so now go ahead and run this and there we go there's all 100 sessions for the event but we know in the real world what if I don't want to get all the sessions what if I want to get five at a time or 10 at a time maybe we're displaying this on a client somewhere and we only want to display so many at a time this is where pagination comes in this is where the cursor connection specification comes in and gives us something that we can work with to make this happen all right so here I am in the spring for graphql documentation there's a section for pagination you can see the graphql cursor connection specification so if you want to learn more about that again if you've been working with graphql you might have seen this in other implementations the idea here is that we pass a couple arguments things like first or after so I want the first 10 after some type of cursor from there you're going to get edges that will give you the cursor every time and then the node is the actual data that you want then you also have page info and then you can just go ahead and read through this specification if you want to learn more about that so what is going to happen here in spring for graphql we have connection types they must be created for every type that needs pagination this adds boilerplate and noise to the schema spring for graphql provides a connection type definition configure to add these types on Startup if they're not already present in the parsed schema files that means that in your schema you only need to do this so we can do something like this and this book connection is automatically going to be created for us so let's go back to our schema and see what this means so instead of getting a list of sessions maybe I want to get paginated sessions so I've taken the first after last and before and what this is going to create is a session connection so I know that the ID is not catching up here doesn't know what that is but I promise you it's been created for us already so great so now this means that instead of fetching all the sessions we're gonna we're gonna create we're gonna get just the ones that we want now we need to implement this on our end right so under event so the event controller we need to declare a data fetcher for sessions so let's go to our event controller and we may need to set up something for this so this is going to be a schema mapping and we'll talk about what we're going to return in a second but this is going to be let's just say void sessions and now you know what are we going to do here so this needs to First Take the parent which is going to be the event and this is where things get fun so we need a way to get the sessions by the event right so how can we do that I think a good place to start would be in the session repository so in the session repository I need to create a method where I can get the um all the sessions by an event ID so I might we're going to start with something called a window a window is new to Spring data I'm going to go ahead and reference you to the documentation there are all kinds of there are a few different ways to get paginated data in Spring data there's also a really nice table in there that says hey like when you need it this way this is the thing that you can reach for so Windows new maybe we'll dive into this a little bit further in another tutorial but for now just a way to get some paginated data but it's a little bit more specific than say a pageable or a slice so when we get a window we're going to call this find by event ID so this is going to take in the event ID we also need something called a scroll position so scroll position we need the limit and we need the sort so sort right so let's go ahead and import this this is coming from there and this is coming from there all right so now we have this method that we can use the scroll position has things in here like key set offset forwards backwards this is just uh part of dealing with spring data stuff here so we have this repository we have this repository method so we need to call this from our event controller somehow right so how are we going to do that well first we need an argument here which we're going to get from bring for graphql so let's say that this is the scroll sub range so we'll just call this the sub range and if we look at scroll sub range and download the sources this is coming from graphql data query container for parameters that limit result elements to a sub range including a relative scroll position which our repository needs number of elements and Direction so this is kind of the important stuff so now what we know we are going to return from here is a window of session okay so how are we going to do this so I'm going to go ahead and paste some code in here now again this is way this is why we ended up using the snapshot of springboot 3.2 because this support right here what we're doing with limit um it was not available in a previous version of spring data so we get the scroll position we say hey on the sub range give me the position or I'll set it to the scroll position dot offset we need a limit so get the count whatever is passed as the say like first or last so give me the first 10 or first 100 or else just default to 10 we could always set that up as a default parameter somewhere in our configuration if we wanted to we want to sort by something so I'm going to sort by title and ascending and then hey from the session repository go ahead and find by event ID and we need to bring that in so private final session repository session Repository add that as a Constructor parameter and that looks good um so now um required type is org hibernate query SPI limit and the provided type was the data domain did we get those types wrong data domain ah so I think that is wrong okay let's see did that fix that all right so let's fix that sort sort and now that should fix that Okay cool so now what happens when we call event sessions this uh is the data resolver for that field so this gets called now this could easily reach out to a another service in our organization but in this case it's just doing pagination we figure out what we need to do pagination here and then we return just the window of that session so think of it as a window into the entire collection we're just getting like those 10 records okay so this means that if we restart application and everything works we should be able to call sessions but pass some some parameters into there right so we could say that we just want the first few so let's try that all right so now when we ask for sessions we are going to provide some parameters we see first after last before so I want to say that I want the first three records now from there this is where the cursor connection spec comes in we don't just ask for the data remember we get things back like edges page info so from edges I can get things like the current cursor I can also get the node so the node is going to include the data that we're interested in so from the node I might say give me the ID the title and the description and then I can also get page info outside of the edges so let's just say here I might want page info like what is available has next start cursor and cursor Etc so let's go ahead and run that and now we see that we get edges inside of there we get nodes so I have a node so these are each of the sessions but there's only three of them and then we have page info has next page okay that's great so what else can we get out of here what is the start cursor what is the end cursor so let's go ahead and run that we see that the start cursor is this the end cursor is this so okay so if that's the start cursor and this is the end cursor now I should be able to do something else so we know this is uh sorted by title so after many summer dies this one I should be able to come in here and say first three but after the cursor which is the end cursor so let's go ahead and run that and now you can see that we get three different ones and then again here's the end cursor we can go ahead and provide that and we can get the next three so that is how you paginate through it using the combination of what nodes are available what is the current cursor what's the next cursor what's the N cursor those kind of things so again that is a really nice way to paginate through data this is based on a specification so we know that if we're implementing this in all of our services across our organization we know that these are going to be consistent which I really like so cool um again check out the documentation for a little bit more info on some of the stuff that we just went through but hey if you found this useful do me a big favor leave me a thumbs up subscribe to the channel and as always friends happy coding [Music]
Info
Channel: Dan Vega
Views: 5,269
Rating: undefined out of 5
Keywords: dan vega, spring for graphql, spring boot, spring framework, graphql, graphql java, graphql paging, graphql pagination
Id: 3YTSh8vJ8eY
Channel Id: undefined
Length: 42min 36sec (2556 seconds)
Published: Wed Jul 19 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.