Wisdom
  • Welcome
  • core
    • Flyway
    • Bean Validation
    • Lombok
    • Webclient
      • Generic Webclient Api
      • SSL Certificate
    • Application Event Publisher
    • REST API's Design
      • Http Methods and Status Codes
      • Resource Naming and URL Structure
      • Request / Response Design
      • Versioning Strategies
      • Filtering and Searching In API
    • Spring Boot Mail Integration
      • Sendgrid
    • Template Engines
      • Java Template Engine [JTE]
  • security
    • Complete Guide to URL Matchers in Spring Security: Types, Examples, Pros, Cons, and Best Use Cases
    • Passwordless Login With Spring Security 6
      • Spring Security OneTimeToken
        • One Time Token with default configuration
        • One Time Token with custom configuration
        • One Time Token With Jwt
  • others
    • How to Integrate WhatsApp for Sending Messages in Your Application
  • java
    • Interview Questions
      • Constructor
      • Serialization
      • Abstract Class
    • GSON
      • Type Token
      • Joda Datetime Custom Serializer and Deserializer
  • Nginx
    • Core Concepts and Basics
    • Deep Dive on NGINX Configuration Blocks
    • Deep Dive on NGINX Directives
    • Deep Dive into Nginx Variables
    • Nginx as a Reverse Proxy and Load Balancer
    • Security Hardening in NGINX
    • Performance Optimization & Tuning in NGINX
    • Dynamic DNS Resolution in Nginx
    • Advanced Configuration & Use Cases in NGINX
    • Streaming & Media Delivery in NGINX
    • Final Configuration
  • Angular
    • How to Open a PDF or an Image in Angular Without Dependencies
    • Displaying Colored Logs with Search Highlighting in Angular 6
    • Implementing Auto-Suggestion in Input Field in Angular Template-Driven Forms
    • Creating an Angular Project Using npx Without Installing It Globally
    • Skip SCSS and Test Files in Angular with ng generate
  • Javascript
    • When JavaScript's Set Falls Short for Ensuring Uniqueness in Arrays of Objects
    • Demonstrating a Function to Get the Last N Months in JavaScript
    • How to Convert Numbers to Words in the Indian Numbering System Using JavaScript
    • Sorting Based on Multiple Criteria
  • TYPESCRIPT
    • Using Omit in TypeScript
Powered by GitBook
On this page
  • 1. Using Query Parameters
  • 2: Using Specifications (Dynamic Queries)
  • 3. Combining Filtering and Searching
  • 4. Pagination and Sorting
  1. core
  2. REST API's Design

Filtering and Searching In API

Filtering and searching are essential features in REST APIs, especially when dealing with large datasets. They allow clients to retrieve only the relevant data, improving performance and usability.

We can do effective filtering, searching and sorting in the following ways -

1. Using Query Parameters

Query parameters in a REST API are key-value pairs appended to the URL after a ?. For filtering and searching:

  • Standardized Syntax: Use clear and intuitive names for query parameters:

    • field=value: Simple equality filter (e.g., /users?role=admin).

    • field[operator]=value: For advanced filtering using operators ([gt], [lt], [gte], [lte], etc.):

    • Combine filters with logical operators like AND or OR.

  • Consistent Format: Ensure naming conventions are consistent:

    • Use camelCase (userName, minPrice) or snake_case (user_name, min_price) based on your API style guide.

  • Reserved Characters: Encode special characters properly to avoid parsing issues.

The simplest way to implement filtering is by using query parameters in the URL. Spring Boot makes it easy to handle query parameters with @RequestParam

2: Using Specifications (Dynamic Queries)

For more complex filtering, you can use Specifications with JPA Criteria API. This approach is flexible and allows you to build dynamic queries.

public class SearchCriteria {
    private String keyword;
    private String fieldName;
    private String operator; // e.g., EQUAL, GREATER_THAN
    private Object value;

    // Getters and Setters
}



public class GenericSpecification<T> implements Specification<T> {
    private SearchCriteria criteria;

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
        if (criteria.getOperator().equalsIgnoreCase("EQUAL")) {
            return builder.equal(root.get(criteria.getFieldName()), criteria.getValue());
        }
        if (criteria.getOperator().equalsIgnoreCase("GREATER_THAN")) {
            return builder.greaterThan(root.get(criteria.getFieldName()), criteria.getValue().toString());
        }
        // Add other conditions here
        return null;
    }
}

3. Combining Filtering and Searching

We can combine filtering and searching in a single endpoint to provide a powerful querying mechanism.

Ex. /products?category=electronics&price[gt]=100&brand=Samsung

Using spring data specification we can combine multiple specifications -

Specification spec = Specification.where(spec1).and(spec2).or(spec3);

4. Pagination and Sorting

We can utilize effective pagination and sorting based on our need to display properly ordered data in chunks.

  • Query Parameter for Sorting: Use a sort query parameter:

    /products?sort=price,desc
    • field: Field to sort by (e.g., price).

    • direction: Sorting direction (asc or desc).

  • Multiple Sort Fields: Support sorting by multiple fields:

    /products?sort=price,asc&sort=name,desc
    
  • Spring Data PagingAndSortingRepository: Use the Pageable interface:

    @GetMapping("/products")
    public Page<Product> getProducts(Pageable pageable) {
        return productRepository.findAll(pageable);
    }

    Example Pageable request:

    bashCopyEdit/products?page=0&size=10&sort=price,desc

Example -

  1. Simple -

    Filter record based on exact match

    1. GET /api/users?role=admin

  2. Rangle Filters

    Support range queries using operators like gt, lt, gte, lte.

    1. GET /api/products?price[gt]=100&price[lte]=500

  3. Multiple Filters

    1. GET /api/orders?status=delivered&date[gte]=2023-01-01&date[lte]=2023-12-31

  4. Full Text Search

    1. GET /api/books?search=Spring Boot

  5. Logical Operators

    1. GET /api/movies?filter=(genre=action AND rating[gte]=8) OR (director=Nolan)

  6. Sorting

    1. GET /api/products?sort=price,asc

    2. GET /api/products?sort=price,desc&sort=name,asc

  7. Pagination

    1. GET /api/users?page=1&size=10

  8. Filtering on Nested or Related Fields

    1. GET /api/orders?customer.name=John&product.category=electronics

    Retrieves orders where the customer’s name is John and the product belongs to the electronics category.

  9. Case-Insensitive Search

    1. GET /api/users?email=example@example.com

    Matches example@example.com, Example@example.com, etc.

  10. Include/Exclude Fields

    1. GET /api/users?fields=id,name,email

    Includes only the id, name, and email fields in the response.

  11. Reserved Characters Handling

    Ensure special characters in query parameters are encoded.

    1. /products?name=Spring%20Boot&description=advanced%2Fbeginner

    Retrieves products where the name is Spring Boot and the description contains advanced/beginner.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api")
public class ProductController {

    @Autowired
    private ProductService productService;

    // 1. Simple Equality Filters
    @GetMapping("/users")
    public List<User> getUsersByRole(@RequestParam(required = false) String role) {
        return productService.getUsersByRole(role);
    }

    // 2. Range Filters
    @GetMapping("/products")
    public List<Product> getProductsByPriceRange(
        @RequestParam(required = false) Double priceGt,
        @RequestParam(required = false) Double priceLte
    ) {
        return productService.getProductsByPriceRange(priceGt, priceLte);
    }

    // 3. Full-Text Search
    @GetMapping("/books")
    public List<Book> searchBooks(@RequestParam(required = false) String search) {
        return productService.searchBooks(search);
    }

    // 4. Logical Operators
    @GetMapping("/movies")
    public List<Movie> getMoviesWithFilters(
        @RequestParam(required = false) String filter
    ) {
        return productService.getMoviesWithFilters(filter);
    }

    // 5. Sorting
    @GetMapping("/products/sorted")
    public List<Product> getSortedProducts(
        @RequestParam(required = false, defaultValue = "id,asc") String[] sort
    ) {
     List<Sort.Order> orders = new ArrayList<>();
    
    if (sort[0].contains(",")) {
        // sort=[field,direction] format
        for (String sortOrder : sort) {
            String[] _sort = sortOrder.split(",");
            orders.add(new Sort.Order(
                getSortDirection(_sort[1]), 
                _sort[0]));
        }
    } else {
        // sort=[field] format
        orders.add(new Sort.Order(
            getSortDirection("asc"), 
            sort[0]));
    }
    
    Pageable pageable = PageRequest.of(page, size, Sort.by(orders));
    
    return ResponseEntity.ok(
        productService.findProducts(name, minPrice, maxPrice, 
                                  categories, pageable));
    }

    // 6. Pagination
    @GetMapping("/users/paginated")
    public Page<User> getUsersWithPagination(Pageable pageable) {
        return productService.getUsersWithPagination(pageable);
    }

    // 7. Nested/Related Field Filtering
    @GetMapping("/orders")
    public List<Order> getOrdersByCustomerAndCategory(
        @RequestParam(required = false) String customerName,
        @RequestParam(required = false) String productCategory
    ) {
        return productService.getOrdersByCustomerAndCategory(customerName, productCategory);
    }

    // 8. Case-Insensitive Search
    @GetMapping("/users/search")
    public List<User> getUsersByEmail(@RequestParam String email) {
        return productService.getUsersByEmail(email);
    }

    // 9. Include/Exclude Fields
    @GetMapping("/users/fields")
    public List<User> getUsersWithSelectedFields(@RequestParam List<String> fields) {
        return productService.getUsersWithSelectedFields(fields);
    }
}

Summary

  • Filtering: Use query parameters or Specifications to narrow down results.

  • Searching: Implement simple or advanced search using query parameters or Specifications.

  • Combining Filtering and Searching: Provide a unified endpoint for flexible querying.

  • Pagination and Sorting: Improve performance and usability by paginating and sorting results.

PreviousVersioning StrategiesNextSpring Boot Mail Integration

Last updated 4 months ago

For more details info see

Specification