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.
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.
Sorting test
Click on the header of the first column. You will see that the id column will be sorted in descending order.
Search test
Enter "no" in the search text-box of the "Name" column, and you will see the filtered result data.
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