Run bulk operations
Bulk SDK and REST requests reduce round trips when several records need the same kind of operation. Refine creates each record in a separate request and uses a single request for ID-based bulk updates and deletes. Use a bounded batch, keep a stable identifier on every record, and retain the original input so a failed request can be inspected or retried.
The examples use the tasks table from the
quickstart.
Generate firstTaskId and secondTaskId once in application code and reuse
them through the sequence. The REST create example does the same with
FIRST_TASK_ID and SECOND_TASK_ID in the current shell.
For Refine, register the provider and sign in.
Import useCreateMany, useUpdateMany, and useDeleteMany from
@refinedev/core. Place the hooks at the top level of a component beneath
<Refine> and invoke mutations from event handlers. Review and confirm the
IDs before invoking deletion.
Create several records#
Send an array to the same data resource used to create one record.
- JavaScript SDK
- Python SDK
- Refine
- REST API
await database
.from('tasks')
.create([
{id: firstTaskId, title: 'Prepare release notes', done: false},
{id: secondTaskId, title: 'Publish release notes', done: false},
])
.execute();
client.database.create(
"tasks",
[
{"id": first_task_id, "title": "Prepare release notes", "done": False},
{"id": second_task_id, "title": "Publish release notes", "done": False},
],
)
const {mutate: createTasks} = useCreateMany();
// Call from an event handler.
createTasks({
resource: 'tasks',
values: [
{id: firstTaskId, title: 'Prepare release notes', done: false},
{id: secondTaskId, title: 'Publish release notes', done: false},
],
});
useCreateMany sends one request per record. A failure can leave some records
created; read the stable IDs before retrying. The successful result contains
the created-record list in data.
/api/apps/$TARUVI_APP_SLUG/datatables/tasks/data/Before you run
FIRST_TASK_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
SECOND_TASK_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
Headers
AuthorizationApi-Key $TARUVI_API_KEYContent-Typeapplication/json
Request body
[
{
"id": "$FIRST_TASK_ID",
"title": "Prepare release notes",
"done": false
},
{
"id": "$SECOND_TASK_ID",
"title": "Publish release notes",
"done": false
}
]
View cURL
FIRST_TASK_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
SECOND_TASK_ID="$(uuidgen | tr '[:upper:]' '[:lower:]')"
curl -X POST "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/datatables/tasks/data/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<JSON
[
{
"id": "$FIRST_TASK_ID",
"title": "Prepare release notes",
"done": false
},
{
"id": "$SECOND_TASK_ID",
"title": "Publish release notes",
"done": false
}
]
JSON
201Returns the created records in data.
REST and the JavaScript builder return the created records in data. Python's
direct create() method returns the created-record list.
Update several records#
Each bulk-update object must include the table's primary key and at least one
field to change. The examples use the generated UUID field named id.
- JavaScript SDK
- Python SDK
- Refine
- REST API
await database
.from('tasks')
.bulkUpdate([
{id: firstTaskId, done: true},
{id: secondTaskId, done: true},
])
.execute();
(
client.database
.from_("tasks")
.update(
[
{"id": first_task_id, "done": True},
{"id": second_task_id, "done": True},
]
)
.execute()
)
const {mutate: updateTasks} = useUpdateMany();
// Call from an event handler.
updateTasks({
resource: 'tasks',
ids: [firstTaskId, secondTaskId],
values: {done: true},
mutationMode: 'pessimistic',
});
Refine applies the same values object to every ID. Omit the primary key
from values so it does not override the selected IDs. For different changes
per record, call useUpdate for each record or use the SDK bulk-update array.
/api/apps/$TARUVI_APP_SLUG/datatables/tasks/data/Headers
AuthorizationApi-Key $TARUVI_API_KEYContent-Typeapplication/json
Request body
[
{
"id": "$FIRST_TASK_ID",
"done": true
},
{
"id": "$SECOND_TASK_ID",
"done": true
}
]
View cURL
curl -X PATCH "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/datatables/tasks/data/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<JSON
[
{
"id": "$FIRST_TASK_ID",
"done": true
},
{
"id": "$SECOND_TASK_ID",
"done": true
}
]
JSON
200Returns the updated records in data.records and their count in data.count.
Bulk update responses place the updated records and their count in
data.records and data.count. After the mutation succeeds, read each
selected record to verify the updated values.
Delete records by ID#
Prefer ID-based deletion when the application already knows the exact target set.
- Affected resource and cascade: The target is the bounded list of record IDs in the named site, app, and table. Review foreign-key behavior for every selected record.
- Reversibility: Bulk deletion is permanent.
- Authorization: Treat authorization as a property of every record; the
caller needs the table policy's
deleteaction for the intended scope. - Backup or export: Export the selected records and retain the exact ID list when the workflow requires recovery.
- Confirmation: Compare the site, app, table, ID list, and intended count with the records read immediately before the request.
- Success response and postcondition: Compare
deleted_countwith the intended count, then read the IDs again and verify that they are absent. - Recovery: This guide documents no bulk undelete. Recreate records only from retained data and re-verify constraints and authorization.
- JavaScript SDK
- Python SDK
- Refine
- REST API
await database
.from('tasks')
.bulkDelete([firstTaskId, secondTaskId])
.execute();
(
client.database
.from_("tasks")
.bulk_delete([first_task_id, second_task_id])
.execute()
)
const {mutate: deleteTasks} = useDeleteMany();
// Call from an event handler.
deleteTasks({
resource: 'tasks',
ids: [firstTaskId, secondTaskId],
mutationMode: 'pessimistic',
});
Refine returns the submitted IDs in data. Read those IDs again to confirm
they are absent.
/api/apps/$TARUVI_APP_SLUG/datatables/tasks/data/Headers
AuthorizationApi-Key $TARUVI_API_KEY
Query parameters
ids$FIRST_TASK_ID,$SECOND_TASK_ID
View cURL
curl -G -X DELETE "$TARUVI_SITE_URL/api/apps/$TARUVI_APP_SLUG/datatables/tasks/data/" \
-H "Authorization: Api-Key $TARUVI_API_KEY" \
--data-urlencode "ids=$FIRST_TASK_ID,$SECOND_TASK_ID"
200Returns the number of deleted records in deleted_count.
The SDK and REST responses report deleted_count. In Refine, verify deletion
by reading the selected IDs again.
Delete records selected by a filter#
Use the filter to select the records, review the complete target set, and save
their id values as an application-owned manifest. Submit that manifest
through the bulk-delete workflow above in bounded chunks.
If the manifest is too large to collect in one run, pause writes and repeatedly read page 1 after deleting each reviewed chunk. Do not advance to page 2 after deleting page 1: offset-based pages shift as records are removed and can skip targets. An immutable cutoff or a previously captured ID manifest provides the same protection when writes cannot be paused.
This select-review-delete workflow keeps every target visible and authorizes each selected record.
You can also delete every matching record in one request with
deleteFiltered() in JavaScript, delete_filtered() in Python, or
DELETE …/data/?filter= in REST. It's permanent: run the same filter as a read
and compare total first. The request is refused when a condition is empty or
when the query also uses search or pagination, and, for callers authorized by
app roles, when more than 1,000 records match.
Plan a reliable batch#
- Choose an explicit batch size of up to 1,000 records. Keep each batch small enough to review, read back, and retry independently.
- Reuse application-owned UUIDs when reconciling a retry. A repeated create can return a duplicate-key conflict instead of inserting a second row.
- Validate required fields and field types before sending the batch.
- Treat authorization as a property of every record, not only the request.
- Read
data.countordeleted_countwhen exposed and compare it with the intended target count. For Refine, verify every selected ID after the mutation. - Split independent work into smaller batches when you need narrower retries or simpler diagnostics.