2
votes

am planning to implement paging using cosmos db continuation token. My api will return results and continuation token to client. My question here is which is the best place to store continuation token? Also the token will be changing per each subsequent request? How to maintain the previous continousTokens?

Here it shows where we get the value from and not where to store it How to pass\user azure continue token via webAPI Pagination in Cosmos DB using Page Size and Page Number

controller.cs

        [Route("myApps")]
        [HttpGet]
        public async Task<IActionResult> GetAllAppsAsync(string continuationToken, CancellationToken cancellationToken)
        {
            var user = this.GetUser();
            var results = await this.appRepository.GetAppsForUserAsync(user, continuationToken, cancellationToken).ConfigureAwait(false);
            var result = this.mapper.Map<AppHeader[]>(results.Value);
            return this.Ok(new KeyValuePair<string, AppDefinitionHeader[]>(results.Key, result));
        }
Repository.cs

 public async Task<KeyValuePair<string, IEnumerable<App>>> GetAppForUserAsync(User user,  string continuationToken, CancellationToken cancellationToken)
        {
            
            try
            {
                FeedOptions queryOptions = new FeedOptions
                {
                    MaxItemCount = 2,
                    RequestContinuation = continuationToken
                };
                string token = string.Empty;
                var query = this.factory.GetClient()
                    .CreateDocumentQuery<AppDefinitionResource>(
                        UriFactory.CreateDocumentCollectionUri(DatabaseName, CollectionName),
                        queryOptions)
                    .AsDocumentQuery();

                List<AppDefinition> results = new List<AppDefinition>();
                while (query.HasMoreResults && results.Count <= 2)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    var response = await query.ExecuteNextAsync<App>(cancellationToken).ConfigureAwait(false);

                    var apps = this.mapper.Map<App[]>(response);
                    results.AddRange(apps);
                    token = response.ResponseContinuation;
                }

                return new KeyValuePair<string, IEnumerable<App>>(token, results);
            }
           
        }

Update :

How to maintain the previous continousTokens?

Ex : If there are 100 records and pageSize=10 and pageNumbers are 1,2,3,4,5,6,7,8,9,10 and if user clicks randomly on any page. In that case how to fetch required records? or just if I place "Previous" button in the below screenshot and want to traverse back in the list.

https://i.stack.imgur.com/BMwI2.gif

1
That's kind of up to you - you'll need to return it to your API's clients and expose a way for the pagination to be passed back to your API by your clients.Martin Costello
Hey Martin, Just updated the question, Am passing continuationToken and results to client and in the subsequent request how to bind continuationToken in controller which is passed from client? I cannot use [FromUri] because token is quite huge and neither I can use body because of Get request. what's the best practice here?Deepak Kothari

1 Answers

2
votes

Yes, the token changes for each request, so that you can retrieve previous pages with previous tokens.

If you pass the token on to the client to keep the backend stateless, you should not send the token within the URL or a URL query, since the token can get quite large. Within the client you can just keep the token in memory. In most cases it's not necessary to persist the token, since a token should be short-lived. It should be short-lived since paginated results reflects all the changes that happen between each page request.


Update based on comments

Since the token may be large, it has to be passed in the body of the request, which also means it shouldn't be a GET request.

An alternative solution would be to store the token in a cache with a smaller key (e.g. a GUID) in the backend (with some limited TTL) and just pass the key between backend and client. That way you can pass the key in the URL or in a URL query. But that requires the backend to hold state.