FreeGridv1.6.0한국어

Server examples (Java, Node, Python, PHP, C#, Go)

Server helpers in six languages turn the grid’s requests into safe SQL: paging, sorting, filters, quick search, the advanced filter, server-side grouping and saving changes.

The common "search, edit, save" screen needs two functions: loadData posts the search form as JSON and fills the grid, and saveChanges posts { created, updated, deleted } and clears the change marks when the server accepts them. For very large lists, restDatasource lets the server sort, filter, page and group. With Spring Security, csrfHeaders reads the _csrf meta tags.

import { createGrid, loadData, saveChanges, restDatasource, csrfHeaders } from '@freegridjs/core';

// search → edit → save (eGovFrame / Spring MVC screens)
await loadData(grid, '/order/selectOrders.do', { headers: csrfHeaders, params: () => ({ status: form.status.value }) });
await saveChanges(grid, '/order/saveOrders.do', { headers: csrfHeaders });   // { created, updated, deleted }

// very large lists: the server sorts, filters, pages and groups
createGrid(el, { columns, datasource: restDatasource('/api/orders/rows'), pagination: { pageSize: 50 }, groupBy: ['status'] });

On the server, download four Java classes (GridRequest, GridQuery, GridRows, GridSave) and add them to your project. They are plain Java 8, so they run on Spring Boot and eGovFrame 3.x to 5.x, with JdbcTemplate or MyBatis. GridQuery only turns allowed columns into SQL and binds every value, so request data never becomes SQL text. A request with a bad number or date throws IllegalArgumentException; answer it with 400. Tested on Spring Boot and in the official eGovFrame 5.0.1 web sample (MyBatis, HSQL).

Download the Java server helpers (freegrid-java-server.zip)

// Spring Boot + JdbcTemplate.  restDatasource('/api/orders/rows'), saveChanges(grid, '/api/orders/save')
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    /** Grid column id → SQL column. Only these can be sorted, filtered or grouped. */
    private static final GridQuery QUERY = new GridQuery()
        .column("orderNo", "ORDER_NO", GridQuery.Type.TEXT)
        .column("orderDate", "ORDER_DATE", GridQuery.Type.DATE)
        .column("status", "STATUS", GridQuery.Type.TEXT)
        .column("qty", "QTY", GridQuery.Type.NUMBER)
        .defaultOrderBy("ID");
    // .dialect(GridQuery.Dialect.LIMIT) for MySQL, MariaDB, PostgreSQL (LIMIT ? OFFSET ?)

    private final JdbcTemplate jdbc;
    public OrderController(JdbcTemplate jdbc) { this.jdbc = jdbc; }

    @PostMapping("/rows")
    public Map<String, Object> rows(@RequestBody Map<String, Object> body) {
        GridQuery.Sql sql = QUERY.build(GridRequest.from(body));
        Long total = jdbc.queryForObject("SELECT COUNT(*) FROM ORDERS" + sql.where, Long.class, sql.countArgs());
        List<Map<String, Object>> rows = jdbc.queryForList(
            "SELECT ID AS \"id\", ORDER_NO AS \"orderNo\", ORDER_DATE AS \"orderDate\", STATUS AS \"status\", QTY AS \"qty\" FROM ORDERS"
                + sql.where + sql.orderBy + sql.page, sql.pageArgs());
        return GridRows.of(rows, total);              // { rows, total }, dates as "YYYY-MM-DD"
    }

    @PostMapping("/save")
    @Transactional
    public Map<String, Object> save(@RequestBody Map<String, Object> body) {
        GridSave save = GridSave.from(body);          // created / updated / deleted
        for (Map<String, Object> r : save.getDeleted()) jdbc.update("DELETE FROM ORDERS WHERE ID = ?", r.get("id"));
        for (Map<String, Object> r : save.getUpdated())
            jdbc.update("UPDATE ORDERS SET STATUS = ?, QTY = ? WHERE ID = ?", r.get("status"), r.get("qty"), r.get("id"));
        for (Map<String, Object> r : save.getCreated())
            jdbc.update("INSERT INTO ORDERS (ORDER_NO, ORDER_DATE, STATUS, QTY) VALUES (?, ?, ?, ?)",
                r.get("orderNo"), GridSave.date(r.get("orderDate")), r.get("status"), r.get("qty"));
        return Collections.singletonMap("ok", true);
    }

    /** Bad numbers, dates or filter ops answer 400 instead of 500. */
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Map<String, Object>> bad(IllegalArgumentException e) {
        return ResponseEntity.badRequest().body(Collections.singletonMap("message", e.getMessage()));
    }
}

Node.js, Python, PHP, C# and Go

For other back ends there is a helper per language that reads the same requests. Each is one file (a package for Go) without dependencies and follows the same rules as the Java helpers: only the columns you list can be sorted, filtered or grouped, every value is a bound parameter, and % and _ in LIKE searches are escaped. A bad number, date or operator answers 400 { message }. Filtering follows the same rules as the grid on screen: reversed between bounds give the same rows, the quick filter finds rows containing every word, and dates compare by day even on date-time columns. POST JSON and method: 'GET' requests are both read (the Java helpers too). Quick search, the advanced filter, server-side grouping and saving are all handled, and the examples folder in the repository has a project for each that runs with 1,000 sample rows.

import { createQuery, readRequest, readSave, toRows } from './freegrid-query.js';

const QUERY = createQuery({
  columns: {                                   // grid column id → SQL column: only these can be sorted / filtered
    orderNo: { sql: 'ORDER_NO', type: 'text' },
    orderDate: { sql: 'ORDER_DATE', type: 'date' },
    qty: { sql: 'QTY', type: 'number' },
  },
  dialect: 'limit',                            // SQL Server / Oracle: 'standard'; pg: placeholder: 'dollar'
  defaultOrderBy: 'ID',
});

app.post('/api/orders/rows', (req, res) => {
  const sql = QUERY.build(readRequest(req.body));
  const total = db.prepare('SELECT COUNT(*) AS n FROM ORDERS' + sql.where).get(sql.countArgs).n;
  const rows = db.prepare(SELECT + sql.where + sql.orderBy + sql.page).all(sql.args);
  res.json(toRows(rows, total));               // { rows, total }, dates as "YYYY-MM-DD"
});

app.post('/api/orders/save', (req, res) => {
  const { created, updated, deleted } = readSave(req.body);
  // INSERT / UPDATE / DELETE in one transaction
});

The ids come from the browser: before an UPDATE or DELETE, check that the signed-in user may change that row (for example WHERE ID = ? AND OWNER_ID = ?).

Questions about this page

Which back ends work with FreeGrid?

Any back end that answers JSON. Ready-made helpers turn the grid’s requests into safe SQL for Java (Spring Boot, eGovFrame), Node.js, Python, PHP, C# (.NET) and Go.

Is the server-side filtering safe from SQL injection?

Yes. Only the columns you list can be sorted or filtered, every value is a bound parameter, and LIKE searches escape % and _.