Skip to content

EN CRUD and Queries

Cavanshirpro edited this page Sep 19, 2026 · 1 revision

SQL CRUD, filters, pagination and batches

English · Türkçe · Languages / Diller

Scope: JSON API Forge 0.5.1 · Source review: 17 September 2026 · Language: English

A SQL resource maps a table to its permitted HTTP operations. A useful small resource looks like this:

{
  "resources": [
    {
      "database": "primary",
      "table": "notes",
      "path": "notes",
      "auto_create": true,
      "columns": {
        "id": {"type": "integer", "primary_key": true, "nullable": false},
        "title": {"type": "string", "nullable": false, "max_length": 160},
        "status": {"type": "string", "default": "draft", "index": true}
      },
      "writable_fields": ["title", "status"],
      "allowed_filters": ["status"],
      "filter_operators": ["eq", "ne", "in"],
      "search_fields": ["title"],
      "allowed_sort": ["id", "title"],
      "permissions": {
        "list": "notes.list", "read": "notes.read", "create": "notes.create",
        "update": "notes.update", "delete": "notes.delete"
      }
    }
  ]
}

Generated routes

Let P be the project API prefix. Route availability depends on enabled, allowed_actions and batch/count settings.

Method/path Behavior
GET P/notes Bounded list
POST P/notes Create a row
GET P/notes/{item_id} Read one row
PATCH P/notes/{item_id} Update supplied fields
PUT P/notes/{item_id} Replace the writable representation while preserving identity/policy fields
DELETE P/notes/{item_id} Delete or configured soft-delete
GET P/notes/_count Count under the same visibility/filter policy

Get the actual route set from forge routes and the project's /_openapi.json. A static Wiki table is not a universal contract for every application.

Field policies

readable_fields is the read allowlist; hidden_fields excludes fields; writable_fields controls client writes. Prefer explicit allowlists. create_schema and update_schema add JSON Schema validation. A database column's existence does not grant client write access.

Primary-key, tenant, owner and soft-delete fields are server-controlled. Keep them out of client write lists. See row authorization for owner_actions and tenant_field.

Filtering and search

GET P/notes?status=draft&limit=20&offset=0&sort=-id
GET P/notes?status__in=draft,published&q=api

SQL operators are eq, ne, gt, gte, lt, lte, in, like, ilike and isnull. Both the field and operator must be allowed. q searches only search_fields. sort=-field requests descending order.

v0.5.1 behavior: the SQL filter builder silently skips unknown or disallowed filters. Check that a typo has not broadened your results. Filters are not authorization; tenant/owner constraints are applied separately.

Offset responses contain items, limit and offset; no automatic total is promised. Use _count when a total is needed, accounting for its cost on large tables.

Cursor pagination

pagination_mode: "cursor" uses cursor_field and a primary-key tie-breaker for stable keyset pagination. Responses contain next_cursor and has_more. Treat cursors as opaque and return them unchanged. Cursor mode rejects offset and sort; offset mode rejects cursor. Index the chosen cursor field and primary key appropriately.

The Python SDK's synchronous iter_items helper uses offsets. For cursor resources, follow next_cursor in your own bounded loop.

Batches and deletion

batch_enabled defaults to false; max_batch_size defaults to 100. Follow each batch endpoint's actual OpenAPI body contract. Do not assume every API called a batch has identical all-or-nothing behavior. Soft-delete visibility applies to list/read/count; retaining physical rows also requires an explicit retention and cleanup policy.

Sources and verification

This page describes the behavior at the pinned commits below. Recheck these contracts when upgrading.


English home · All English pages · Version maintenance

Clone this wiki locally