AG-Grid is a powerful JavaScript data grid library, ideal for building dynamic, high-performance tables with features like sorting, filtering, and pagination. In this article, we’ll create an API in Go to support AG-Grid, enabling efficient server-side data operations, including filtering, sorting, and pagination. By integrating AG-Grid with Go API, we’ll develop a robust solution that ensures smooth performance, even when working with large datasets.
Prerequisites
- Go 1.21
- MySQL
Setup project
Setting up the Go project dependencies.
go mod init app
go get github.com/gin-gonic/gin
go get gorm.io/gorm
go get gorm.io/driver/mysql
go get github.com/joho/godotenv
Create a testing database named "example" and run the database.sql file to import the table and data.
Project structure
├─ .env
├─ main.go
├─ config
│ └─ db.go
├─ controllers
│ └─ product_controller.go
├─ models
│ └─ product.go
├─ public
│ └─ index.html
└─ router
└─ router.go
Project files
.env
This file contains the database connection information.
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=example
DB_USER=root
DB_PASSWORD=
db.go
This file sets up the database connection using GORM. It declares a global variable DB to hold the database connection instance to use later in our application.
package config
import (
"fmt"
"os"
"github.com/joho/godotenv"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
var DB *gorm.DB
func SetupDatabase() {
godotenv.Load()
connection := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true", os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_DATABASE"))
db, _ := gorm.Open(mysql.Open(connection), &gorm.Config{NamingStrategy: schema.NamingStrategy{SingularTable: true}})
DB = db
}
router.go
This file sets up routing for a Gin web application. It initializes a router for a DataTables API and serves a static index.html file at the root URL.
package router
import (
"app/controllers"
"github.com/gin-gonic/gin"
)
func SetupRouter() {
productController := controllers.ProductController{}
router := gin.Default()
router.StaticFile("/", "./public/index.html")
router.GET("/api/products", productController.Index)
router.Run()
}
product.go
This file defines the Product model for the application.
package models
type Product struct {
Id int
Name string
Price float64
}
product_controller.go
This file defines a function to handle incoming requests and return the DataTables data.
package controllers
import (
"app/config"
"app/models"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
type ProductController struct {
}
func (con *ProductController) Index(c *gin.Context) {
size, _ := strconv.Atoi(c.DefaultQuery("length", "10"))
start, _ := strconv.Atoi(c.Query("start"))
order := "id"
if c.Query("order[0][column]") != "" {
order = c.Query("columns[" + c.Query("order[0][column]") + "][data]")
}
direction := c.DefaultQuery("order[0][dir]", "asc")
var products []models.Product
query := config.DB.Model(&products)
var recordsTotal, recordsFiltered int64
query.Count(&recordsTotal)
search := c.Query("search[value]")
if search != "" {
search = "%" + search + "%"
query.Where("name like ?", search)
}
query.Count(&recordsFiltered)
query.Order(order + " " + direction).
Offset(start).
Limit(size).
Find(&products)
c.JSON(http.StatusOK, gin.H{"draw": c.Query("draw"), "recordsTotal": recordsTotal, "recordsFiltered": recordsFiltered, "data": products})
}
The product_controller.go
file defines a controller for managing product-related API requests in a Go application using the Gin framework. It features an Index
method that retrieves a paginated list of products based on query parameters for pagination, sorting, and searching. The method extracts parameters for pagination, constructs a query to fetch products from the database, and applies filtering if a search term is provided. After counting the total matching products, it orders and limits the results before returning a JSON response containing the product data and total count, facilitating integration with frontend applications.
main.go
This file is the main entry point of our application. It will create and setting up the Gin web application.
package main
import (
"app/config"
"app/router"
)
func main() {
config.SetupDatabase()
router.SetupRouter()
}
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
go run main.go
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 effectively integrated AG-Grid with a Go API to create a robust and efficient data grid solution. By utilizing Go's backend capabilities, we enabled AG-Grid to handle server-side filtering, sorting, and pagination, ensuring smooth performance even with large datasets. This integration not only optimizes data management but also enhances the user experience with dynamic, responsive tables on the frontend. With AG-Grid and Go working in harmony, we’ve built a scalable and high-performance grid system that is well-suited for real-world applications.
Source code: https://github.com/stackpuz/Example-AG-Grid-Go
Create a CRUD Web App in Minutes: https://stackpuz.com
Top comments (0)