© Balaji Varanasi and Maxim Bartkov 2022
B. Varanasi, M. BartkovSpring RESThttps://doi.org/10.1007/978-1-4842-7477-4_7

7. Versioning, Paging, and Sor ting

Balaji Varanasi1   and Maxim Bartkov2
(1)
Salt Lake City, UT, USA
(2)
Kharkov, Ukraine
 
In this chapter we will discuss the following:
  • Strategies for versioning REST services

  • Adding pagination capabilities

  • Adding sorting capabilities

We all are familiar with the famous proverb “The only thing constant in life is change.” This applies to software development. In this chapter, we will look at versioning our API as a way to handle such changes. Additionally, dealing with large datasets can be problematic especially when mobile clients are involved. Large datasets can also result in server overload and performance issues. To handle this, we will employ paging and sorting techniques and send data in manageable chunks.

Versioning

As user requirements and technology change, no matter how planned our design is, we will end up changing our code. This will involve making changes to REST resources by adding, updating, and sometimes removing attributes. Although the crux of the API—read, create, update, and remove one or more resources—remains the same, this could result in such drastic changes to the representation that it may break any existing consumers. Similarly, changes to functionality such as securing our services and requiring authentication or authorization can break existing consumers. Such major changes typically call for new versions of the API.

In this chapter, we will be adding paging and sorting functionality to our QuickPoll API. As you will see in later sections, this change will result in changes to the representations returned for some of the GET HTTP methods. Before we version our QuickPoll API to handle paging and sorting, let’s look at some approaches for versioning.

Versioning Approaches

There are four popular approaches to versioning a REST API:
  • URI versioning

  • URI parameter versioning

  • Accept header versioning

  • Custom header versioning

None of these approaches are silver bullets, and each has its fair share of advantages and disadvantages. In this section we will look at these approaches along with some real-world public APIs that use them.

URI Versioning

In this approach, version information becomes part of the URI. For example, http://api.example.org/v1/users and http://api.example.org/v2/users represent two different versions of an application API. Here we use v notation to denote versioning, and the numbers 1 and 2 following the v indicate the first and second API versions.

URI versioning has been one of the most commonly used approaches and is used by major public APIs such as Twitter, LinkedIn, Yahoo, and SalesForce. Here are some examples:

As you can see, LinkedIn, Yahoo, and SalesForce use the v notation. In addition to a major version, SalesForce uses a minor version as part of its URI version. Twilio, by contrast, takes a unique approach and uses a timestamp in the URI to differentiate its versions.

Making a version part of the URI is very appealing as the version information is right in the URI. It also simplifies API development and testing. Folks can easily browse and use different versions of REST services via a web browser. On the contrary, this might make client’s life difficult. For example, consider a client storing references to user resources in its database. On switching to a new version, these references get outdated and the client has to do a mass database update to upgrade references to new version.

URI Parameter Versioning

This is similar to the URI versioning that we just looked at except that the version information is specified as a URI request parameter. For example, the URI http://api.example.org/users?v=2 uses the version parameter v to represent the second version of the API. The version parameter is typically optional, and a default version of the API will continue working for requests without version parameter. Most often, the default version is the latest version of the API.

Although as not popular as other versioning strategies, a few major public APIs such as Netf lix have used this strategy. The URI parameter versioning shares the same disadvantages of URI versioning. Another disadvantage is that some proxies don’t cache resources with a URI parameter, resulting in additional network traffic.

Accept Header Versioning

This versioning approach uses the Accept header to communicate version information. Because the header contains version information, there will be only one URI for multiple versions of API.

Up to this point, we have used standard media types such as "application/json" as part of the Accept header to indicate the type of content the client expects. To pass additional version information, we need a custom media type. The following convention is popular when creating a custom media type:
vnd.product_name.version+ suffix

The vnd is the starting point of the custom media type and indicates vendor. The product or producer name is the name of the product and distinguishes this media type from other custom product media types. The version part is represented using strings such as v1 or v2 or v3. Finally, the suffix is used to specify the structure of the media type. For example, the +json suffix indicates a structure that follows the guidelines established for media type "application/json." RFC 6389 (https://tools.ietf.org/html/rfc6839) gives a full list of standardized prefixes such as +xml, +json, and +zip. Using this approach, a client, for example, can send an application/vnd.quickpoll.v2+json accept header to request the second version of the API.

The Accept header versioning approach is becoming more and more popular as it allows fine-grained versioning of individual resources without impacting the entire API. This approach can make browser testing harder as we have to carefully craft the Accept header. GitHub is a popular public API that uses this Accept header strategy. For requests that don’t contain any Accept header, GitHub uses the latest version of the API to fulfill the request.

Custom Header Versioning

The custom header versioning approach is similar to the Accept header versioning approach except that instead of the Accept header, a custom header is used. Microsoft Azure takes this approach and uses the custom header x-ms-version. For example, to get the latest version of Azure at the time of writing this book, your request needs to include a custom header:
x-ms-version: 2021-09-14

This approach shares the same pros and cons as that of the Accept header approach. Because the HTTP specification provides a standard way of accomplishing this via the Accept header, the custom header approach hasn’t been widely adopted.

Deprecating an API

As you release new versions of an API, maintaining older versions becomes cumbersome and can result in maintenance nightmares. The number of versions to maintain and their longevity depend on the API user base, but it is strongly recommended to maintain at least one older version.

API versions that will no longer be maintained need to be deprecated and eventually retired. It is important to remember that deprecation is intended to communicate that the API is still available but will cease to exist in the future. API users should be given plenty of notices about deprecation so that they can migrate to newer versions.

QuickPoll Versioning

In this book, we will be using the URI versioning approach to version the QuickPoll REST API.

Implementing and maintaining different versions of an API can be difficult, as it generally complicates code. We want to make sure that changes in one version of code don’t impact other versions of the code. To improve maintainability, we want to make sure that we avoid code duplication as much as possible. Here are two approaches for organizing code to support multiple API versions:
  • Complete code replication—In this approach, you replicate the entire code base and maintain parallel code paths for each version. Popular API builder Apigility takes this approach and clones the entire code base for each new version. This approach makes it easy to make code changes that wouldn’t impact other versions. It also makes it easy to switch backend datastores. This would also allow each version to become a separate deployable artifact. Although this approach provides a lot of flexibility, we will be duplicating the entire code base.

  • Version-specific code replication—In this approach, we only replicate the code that is specific to each version. Each version can have its own set of controllers and request/response DTO objects but will reuse most of the common service and backend layers. For smaller applications, this approach can work well as version-specific code can simply be separated into different packages. Care must be taken when making changes to the reused code, as it might have impact on multiple versions.

Spring MVC makes it easy to version a QuickPoll application using the URI versioning approach. Considering that versioning plays a crucial role in managing changes, it is important that we version as early as possible in the development cycle. Hence, we will assign a version (v1) to all of the QuickPoll services that we have developed so far. To support multiple versions, we will follow the second approach and create a separate set of controllers.

Note

In this chapter we will continue building on the work that we did on the QuickPoll application in the previous chapters. Alternatively, a starter project inside the Chapter7starter folder of the downloaded source code is available for you to use. The completed solution is available under the Chapter7final folder. Please refer to this solution for complete listings containing getters/setters and additional imports. The downloaded Chapter7 folder also contains an exported Postman collection containing REST API requests associated with this chapter.

We begin the versioning process by creating two packages com.apress.v1.controller and com.apress.v2.controller. Move all of the controllers from the com.apress.controller package to the com.apress.v1.controller. To each controller in the new v1 package, add a class-level @RequestMapping ("/v1") annotation. Because we will have multiple versions of controllers, we need to give unique component names to individual controllers. We will follow the convention of appending version number to the unqualified class name to derive our component name. Using this convention, the v1 PollController will have a component name pollControllerV1.

Listing 7-1 shows the portion of the PollController class with these modifications. Notice that the component name is provided as a value to the @RestController annotation. Similarly, assign the component name voteControllerV1 to the v1 VoteController and computeResultControllerV1 to the v1 ComputeResultController.
package com.apress.v1.controller;
import org.springframework.web.bind.annotation.RequestMapping;
@RestController("pollControllerV1")
@RequestMapping("/v1")
@Api(value = "polls", description = "Poll API")
public class PollController {
}
Listing 7-1

Version 1 of the Poll Controller

Note

Even though the behavior and code of VoteController and ComputeResultControler don’t change across versions, we are copying the code to keep things simple. In real-world scenarios, refactor code into reusable modules, or use inheritance to avoid code duplication.

With the class-level @RequestMapping annotation in place, all of the URIs in the v1 PollController become relative to "/v1/." Restart the QuickPoll application, and, using Postman, verify that you can create a new Poll at the new http://localhost:8080/v1/polls endpoint.

To create the second version of the API, copy all of the controllers from the v1 package to the v2 package. Change the class-level RequestMapping value from "/v1/" to "/v2/" and the component name suffix from "V1" to "V2." Listing 7-2 shows the modified portions of the V2 version of the PollController. Because the v2 PollController is a copy of the v1 PollController, we have omitted the PollController class implementation from Listing 7-2.
@RestController("pollControllerV2")
@RequestMapping("/v2")
@Api(value = "polls", description = "Poll API")
public class PollController {
        // Code copied from the v1 Poll Controller
}
Listing 7-2

Version 2 of the Poll Controller

Once you have completed modifications for the three controllers, restart the QuickPoll application, and, using Postman, verify that you can create a new poll using the http://localhost:8080/v2/polls endpoint. Similarly, verify that you can access the VoteController and ComputeResultController endpoints by accessing the http://localhost:8080/v2/votes and http://localhost:8080/v2/computeresult endpoints.

SwaggerConfig

The versioning changes that we made require changes to our Swagger configuration so that we can use the UI to test and interact with both REST API versions. Listing 7-3 shows the refactored com.apress.SwaggerConfig class. As discussed in the previous chapter, a springfox.documentation.spring.web.plugins.Docket instance represents a Swagger group. Hence, the refactored SwaggerConfig class contains two methods, each returning a Docket instance representing an API group. Also, notice that we have extracted API information to its own method and used it to configure both instances of Docket.
package com.apress;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.Collections;
@Configuration
public class SwaggerConfiguration {
    @Bean
    public Docket apiV1() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.regex("/v1/*.*"))
                .build()
                .apiInfo(apiInfo("v1"))
                .groupName("v1")
                .useDefaultResponseMessages(false);
    }
    @Bean
    public Docket apiV2() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.regex("/v2/*.*"))
                .build()
                .apiInfo(apiInfo("v2"))
                .groupName("v2")
                .useDefaultResponseMessages(false);
    }
    private ApiInfo apiInfo(String version) {
        return new ApiInfo(
                "QuickPoll REST API",
                "QuickPoll Api for creating and managing polls",
                version,
                "Terms of service",
                new Contact("Maxim Bartkov", "www.linkedin.com/in/bartkov-maxim", "[email protected]"),
                "MIT License", "http://opensource.org/licenses/MIT", Collections.emptyList());
    }
}
Listing 7-3

Refactored SwaggerConfig Class

With this newly refactored SwaggerConfig, restart the QuickPoll application and launch Swagger UI in a web browser at http://localhost:8080/swagger-ui/index.html. After the UI has launched, append the request parameter ?group=v2 to the http://localhost:8080/v2/api-docs URI in the Swagger UI’s input box and hit Explore. You should see and interact with the v2 version of the API as shown in Figure 7-1.
../images/332520_2_En_7_Chapter/332520_2_En_7_Fig1_HTML.png
Figure 7-1

Swagger UI for QuickPoll 2.0 version

This concludes the configuration needed to version our QuickPoll application and sets the stage for adding pagination and sorting support in the final two sections of this chapter.

Pagination

REST APIs are consumed by a variety of clients ranging from desktop applications to Web to mobile devices. Hence, while designing a REST API capable of returning vast datasets, it is important to limit the amount of data returned for bandwidth and performance reasons. The bandwidth concerns become more important in the case of mobile clients consuming the API. Limiting the data can vastly improve the server’s ability to retrieve data faster from a datastore and the client’s ability to process the data and render the UI. By splitting the data into discrete pages or paging data, REST services allow clients to scroll through and access the entire dataset in manageable chunks.

Before we start implementing pagination in our QuickPoll application, let’s look at four different pagination styles: page number pagination, limit offset pagination, cursor-based pagination, and time-based pagination.

Page Number Pagination

In this pagination style, the clients specify a page number containing the data they need. For example, a client wanting all the blog posts in page 3 of our hypothetical blog service can use the following GET method:
http://blog.example.com/posts?page=3
The REST service in this scenario would respond with a set of posts. The number of posts returned depends on the default page size set in the service. It is possible for the client to override the default page size by passing in a page-size parameter:
http://blog.example.com/posts?page=3&size=20
GitHub’s REST services use this pagination style. By default, the page size is set to 30 but can be overridden using the per_page parameter :
https://api.github.com/user/repos?page=2&per_page=100

Limit Offset Pagination

In this pagination style, the client uses two parameters: a limit and an offset to retrieve the data that they need. The limit parameter indicates the maximum number of elements to return, and the offset parameter indicates the starting point for the return data. For example, to retrieve 10 blog posts starting from the item number 31, a client can use the following request:
http://blog.example.com/posts?limit=10&offset=30

Cursor-Based Pagination

In this pagination style, the clients make use of a pointer or a cursor to navigate through the dataset. A cursor is a service-generated random character string that acts as a marker for an item in the dataset. To understand this style, consider a client making the following request to get blog posts:
http://blog.example.com/posts
On receiving the request, the service would send data similar to this:
{
        "data" :     [
                      ... Blog data
                               ],
              "cursors" : {
                       "prev" : null,
                       "next" : "123asdf456iamcur"
                          }
}
This response contains a set of blogs representing a subset of the total dataset. The cursors that are part of the response contain a prev field that can be used to retrieve the previous subset of the data. However, because this is the initial subset, the prev field value is empty. The client can use the cursor value in the next field to get the next subset of the data using the following request:
http://api.example.com/posts?cursor=123asdf456iamcur

On receiving this request, the service would send the data along with the prev and next cursor fields. This pagination style is used by applications such as Twitter and Facebook that deal with real-time datasets (tweets and posts) where data changes frequently. The generated cursors typically don’t live forever and should be used for short-term pagination purposes only.

Time-Based Pagination

In this style of pagination, the client specifies a timeframe to retrieve the data in which they are interested. Facebook supports this pagination style and requires time specified as a Unix timestamp. These are two Facebook example requests:
https://graph.facebook.com/me/feed?limit=25&until=1364587774
https://graph.facebook.com/me/feed?limit=25&since=1364849754

Both examples use the limit parameter to indicate the maximum number of items to be returned. The until parameter specifies the end of the time range, whereas the since parameter specifies the beginning of the time range.

Pagination Data

All the pagination styles in the previous sections return only a subset of the data. So, in addition to supplying the requested data, it becomes important for the service to communicate pagination-specific information such as total number of records or total number of pages or current page number and page size. The following example shows a response body with pagination information:
{
    "data": [
         ... Blog Data
    ],
    "totalPages": 9,
    "currentPageNumber": 2,
    "pageSize": 10,
    "totalRecords": 90
}
Clients can use the pagination information to assess the current state as well as construct URLs to obtain the next or previous datasets. The other technique services employ is to include the pagination information in a special Link header. The Link header is defined as part of RFC 5988 (http://tools.ietf.org/html/rfc5988). It typically contains a set of ready-made links to scroll forward and backward. GitHub uses this approach; here is an example of a Link header value:
Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next", <https://api.github.com/user/repos?page=50&per_page=100>; rel="last"

QuickPoll Pagination

To support large poll datasets in a QuickPoll application, we will be implementing the page number pagination style and will include the paging information in the response body.

We begin the implementation by configuring our QuickPoll application to load dummy poll data into its database during the bootstrapping process. This would enable us to test our polling and sorting code. To achieve this, copy the import.sql file from the downloaded chapter code into srcmain esources folder. The import.sql file contains DML statements for creating test polls. Hibernate out of the box loads the import.sql file found under the classpath and executes all of the SQL statements in it. Restart the QuickPoll application, and navigate to http://localhost:8080/v2/polls in Postman; it should list all of the loaded test polls.

Spring Data JPA and Spring MVC provides out-of-the-box support for the page number pagination style, making our QuickPoll paging implementation easy. Central to paging (and sorting) functionality in Spring Data JPA is the org.springframework.data.repository.PagingAndSortingRepository interface shown in Listing 7-4.
public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID> {
    Page<T> findAll(Pageable pageable);
    Iterable<T> findAll(Sort sort);
}
Listing 7-4

Spring Data JPA’s Paging and Sorting Repository

The PagingAndSortingRepository interface extends the CrudRepository interface that we have been using so far in the QuickPoll application. Additionally, it adds two finder methods that return entities matching the paging and sorting criteria provided. The findAll method responsible for paging takes a Pageable instance to read information such as page size and page number. Additionally, it also takes sorting information, which we will zoom in on in a later section of this chapter. This findAll method returns a Page instance that contains the data subset and the following information:
  • Total elements—Total elements in the result set

  • Number of elements—Number of elements in the returned subset

  • Size—The maximum number of elements in each page

  • Total pages—Total number of pages in the result set

  • Number—Returns the current page number

  • Last—Flag indicating if it is the last data subset

  • First—Flag indicating if it is the first data subset

  • Sort—Returns parameters used for sorting, if any

The next step in implementing paging in QuickPoll is to make our PollRepository extend PagingAndSortingRepository instead of current CrudRepository. Listing 7-5 shows the new PollRepository implementation. Because the PagingAndSortingRepository extends the CrudRepository, all of the functionality needed for the first version of our API remains intact.
package com.apress.repository;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.apress.domain.Poll;
public interface PollRepository extends PagingAndSortingRepository<Poll, Long> {
}
Listing 7-5

PollRepository Implementation

Changing the repository to use PagingAndSortingRepository concludes our backend implementation needed for paging. We now move on to refactoring the V2 PollController so that it uses the new paging finder method. Listing 7-6 shows the refactored getAllPolls method of the V2 com.apress.v2.controller.PollController. Notice that we have added the Pageable parameter to the getAllPolls method. On receiving a GET request on "/polls," Spring MVC inspects the request parameters, constructs a Pageable instance, and passes it to the getAllPolls method. Typically, the passed-in instance is of the type PageRequest. The Pageable parameter is then passed to the new finder method, and the paged data is retuned as part of the response.
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@RequestMapping(value="/polls", method=RequestMethod.GET)
@ApiOperation(value = "Retrieves all the polls", response=Poll.class, responseContainer="List")
public ResponseEntity<Page<Poll>> getAllPolls(Pageable pageable) {
        Page<Poll> allPolls = pollRepository.findAll(pageable);
        return new ResponseEntity<>(allPolls, HttpStatus.OK);
}
Listing 7-6

GetAllPolls Method with Paging Functionality

This concludes the QuickPoll pagination implementation. Restart the QuickPoll application, and submit a GET request to http://localhost:8080/v2/polls?page=0&size=2 using Postman. The response should contain two poll instances with paging-related metadata. Figure 7-2 shows the request as well as the metadata portion of the response.
../images/332520_2_En_7_Chapter/332520_2_En_7_Fig2_HTML.jpg
Figure 7-2

Paged results along with paging metadata

Note

Spring Data JPA uses a zero index–based paging approach. Hence, the first page number starts with 0 and not 1.

Changing Default Page Size

Spring MVC uses an org.springframework.data.web.PageableHandlerMethodArgumentResolver to extract paging information from the request parameters and inject Pageable instances into Controller methods. Out of the box, the PageableHandlerMethodArgumentResolver class sets the default page size to 20. Hence, if you perform a GET request on http://localhost:8080/v2/polls, the response would include 20 polls. Although 20 is a good default page size, there might be occasions when you might want to change it globally in your application. To do this, you need to create and register a new instance of PageableHandlerMethodArgumentResolver with the settings of your choice.

Spring Boot applications requiring changes to default MVC behavior need to create classes of type org.springframework.web.servlet.config.annotation.WebMvcConfigurer and use its callback methods for customization. Listing 7-7 shows the newly created QuickPollMvcConfigAdapter class in the com.apress package with the configuration to set the default page size to 5. Here we are using the WebMvcConfigurer's addArgumentResolvers callback method. We begin the method implementation by creating an instance of PageableHandlerMethodArgumentResolver. The setFallbackPageable method, as the name suggests, is used by Spring MVC when no paging information is found in the request parameters. We create a PageRequest instance with 5 as the default page size and pass it to the setFallbackPageable method. We then register our PageableHandlerMethodArgumentResolver instance with Spring using the passed-in argumentResolvers parameter.
package com.apress;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class QuickPollMvcConfigAdapter implements WebMvcConfigurer {
        @Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableHandlerMethodArgumentResolver phmar = new PageableHandlerMethodArgumentResolver();
            // Set the default size to 5
            phmar.setFallbackPageable(PageRequest.of(0, 5));
            argumentResolvers.add(phmar);
        }
}
Listing 7-7

Code to Change Default Page Size to 5

Restart the QuickPoll application and perform a GET request on http://localhost:8080/v2/polls using Postman. You will notice that the response now includes only five polls. The associated paging metadata is shown in Listing 7-8.
{
    ..... Omitted Poll Data ......
    "totalPages": 4,
    "totalElements": 20,
    "last": false,
    "size": 5,
    "number": 0,
    "sort": null,
    "numberOfElements": 5,
    "first": true
}
Listing 7-8

Paging Metadata for Default Page Size 5

Sor  ting

Sorting allows REST clients to determine the order in which items in a dataset are arranged. REST services supporting sorting allow clients to submit parameters with properties to be used for sorting. For example, a client can submit the following request to sort blog posts based on their created date and title:

http://blog.example.com/posts?sort=createdDate,title

Sort Ascending or Sort Descending

The REST services can also allow the clients to specify one of the two sort directions: ascending or descending. Because there is no set standard around this, the following examples showcase popular ways for specifying sort direction:
http://blog.example.com/posts?sortByDesc=createdDate&sortByAsc=title
http://blog.example.com/posts?sort=createdDate,desc&sort=title,asc
http://blog.example.com/posts?sort=-createdDate,title
In all of these examples, we are retrieving blog posts in the descending order of their created date. Posts with the same created date are then sorted based on their titles:
  • In the first approach, the sort parameter clearly specifies if the direction should be ascending or descending.

  • In the second approach, we have used the same parameter name for both directions. However, the parameter value spells out the sort direction.

  • The last approach uses the “-” notation to indicate that any property prefixed with a “-” should be sorted on a descending direction. Properties that are not prefixed with a “-” will be sorted in the ascending direction.

QuickPoll Sorting

Considering that sorting is typically used in conjunction with paging, Spring Data JPA’s PagingAndSortingRepository and Pageable implementations are designed to handle and service sorting requests from the ground up. Hence, we don’t require any explicit implementation for sorting.

To test sorting functionality, submit a GET request to http://localhost:8080/v2/polls/?sort=question using Postman. You should see the response with Polls sorted in ascending order of their question text along with sort metadata. Figure 7-3 shows the Postman request along with the sort metadata.
../images/332520_2_En_7_Chapter/332520_2_En_7_Fig3_HTML.png
Figure 7-3

Sort metadata

To sort on multiple fields with different sort directions, Spring MVC requires you to follow the second approach discussed in the previous section. The following request sorts on ascending question value and descending id value:
http://localhost:8080/v2/polls/?sort=question,asc&sort=id,desc

Summary

In this chapter we reviewed the different strategies for versioning REST API. We then implemented versioning in QuickPoll using the URL versioning approach. We also reviewed the different approaches for dealing with large datasets using pagination and sorting techniques. Finally, we used Spring Data’s out-of-the-box functionality to implement page number pagination style. In the next chapter, we will review strategies for securing REST services.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.103.96