Bulk Operations
Bulk Operations
Nuxt Auto API provides atomic bulk operations for creating, updating, and deleting multiple records in a single request.
Overview
All bulk operations are:
- Atomic: Use database transactions (rollback on any failure)
- Authorized: Check permissions for each item
- Validated: Validate each item before processing
- Hooked: Execute lifecycle hooks for each item
Bulk Create
Create multiple records in a single request:
POST /api/users/bulk
{
"items": [
{
"name": "John Doe",
"email": "john@example.com"
},
{
"name": "Jane Smith",
"email": "jane@example.com"
}
]
}
Response:
{
"data": [
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2024-01-01T00:00:00Z"
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane@example.com",
"createdAt": "2024-01-01T00:00:00Z"
}
],
"meta": {
"total": 2,
"successful": 2,
"failed": 0
}
}
Bulk Update
Update multiple records by ID:
PATCH /api/users/bulk
{
"items": [
{
"id": 1,
"data": {
"name": "John Updated"
}
},
{
"id": 2,
"data": {
"email": "jane.new@example.com"
}
}
]
}
Response:
{
"data": [
{
"id": 1,
"name": "John Updated",
"email": "john@example.com",
"updatedAt": "2024-01-01T00:01:00Z"
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane.new@example.com",
"updatedAt": "2024-01-01T00:01:00Z"
}
],
"meta": {
"total": 2,
"successful": 2,
"failed": 0
}
}
Bulk Delete
Delete multiple records by ID:
DELETE /api/users/bulk
{
"ids": [1, 2, 3]
}
Response:
{
"data": [
{ "id": 1, "deleted": true },
{ "id": 2, "deleted": true },
{ "id": 3, "deleted": true }
],
"meta": {
"total": 3,
"successful": 3,
"failed": 0
}
}
Transaction Behavior
By default, all bulk operations use database transactions:
// If ANY item fails, ALL changes are rolled back
PATCH /api/users/bulk
{
"items": [
{ "id": 1, "data": { "name": "Valid" } },
{ "id": 999, "data": { "name": "Invalid ID" } } // Fails
]
}
// Result: Neither update is applied (rollback)
{
"statusCode": 400,
"message": "Bulk update failed (transaction rolled back)",
"data": {
"errors": [
{
"index": 1,
"id": 999,
"error": "Record with id 999 not found"
}
]
}
}
Non-Transactional Mode
Disable transactions for partial success:
export default defineNuxtConfig({
autoApi: {
bulk: {
transactional: false // Allow partial success
}
}
})
With transactional: false:
PATCH /api/users/bulk
{
"items": [
{ "id": 1, "data": { "name": "Valid" } }, // Succeeds
{ "id": 999, "data": { "name": "Invalid" } } // Fails
]
}
// Result: First update succeeds, second fails
{
"data": [
{
"id": 1,
"name": "Valid",
"updatedAt": "..."
}
],
"meta": {
"total": 2,
"successful": 1,
"failed": 1,
"errors": [
{
"index": 1,
"id": 999,
"error": "Record with id 999 not found"
}
]
}
}
Batch Size Limits
Protect your database from oversized requests:
export default defineNuxtConfig({
autoApi: {
bulk: {
maxBatchSize: 100 // Default: 100
}
}
})
Exceeding the limit returns an error:
POST /api/users/bulk
{
"items": [/* 150 items */]
}
// Response:
{
"statusCode": 400,
"message": "Batch size exceeds maximum of 100"
}
Authorization
Each item is individually authorized:
// User can only update their own records
PATCH /api/posts/bulk
{
"items": [
{ "id": 1, "data": { "title": "My Post" } }, // User owns: ✅
{ "id": 2, "data": { "title": "Others Post" } } // User doesn't own: ❌
]
}
// In transactional mode: entire request fails
// In non-transactional mode: only authorized items succeed
Lifecycle Hooks
Hooks execute for each item in the batch:
// server/plugins/user-hooks.ts
export default defineNitroPlugin(() => {
const hooks = globalThis.__autoApiHooks || (globalThis.__autoApiHooks = {})
hooks.users = {
beforeCreate: async (data) => {
console.log('Creating user:', data.email)
// Set default values, validate, etc.
data.role = data.role || 'user'
return data
},
afterCreate: async (user) => {
console.log('User created:', user.id)
// Send welcome email, log audit, etc.
}
}
})
Bulk create triggers hooks for each item:
POST /api/users/bulk
{
"items": [
{ "name": "User 1", "email": "user1@example.com" },
{ "name": "User 2", "email": "user2@example.com" }
]
}
// Console output:
// Creating user: user1@example.com
// Creating user: user2@example.com
// User created: 1
// User created: 2
Validation
Each item is validated before processing:
// With validation schema
export const userValidation = {
create: z.object({
name: z.string().min(2),
email: z.string().email()
})
}
// Request with validation errors
POST /api/users/bulk
{
"items": [
{ "name": "Valid User", "email": "valid@example.com" },
{ "name": "X", "email": "invalid" } // Validation fails
]
}
// In transactional mode: entire request fails
// In non-transactional mode: only valid items succeed
Frontend Usage
With Composables
// Bulk create
const { execute, data, error } = useAutoApiAction('users', 'bulk')
await execute({
method: 'POST',
body: {
items: [
{ name: 'User 1', email: 'user1@example.com' },
{ name: 'User 2', email: 'user2@example.com' }
]
}
})
console.log(data.value.meta) // { total: 2, successful: 2, failed: 0 }
// Bulk update
const { execute } = useAutoApiAction('users', 'bulk')
await execute({
method: 'PATCH',
body: {
items: [
{ id: 1, data: { name: 'Updated 1' } },
{ id: 2, data: { name: 'Updated 2' } }
]
}
})
// Bulk delete
const { execute } = useAutoApiAction('users', 'bulk')
await execute({
method: 'DELETE',
body: {
ids: [1, 2, 3]
}
})
Direct Fetch
// Bulk create
const response = await $fetch('/api/users/bulk', {
method: 'POST',
body: {
items: [...]
}
})
// Bulk update
await $fetch('/api/users/bulk', {
method: 'PATCH',
body: {
items: [
{ id: 1, data: { ... } }
]
}
})
// Bulk delete
await $fetch('/api/users/bulk', {
method: 'DELETE',
body: {
ids: [1, 2, 3]
}
})
Use Cases
Data Import
// Import CSV data
const users = parseCSV(csvFile)
const { data } = await $fetch('/api/users/bulk', {
method: 'POST',
body: { items: users }
})
console.log(`Imported ${data.meta.successful} users`)
Batch Updates
// Mark multiple items as processed
const itemIds = [1, 2, 3, 4, 5]
await $fetch('/api/tasks/bulk', {
method: 'PATCH',
body: {
items: itemIds.map(id => ({
id,
data: { status: 'completed', completedAt: new Date() }
}))
}
})
Cleanup Operations
// Delete old records
const oldRecordIds = await $fetch('/api/logs', {
query: {
filter: {
createdAt: { $lt: '2023-01-01' }
},
fields: 'id'
}
})
await $fetch('/api/logs/bulk', {
method: 'DELETE',
body: {
ids: oldRecordIds.data.map(r => r.id)
}
})
Configuration Reference
export default defineNuxtConfig({
autoApi: {
bulk: {
// Enable/disable bulk operations (default: true)
enabled: true,
// Maximum items per batch (default: 100)
maxBatchSize: 100,
// Use transactions (default: true)
transactional: true
}
}
})
Error Handling
Transactional Mode Errors
{
"statusCode": 400,
"message": "Bulk update failed (transaction rolled back)",
"data": {
"errors": [
{
"index": 1,
"id": 999,
"error": "Record with id 999 not found"
}
]
}
}
Non-Transactional Mode Errors
{
"data": [/* successful items */],
"meta": {
"total": 5,
"successful": 3,
"failed": 2,
"errors": [
{
"index": 1,
"id": 999,
"error": "Record not found"
},
{
"index": 3,
"error": "Validation failed"
}
]
}
}
Performance Considerations
Batch Size
Smaller batches = More requests but safer:
- Recommended: 50-100 items per batch
- Large imports: Split into multiple batches
// Split large dataset
const BATCH_SIZE = 100
const batches = chunk(largeDataset, BATCH_SIZE)
for (const batch of batches) {
await $fetch('/api/users/bulk', {
method: 'POST',
body: { items: batch }
})
}
Indexes
Ensure indexes exist for bulk updates/deletes:
-- Primary key index (usually automatic)
CREATE INDEX idx_users_id ON users(id);
-- For tenant-scoped bulk operations
CREATE INDEX idx_users_org_id ON users(organization_id);
Memory Usage
Large batches consume memory:
- Server: Holds all items in memory during transaction
- Client: JSON serialization/parsing overhead
Multi-Tenancy
Bulk operations respect tenant scoping:
// Automatically scoped to current tenant
POST /api/users/bulk
{
"items": [
{ "name": "User 1", "email": "user1@example.com" }
// organizationId automatically added
]
}
// Can only update/delete records in current tenant
PATCH /api/users/bulk
DELETE /api/users/bulk
Soft Deletes
Bulk delete respects soft delete configuration:
// If soft deletes are enabled
DELETE /api/users/bulk
{
"ids": [1, 2, 3]
}
// Records are marked as deleted, not physically removed
// deletedAt timestamp is set
Best Practices
- Use transactions: Keep
transactional: truefor data integrity - Limit batch size: Don't exceed 100-200 items per request
- Handle errors: Check
meta.failedandmeta.errorsin responses - Validate upfront: Validate data before sending bulk requests
- Use hooks: Implement audit logging in
after*hooks - Monitor performance: Track bulk operation duration and optimize
- Split large imports: Break into smaller batches with progress tracking
Nested Relations
Nuxt Auto API supports advanced nested relation loading with field selection, filtering, and pagination at each level of nesting.
Getting Started
Nuxt Auto Admin automatically generates a beautiful, type-safe admin panel from your Auto API resources with built-in CRUD operations, permissions, and customization options.