AG-Grid with Spring Boot API

Create an API for AG-Grid with Spring Boot

AG-Grid is a robust JavaScript data grid library, perfect for creating dynamic, high-performance tables with advanced features like sorting, filtering, and pagination. In this article, we’ll build an API in Spring Boot to support AG-Grid, enabling efficient server-side data handling, including filtering, sorting, and pagination. By integrating AG-Grid with Spring Boot, we’ll create a seamless solution that ensures smooth performance, even when working with large datasets.

Prerequisites

  • JAVA 17
  • Maven
  • MySQL

Setup project

Create a testing database named "example" and run the database.sql file to import the table and data.

Project structure

├─ pom.xml
└─ src
   └─ main
      ├─ java
      │  └─ com
      │     └─ stackpuz
      │        └─ example
      │           ├─ App.java
      │           ├─ controller
      │           │  └─ ProductController.java
      │           ├─ dto
      │           │  └─ AgGrid.java
      │           ├─ entity
      │           │  └─ Product.java
      │           └─ repository
      │              └─ ProductRepository.java
      └─ resources
         ├─ application.properties
         └─ static
            └─ index.html

Project files

pom.xml

This file contains the configuration and dependencies of the Maven project.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.stackpuz</groupId>
	<artifactId>example-aggrid</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>example-aggrid</name>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.10</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-devtools</artifactId>
	    </dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.30</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
	 </dependencies>
</project>

application.properties

This file contains the database configuration.

spring.datasource.url= jdbc:mysql://localhost/example
spring.datasource.username = root
spring.datasource.password = 
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQLDialect

App.java

This file is the main entry point for the Spring Boot application.

package com.stackpuz.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

ProductRepository.java

This file defines the product repository by utilizing the JpaRepository class, which has the basic CRUD operations and the pagination feature, so we can use it to implement the pagination with less effort.

package com.stackpuz.example.repository;

import com.stackpuz.example.entity.Product;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Integer> {

    Page<Product> findByNameContains(Pageable pageable, String name);

}

findByNameContains is described by their name, this method is used to get the product paginated data filter by its name.

Product.java

This file defines the product entity that maps to our database table named "Product".

package com.stackpuz.example.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Product {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private int id;
    private String name;
    private BigDecimal price;
}

We use the lombok library features to reduce the amount of code written for our entity by using @Getter @Setter @NoArgsConstructor annotations.

AgGrid.java

This file defines the DTO (Data Transfer Object) for AG-Grid.

package com.stackpuz.example.dto;

import java.util.List;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class AgGrid<T> {
    private long count;
    private List<T> data;

    public AgGrid(long count, List<T> data) {
        this.count = count;
        this.data = data;
    }
}

AgGrid<T> meaning this is the Generic Class that can be use for any type of the Entity, to return AG-Grid data to the client.

ProductController.java

package com.stackpuz.example.controller;

import java.util.Optional;

import com.stackpuz.example.entity.Product;
import com.stackpuz.example.repository.ProductRepository;
import com.stackpuz.example.dto.AgGrid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.web.bind.annotation.*;

@RestController
public class ProductController {

    @Autowired
    private ProductRepository repository;

    @GetMapping("/api/products")
    public AgGrid<Product> getProducts(@RequestParam("page") Optional<Integer> pageParam, @RequestParam("size") Optional<Integer> sizeParam, @RequestParam("order") Optional<String> orderParam, @RequestParam("direction") Optional<String> directionParam, @RequestParam("search") Optional<String> searchParam) {
        int page = pageParam.orElse(1) - 1;
        int size = sizeParam.orElse(10);
        String order = orderParam.orElse("id");
        String direction = directionParam.orElse("asc");
        String search = searchParam.orElse("");
        PageRequest pageRequest = PageRequest.of(page, size, Sort.by(Direction.fromString(direction), order));
        Page<Product> pageProduct = (search.isEmpty() ? repository.findAll(pageRequest) : repository.findByNameContains(pageRequest, search));
        return new AgGrid<Product>(pageProduct.getTotalElements(), pageProduct.getContent());
    }
}

The ProductController.java file defines a Spring Boot controller responsible for handling API requests related to products. It features a single endpoint (/api/products) that retrieves a paginated list of products based on optional query parameters for pagination, sorting, and searching. The controller uses the ProductRepository to fetch product data, applying the specified sorting order and direction, and supports searching by product name. The response is structured as an AgGrid object, containing the total number of products and the current page's product data, facilitating integration with frontend data grid components.

index.html

<!DOCTYPE html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/ag-grid-community/dist/ag-grid-community.min.js"></script>
</head>
<body>
    <div id="grid" class="ag-theme-alpine" style="height: 400px; width: 640px; margin: 1em"></div>
    <script>
        function getQuery(params) {
            let query = new URLSearchParams()
            let size = params.endRow - params.startRow
            let page = Math.floor(params.startRow / size) + 1
            query.append('page', page)
            query.append('size', size)
            if (params.sortModel.length) {
                let sort = params.sortModel[0]
                query.append('order', sort.colId)
                query.append('direction', sort.sort)
            }
            if (params.filterModel.name) {
                query.append('search', params.filterModel.name.filter)
            }
            return query.toString()
        }
        let columns = [
            { headerName: 'ID', field: 'id', sortable: true },
            {
                headerName: 'Name', field: 'name', sortable: true, filter: true,
                filterParams: {
                    filterOptions: ['contains'],
                    maxNumConditions: 1,
                }
            },
            { headerName: 'Price', field: 'price', sortable: true }
        ]
        let gridOptions = {
            columnDefs: columns,
            rowModelType: 'infinite',
            pagination: true,
            paginationPageSize: 20,
            cacheBlockSize: 20,
            datasource: {
                getRows: function (params) {
                    let query = getQuery(params)
                    fetch(`/api/products?${query}`)
                        .then(response => response.json())
                        .then(json => {
                            params.successCallback(json.data, json.count)
                        })
                }
            }
        }
        document.addEventListener('DOMContentLoaded', () => {
            agGrid.createGrid(document.querySelector('#grid'), gridOptions)
        })
    </script>
</body>
</html>

The index.html file sets up a web page that uses the AG-Grid library to display a dynamic data grid for products. It includes a grid styled with the AG-Grid theme and a JavaScript section that constructs query parameters for pagination, sorting, and filtering. The grid is configured with columns for ID, Name, and Price, and it fetches product data from an API endpoint based on user interactions. Upon loading, the grid is initialized, allowing users to view and manipulate the product list effectively.

Run project

mvn spring-boot:run

Open the web browser and goto http://localhost:8080
You will find this test page.

test page

Testing

Page size test

Change page size by selecting 50 from the "Page Size" drop-down. You will get 50 records per page, and the last page will change from 5 to 2.

page size test

Sorting test

Click on the header of the first column. You will see that the id column will be sorted in descending order.

sorting test

Search test

Enter "no" in the search text-box of the "Name" column, and you will see the filtered result data.

search test

Conclusion

In conclusion, we’ve successfully implemented AG-Grid with Spring Boot to create a powerful, efficient data grid solution. By utilizing Spring Boot's backend capabilities, we enabled AG-Grid to perform server-side filtering, sorting, and pagination, providing smooth and optimized handling of large datasets. This integration not only streamlines backend data management but also enhances the frontend experience with dynamic, responsive tables. With AG-Grid and Spring Boot working in tandem, we’ve built a scalable, high-performance grid system ready for real-world applications.

Source code: https://github.com/stackpuz/Example-AG-Grid-Spring-Boot-3

Create a CRUD Web App in Minutes: https://stackpuz.com

Related post