Skip to content

EN SQL RPC and Idempotency

Cavanshirpro edited this page Sep 19, 2026 · 1 revision

SQL/RPC, transactions and idempotency

English · Türkçe · Languages / Diller

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

An operation exposes one or more predeclared SQL statements through a named HTTP action. Defaults are method POST, path rpc/<name>, database primary and transaction=true. At least one statement is required. Private operations need permission; public operations require explicit public=true.

Statement model

Field Meaning
sql Fixed SQL written by a trusted operator
params Bind parameter → value source
mode execute, fetch_one, fetch_all, scalar
result_name Name of the statement result in the operation output
max_rows Bounded result size; default 1000
require_rowcount_min/max Reject an unexpected number of affected rows

Value sources include $body.*, $path.*, $query.*, $header.*, $param.*, $principal.* and, where appropriate, $request.id. $param refers to already-validated request parameters. Bind user values; do not concatenate them into SQL or turn arbitrary body values into table/column identifiers.

Atomicity

If a statement fails within a transaction, earlier steps in that database transaction roll back. A transfer can combine a conditional debit, credit and ledger insertion. If insufficient funds cause the debit to affect zero rows, a rowcount guard can reject the operation.

transaction=false is restricted to explicit read-only SELECT/SHOW/EXPLAIN work and cannot contain execute statements. allow_ddl is a separate opt-in setting. These controls reduce configuration mistakes; they do not provide a sandbox for hostile SQL.

Idempotency contract

With idempotency=true:

  • transaction=true is required.
  • The default Idempotency-Key header is required and bounded to 1–200 characters.
  • Identity combines project, operation, principal kind/subject and key.
  • A fingerprint includes body, path/query, validated parameters, tenant and referenced headers.
  • The same identity and inputs replay the stored successful result.
  • Reusing a key with different inputs returns 409 conflict.
  • The ledger record, SQL side effects and result commit together in the business database.

If the network response is lost after execution, retry the same command with the same key and payload. A new key can create a second side effect while the first result is uncertain. Allocate a key per user action/business command and retain it throughout the permitted retry window.

Replays expose X-Forge-Idempotent-Replay: true and, in object responses, _idempotent_replay: true. They do not repeat cache invalidation or background hooks. Default TTL is 86400 seconds; after expiry, the same key can be treated as a new operation.

Interaction with caching

Operation response caching is limited to GET and read-only statements. An idempotent operation cannot enable response caching simultaneously. Principal-dependent SQL cannot use vary_by_principal=false. Binding $request.id in cached/idempotent operations is rejected.

After mutations, invalidate_resources uses resource paths and invalidate_operations uses operation names. Invalidation occurs on the first successful execution, not again on replay.

Boundaries

Idempotency does not add email, Discord requests, another database or an external payment service to the SQL transaction. External effects need an outbox/inbox or the provider's own idempotency mechanism. Failover endpoints that do not share the ledger and business data can each execute the same command independently.

Start with your first API, then inspect exampleApps/app/GuildLedger. All fields are in the operation reference.

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