Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Brice Pellé
Brice Pellé

Posted on

     

Implementing pagination with AWS AppSync

Why Pagination

In this post, I'm going to show how you can get started with pagination in GraphQL using an AWS AppSync API and the AWS Amplify framework. The primary reason to use pagination is to control and limit the amount of data that is fetched from your backend and returned to your client at once. Pagination can help build efficient and cost-effective solutions by controlling the amount of work done to retrieve data in the backend. It can also improve overall responsiveness by returning smaller sets of data faster to the application.

Types of pagination

2 common forms of pagination are offset-based and token-based pagination. With offset-based pagination, you specify the page size and a starting point: the row after which, or the page at which you want to start fetching data. When using a page, the page along with the page size identifies the row after which to start fetching data (e.g.:offset = (page - 1) * page_size - 1 in a zero-based index). You can find offset-based pagination when dealing with relational databases. For example, in mysql you can fetch data from an offset using theLIMITclause. In this example, 5 is the offset (fetch after that row) and 10 is the page size (return 10 items).

SELECT*FROMtblLIMIT5,10;#Retrieverows6-15
Enter fullscreen modeExit fullscreen mode

With token-based pagination, a token is used to specify the record after which additional items should be fetched, along with the page size. The implementation of the token is system-specific. DynamoDB is an example of at system that uses token-pagination topaginate the results from Query operations. With DynamoDB, the result of a query may return aLastEvaluatedKey element. This is a token indicating that additional items can be fetched for this specific query. You can then continue the query and get the rest of the items by repeating the query and settingExclusiveStartKey to the last value ofLastEvaluatedKey.

How pagination works with AWS AppSync

AWS AppSync is a fully managed GraphQl service that makes it easy to build data-driven solutions in the cloud. Using the AWS Amplify GraphQL transform, you can quickly build AppSync APIs with types backed by data sources in your accounts. For example, you can use the@model directive in your schema to generate an API with types backed by DynamoDB tables.

Let’s take a look a how to work with pagination using Amplify and AppSync. I built a simple React app to showcase pagination with AppSync:Pagination with AWS AppSync. You can find the entire code here:https://github.com/onlybakam/todo-app-pagination. I am using theAmplify API library to easily interact with the AppSync API.

preview

I created a new amplify project and created an AppSync API using the CLI. To find out how to get started with this, check out theGetting Started guide. I then created the following schema:

typeTodo@model@key(fields:["owner","dueOn"]name:"ByDate"queryField:"listTodosByDate"){id:ID!name:String!description:Stringowner:String!dueOn:AWSDateTime!}
Enter fullscreen modeExit fullscreen mode

The@key directive allows you to create a query to fetch todos per owner sorted by their due date. Check outAmplify Framework Docs - Data access patterns to find out more about how the@key can enable various data access patterns.

To fetch a list of todos for an owner, you execute theListTodosByDate query. You can specify the amount of items you want returned using thelimit argument. By default, the limit is set to 100. You can also specify the order the items are sorted by usingsortDirection (set toASC orDESC).

queryListTodosByDate($owner:String$dueOn:ModelStringKeyConditionInput$sortDirection:ModelSortDirection$filter:ModelTodoFilterInput$limit:Int$nextToken:String){listTodosByDate(owner:$ownerdueOn:$dueOnsortDirection:$sortDirectionfilter:$filterlimit:$limitnextToken:$nextToken){items{idnamedescriptionownerdueOn}nextToken}}
Enter fullscreen modeExit fullscreen mode

The query returns a list of items and anextToken field. IfnextToken is set, this indicates there are more items to fetch. In a subsequent query, you can pass this value in the query arguments to continue fetching items starting after the final item that was last returned.

In the application, we want to be able to paginate forward and backwards through todos. To do this, we maintain 3 state variables

const[nextToken,setNextToken]=useState(undefined)const[nextNextToken,setNextNextToken]=useState()const[previousTokens,setPreviousTokens]=useState([])
Enter fullscreen modeExit fullscreen mode
  • nextToken is the the token used to fetch the current items
  • nextNextToken is the token returned by the last fetch. If this token is set, you can paginate forward.
  • previousTokens is an array of previous tokens. These tokens allow us to paginate the todo list backwards. If there is a token in the array, you can paginate backwards.

A new set of todos is fetched whenever theowner,nextToken orsortDirection changes.

import{listTodosByDate}from'./graphql/queries'import{API,graphqlOperation}from'@aws-amplify/api'useEffect(()=>{constfetch=async()=>{constvariables={nextToken,owner,limit,sortDirection,}constresult=awaitAPI.graphql(graphqlOperation(listTodosByDate,variables))setNextNextToken(result.data.listTodosByDate.nextToken)setTodos(result.data.listTodosByDate.items)}fetch()},[nextToken,owner,sortDirection])
Enter fullscreen modeExit fullscreen mode

Loading the initial list of items

loading initial list

When the owner changes, all the fields are reset.nextToken is set toundefined which makes the query fetch items from the beginning. When the query returns, the value ofnextToken in the result is assigned tonextNextToken. It’s important here to not immediately assign the value to thenextToken state as this would trigger another fetch right away.

Pagination forward

pagination forward

IfnextNextToken is set, you can paginate forward. When the user presses the “Next” button, the current value ofnextToken is pushed on thepreviousTokens array. Next,nextToken is set to the current value ofnextNextToken. FinallynextNextToken is then set toundefined. When the query returns, again the value ofnextToken in the result is assigned tonextNextToken. This process can be repeated as long as the query indicates that there are more items to paginate.

Pagination backwards

pagination backwards

ThepreviousTokens array stores the previously used tokens in order (think of is as a history stack). To paginate backwards, the last value is popped off the array and assigned tonextToken which triggers a new query. This allows you to repeat the query from a known "starting point". The query results may return a differentnextToken. This is because items may have been inserted or deleted since the nextTokenA was returned. By assigning the value ofnextToken in the result is tonextNextToken, you keep paginating forward from the right position.

list of tokens

Conclusion

This post provided an overview of pagination and a simple solution for handling pagination in a React app with an AppSync API. Getting started with AWS AppSync and AWS Amplify is really easy. Check out the docshere.

You can find the code for this application here:https://github.com/onlybakam/todo-app-pagination. You can check out an implementation of it here:Pagination with AWS AppSync.

Top comments(9)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
mbappai profile image
Mujahid Bappai
Fullstack web2 dev trying to figure out what the hell is going on in web3
  • Location
    Nigeria
  • Work
    Fullstack developer freelancer
  • Joined

Great work Bryce for such an amazing explanation! I am however curious to know how to query say page 5 of your dynamodb without having it’s token stored in the previousTokens state array. Basically an offset query like you explained, all while using a token based pagination system like this. Thanks in advance for the feedback.

CollapseExpand
 
onlybakam profile image
Brice Pellé
  • Location
    Dallas, TX
  • Joined

this is not possible with dynamodb. if this type of indexing is needed, you may have to use another data source. you'll typically find this type offset indexing with sql databases. of course there are pros/cons to the diff implementations.

CollapseExpand
 
mbappai profile image
Mujahid Bappai
Fullstack web2 dev trying to figure out what the hell is going on in web3
  • Location
    Nigeria
  • Work
    Fullstack developer freelancer
  • Joined

Thanks for the reply Brice. I recently started looking into amazon redshift, which basically allows you to copy your nosql database into it which then wraps it around an sql querying interface for you to use sql queries on.
PS: The redshift info above might not be entirely accurate as I'm still yet to look deeper into it.

CollapseExpand
 
zirkelc profile image
Chris Cook
I post things so I don’t forget them
  • Work
    Co-Founder at Flyweight.io
  • Joined

I'm currently evaluating the pagination of list queries generated by Amplify. The first initial query with alimit variable does return an emptyitems array and anextToken. Then I have to send a second query with thisnextToken to get the actual first batch ofitems. Is this normal? Why doesn't the first query return items plus pagination token?

CollapseExpand
 
peteprovar profile image
Pete Haughie
  • Joined

I've been trying to implement something like this over the last day or so and am having an issue debugging it.
When I checked the demonstration I realised that the same bug is present in there too. The nextToken never updates after the first call.

CollapseExpand
 
sashrika profile image
Sashrika Waidyarathna
Builds applications which customers love. Explores the possibilities with Ethereum and other decentralized technologies.
  • Location
    Sri Lanka
  • Education
    University of Moratuwa
  • Work
    Tech Lead at LiveRoom (Pvt) Ltd
  • Joined

Such a brilliant work Brice. What is your opinion on showing the total number of pages? Can we know it before hand?

CollapseExpand
 
onlybakam profile image
Brice Pellé
  • Location
    Dallas, TX
  • Joined

typically how you do this depends on the data source. With DynamoDB, this is not something that can be retried from the table directly. the best thing to do is to keep track of your count in a separate table. on way to do this is to use dynamodb streams.

CollapseExpand
 
mbappai profile image
Mujahid Bappai
Fullstack web2 dev trying to figure out what the hell is going on in web3
  • Location
    Nigeria
  • Work
    Fullstack developer freelancer
  • Joined
• Edited on• Edited

Not being able to have the total count of your table being returned as part of your response object is one of dynamodb‘s biggest shortcomings till this day. There is a long trending issue that opened on GitHub trying to resolve this problem to which you can find the linkhere. You can find some work arounds implemented by other developers on the thread as well as mine which will be posted in a not too far distant future.

CollapseExpand
 
drclohite profile image
David White
Working with security, governance, and compliance teams I am constantly delighted to discover many CISOs and cybersecurity analysts share a lot of humour.
  • Location
    London, UK
  • Work
    Full Stack developer & Evangelist
  • Joined

Very good, I was able to adapt this to make it work for my application. saved me hours if not days! Thank you

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Dallas, TX
  • Joined

More fromBrice Pellé

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp