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
| Endpoint | Method | Auto-enabled When |
|---|---|---|
/{resource}/batch | POST | crud.create exists |
/{resource}/batch | PATCH | crud.update exists |
/{resource}/batch | DELETE | crud.delete exists |
/{resource}/batch | PUT | crud.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}/batchRequest
{
"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}/batchEvery 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}/batchRequest
{
"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}/batchInserts 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:
- Authentication — Required for all batch endpoints (401 if missing)
- Firewall — Applied per-record for update/delete/upsert (not applicable to create)
- Access — Role-based check using the batch operation's access config
- Guards — Per-record validation (createable/updatable/immutable fields)
- Masking — Applied to all records in the success array before response
See Also
- CRUD Endpoints — Single-record operations
- Error Responses — Error format reference
- Guards — Field-level write protection