Quickback Docs

Batch Operations

Bulk create, update, upsert, and delete records with optional atomic transactions.

Batch operations let you create, update, or delete multiple records in a single request. They are auto-enabled when the corresponding CRUD operation exists in your definition.

Available Endpoints

EndpointMethodAuto-enabled When
/{resource}/batchPOSTcrud.create exists
/{resource}/batchPATCHcrud.update exists
/{resource}/batchDELETEcrud.delete exists
/{resource}/batchPUTcrud.put exists

To disable a batch operation, set it to false in your definition:

crud: {
  create: { access: { roles: ['admin'] } },
  batchCreate: false,  // Disable batch create
}

Batch Create

POST /api/v1/{resource}/batch

Request

{
  "records": [
    { "name": "Room A", "capacity": 10 },
    { "name": "Room B", "capacity": 20 },
    { "name": "Room C", "capacity": 30 }
  ],
  "options": {
    "atomic": false
  }
}

Response (201 or 207)

{
  "success": [
    { "id": "rm_abc1", "name": "Room A", "capacity": 10, "createdAt": "2025-01-15T10:00:00Z" },
    { "id": "rm_abc3", "name": "Room C", "capacity": 30, "createdAt": "2025-01-15T10:00:00Z" }
  ],
  "errors": [
    {
      "index": 1,
      "record": { "name": "Room B", "capacity": 20 },
      "error": {
        "error": "Database insert failed",
        "layer": "validation",
        "code": "INSERT_FAILED",
        "details": { "reason": "UNIQUE constraint failed: rooms.name" }
      }
    }
  ],
  "meta": {
    "total": 3,
    "succeeded": 2,
    "failed": 1,
    "atomic": false
  }
}
  • 201 — All records created successfully
  • 207 — Partial success (some errors, some successes)

Fields applied automatically to each record:

  • ID generation (UUID, prefixed, etc.)
  • Ownership fields (organizationId, ownerId, createdBy)
  • Audit fields (createdAt, modifiedAt)
  • Default values and computed fields

Batch Update

PATCH /api/v1/{resource}/batch

Every record must include an id field.

Request

{
  "records": [
    { "id": "rm_abc1", "capacity": 15 },
    { "id": "rm_abc2", "name": "Updated Room" },
    { "id": "rm_xyz9", "capacity": 50 }
  ],
  "options": {
    "atomic": false
  }
}

Response (200 or 207)

{
  "success": [
    { "id": "rm_abc1", "name": "Room A", "capacity": 15, "modifiedAt": "2025-01-15T11:00:00Z" },
    { "id": "rm_abc2", "name": "Updated Room", "capacity": 20, "modifiedAt": "2025-01-15T11:00:00Z" }
  ],
  "errors": [
    {
      "index": 2,
      "record": { "id": "rm_xyz9", "capacity": 50 },
      "error": {
        "error": "Not found",
        "layer": "firewall",
        "code": "NOT_FOUND",
        "details": { "id": "rm_xyz9" }
      }
    }
  ],
  "meta": {
    "total": 3,
    "succeeded": 2,
    "failed": 1,
    "atomic": false
  }
}

Records that don't exist or aren't accessible through the firewall return a NOT_FOUND error. Guard rules (immutable, protected, not-updatable fields) are checked per record.

Missing IDs

If any records are missing the id field, the entire request is rejected:

{
  "error": "Records missing required ID field",
  "layer": "validation",
  "code": "BATCH_MISSING_IDS",
  "details": { "indices": [0, 2] },
  "hint": "All records must include an ID field for batch update."
}

Batch Delete

DELETE /api/v1/{resource}/batch

Request

{
  "ids": ["rm_abc1", "rm_abc2", "rm_abc3"],
  "options": {
    "atomic": false
  }
}

Note: Batch delete uses an ids array (not records).

Response (200 or 207)

Soft delete (default):

{
  "success": [
    { "id": "rm_abc1", "name": "Room A", "deletedAt": "2025-01-15T12:00:00Z" },
    { "id": "rm_abc2", "name": "Room B", "deletedAt": "2025-01-15T12:00:00Z" }
  ],
  "errors": [],
  "meta": {
    "total": 2,
    "succeeded": 2,
    "failed": 0,
    "atomic": false
  }
}

Hard delete: Returns objects with only the id field (record data is deleted).

Batch Upsert

PUT /api/v1/{resource}/batch

Inserts new records or updates existing ones. Every record must include an id field.

Note: Batch upsert is only available when generateId is set to false (user-provided IDs) in your configuration.

Request

{
  "records": [
    { "id": "rm_001", "name": "Room A", "capacity": 10 },
    { "id": "rm_002", "name": "Updated Room B", "capacity": 25 }
  ],
  "options": {
    "atomic": false
  }
}

Response (201 or 207)

The compiler checks which IDs already exist and splits the batch into creates and updates. Create records get ownership and default fields; update records get only modifiedAt.

Atomic Mode

By default, batch operations use partial success mode — each record is processed independently, and failures don't affect other records.

Set "atomic": true to require all-or-nothing processing:

{
  "records": [...],
  "options": {
    "atomic": true
  }
}

Atomic Failure Response (400)

If any record fails in atomic mode, the entire batch is rejected:

{
  "error": "Batch operation failed in atomic mode",
  "layer": "validation",
  "code": "BATCH_ATOMIC_FAILED",
  "details": {
    "failedAt": 1,
    "reason": "Database insert failed",
    "errorDetails": { "reason": "UNIQUE constraint failed" }
  },
  "hint": "Transaction rolled back. Fix the error and retry the entire batch."
}

Batch Size Limit

The default maximum batch size is 100 records. Requests exceeding the limit are rejected:

{
  "error": "Batch size limit exceeded",
  "layer": "validation",
  "code": "BATCH_SIZE_EXCEEDED",
  "details": { "max": 100, "actual": 250 },
  "hint": "Maximum 100 records allowed per batch. Split into multiple requests."
}

Configuration

Batch operations inherit access control from their corresponding CRUD operation. You can override per-batch settings:

export default defineTable(rooms, {
  crud: {
    create: { access: { roles: ['admin', 'member'] } },
    update: { access: { roles: ['admin', 'member'] } },
    delete: { access: { roles: ['admin'] }, mode: 'soft' },

    // Override batch-specific settings
    batchCreate: {
      access: { roles: ['admin'] },    // More restrictive than single create
      maxBatchSize: 50,                 // Default: 100
      allowAtomic: true,               // Default: true
    },
    batchUpdate: {
      maxBatchSize: 100,
      allowAtomic: true,
    },
    batchDelete: {
      access: { roles: ['admin'] },
      maxBatchSize: 100,
      allowAtomic: true,
    },

    // Disable a batch operation entirely
    batchUpsert: false,
  },
});

Security

All security pillars apply to batch operations:

  1. Authentication — Required for all batch endpoints (401 if missing)
  2. Firewall — Applied per-record for update/delete/upsert (not applicable to create)
  3. Access — Role-based check using the batch operation's access config
  4. Guards — Per-record validation (createable/updatable/immutable fields)
  5. Masking — Applied to all records in the success array before response

See Also

On this page