Rows
Read a page of rows from one table, optionally sorted, searched, and filtered. The response bundles the column metadata together with the page so a grid can render without a second request.
Request
GET /api/tables/{table}/rows
Path parameters
Query parameters
| Name | Type | Default | Notes |
page |
integer ≥ 1 |
1 |
1-indexed page number. Values below 1 are clamped to 1. |
page_size |
integer |
50 |
Rows per page. Clamped to the range [1, 1000]. |
sort_column |
string |
— |
A real column name from GET /api/tables/{table}/columns. Validated against the live schema; unknown or unsafe names return 400. |
sort_direction |
asc or desc |
asc |
Only desc / DESC selects descending order; anything else (including omitted) sorts ascending. Has no effect unless sort_column is also set. |
search |
string |
— |
Case-insensitive substring match (ILIKE '%value%') applied across every text-like column — text, character varying, character, and uuid. Non-text columns are not included in the search. |
filter.<column> |
string (repeatable) |
— |
Per-column filter. Any query parameter whose name begins with filter. is treated as a filter on the column named after the dot. Multiple filters are AND-ed. See "Filter semantics" below. |
Filter semantics
For most columns the filter runs as "column"::text ILIKE '%value%' — a case-insensitive substring match after casting to text. For boolean columns the filter is an exact match: values yes, true, t, 1 match TRUE; no, false, f, 0 match FALSE; anything else matches no rows.
Response
200 OK. A JSON object describing the requested page.
| Field | Type | Notes |
columns | array | The same shape as GET /api/tables/{table}/columns. Included so the client can render headers and NULL cells without a second round-trip. |
rows | array of objects | One object per row, keyed by real column name. Values are JSON-typed according to the source SQL type — see "Value encoding" below. |
total_rows | integer | Total number of rows matching the search and filters across the whole table, not just the returned page. Use this for pagination controls. |
page | integer | The page that was returned (after clamping). |
page_size | integer | The page size that was used (after clamping). |
Value encoding
| SQL type | JSON representation |
smallint, integer | JSON number. |
bigint | JSON number when the absolute value fits in 253, otherwise a JSON string to preserve precision in JavaScript clients. |
real, double precision | JSON number. |
numeric | JSON string — arbitrary-precision decimals are never widened to a float. |
boolean | JSON boolean. |
json, jsonb | The parsed JSON value, inlined. |
timestamp without time zone | String, formatted "YYYY-MM-DD HH:MM:SS". |
timestamp with time zone | RFC 3339 string (UTC). |
date | String, formatted "YYYY-MM-DD". |
time with/without time zone | String, formatted "HH:MM:SS". |
uuid | String. |
| Other | String — the value as PostgreSQL would render it via ::text. |
SQL NULL | JSON null. |
Example
curl "http://127.0.0.1:3141/api/tables/vehicles_log/rows\
?page=1\
&page_size=2\
&sort_column=recorded_at\
&sort_direction=desc\
&search=ADT\
&filter.vehicle_id=ADT3\
&filter.is_active=yes"
{
"columns": [
{ "name": "id", "display_name": "Id", "data_type": "bigint",
"display_type": "Number", "is_nullable": false, "is_primary_key": true },
{ "name": "vehicle_id", "display_name": "Vehicle", "data_type": "character varying",
"display_type": "Text", "is_nullable": false, "is_primary_key": false },
{ "name": "posn_lat", "display_name": "Latitude", "data_type": "double precision",
"display_type": "Decimal", "is_nullable": true, "is_primary_key": false },
{ "name": "is_active", "display_name": "Active", "data_type": "boolean",
"display_type": "Yes/No", "is_nullable": false, "is_primary_key": false },
{ "name": "recorded_at", "display_name": "Recorded At",
"data_type": "timestamp with time zone", "display_type": "Date & Time",
"is_nullable": false, "is_primary_key": false }
],
"rows": [
{
"id": 523401,
"vehicle_id": "ADT3",
"posn_lat": 52.41933,
"is_active": true,
"recorded_at": "2026-04-12T17:42:05+00:00"
},
{
"id": 523397,
"vehicle_id": "ADT3",
"posn_lat": null,
"is_active": true,
"recorded_at": "2026-04-12T17:41:55+00:00"
}
],
"total_rows": 18422,
"page": 1,
"page_size": 2
}
Errors
| Status | When |
400 | sort_column or a filter.<name> references an unknown column, or the name contains characters other than letters, digits, underscore, hyphen, or space. Body: {"error": "Unknown sort column: X"} or {"error": "Invalid filter column name: X"}. |
404 | The table is not in the allow-list or does not exist. |
503 | The server is still in setup mode. |
500 | The query failed. See the server log for detail. |
Related