Skip to main content
DELETE
/
{collection}
/
{id}
Delete Document
curl --request DELETE \
  --url https://api.example.com/{collection}/{id} \
  --header 'Authorization: <authorization>'
{
  "deleted": true,
  "id": "<string>"
}

Overview

Delete a document from a collection by its ID. The operation:
  • Validates permissions - Checks if user can delete this specific document
  • Saves final snapshot - Stores version before deletion if versioning is enabled
  • Executes hooks - Runs pre/post-delete hooks for validation and side effects
  • Logs audit trail - Records deletion with document snapshot
  • Returns confirmation - Confirms successful deletion with document ID

Request

Path Parameters

collection
string
required
The name of the collection containing the document
id
string
required
The unique identifier (_id) of the document to delete

Headers

Authorization
string
required
Bearer token for authentication

Response

deleted
boolean
Always true if the deletion was successful
id
string
The ID of the deleted document

Examples

Basic Delete

curl -X DELETE https://api.example.com/users/507f1f77bcf86cd799439011 \
  -H "Authorization: Bearer YOUR_TOKEN"

Response

{
  "deleted": true,
  "id": "507f1f77bcf86cd799439011"
}

Delete with String ID

curl -X DELETE https://api.example.com/products/SKU-12345 \
  -H "Authorization: Bearer YOUR_TOKEN"
{
  "deleted": true,
  "id": "SKU-12345"
}

Implementation Details

MongoDB Query Pattern

The handler uses MongoDB’s deleteOne operation:
// Convert ID to appropriate format
var filter bson.M
objectID, err := primitive.ObjectIDFromHex(id)
if err == nil {
    filter = bson.M{"_id": objectID}
} else {
    filter = bson.M{"_id": id}
}

// Delete the document
result, err := collection.DeleteOne(ctx, filter)
if result.DeletedCount == 0 {
    return ErrNotFound
}

Workflow

  1. Extract collection and ID from URL path (handlers_crud.go:465-466)
  2. Validate parameters - collection and ID required (handlers_crud.go:468-475)
  3. Authenticate request and get auth context (handlers_crud.go:478)
  4. Validate collection exists in schema (handlers_crud.go:484)
  5. Fetch document to verify existence and check permissions (handlers_crud.go:497)
  6. Handle not found - return 404 if document doesn’t exist (handlers_crud.go:499)
  7. Check RBAC delete permission on specific document (handlers_crud.go:519)
  8. Execute pre-delete hooks - allow cancellation (handlers_crud.go:528)
  9. Save version snapshot - final snapshot if versioning enabled (handlers_crud.go:547)
  10. Delete from MongoDB (handlers_crud.go:552)
  11. Execute post-delete hooks - trigger side effects (handlers_crud.go:561)
  12. Log audit event with document snapshot (handlers_crud.go:576)
  13. Return success response (handlers_crud.go:579)

Permission Check

The system verifies delete permissions on the specific document:
if !h.canPerformAction(authCtx, collection, config.ActionDelete, doc) {
    return ErrForbidden
}
Example permission rules:
  • Owner-only: resource.created_by == user.id
  • Admin-only: user.role == "admin"
  • Department managers: resource.department == user.department && user.role == "manager"
  • No one (prevent deletion): Set no delete action in policy

Version Snapshot

Before deletion, a final snapshot is saved:
if collConfig.Versioning.Enabled && h.version != nil {
    h.saveVersionSnapshot(id, collection, doc, authCtx.UserID)
}
This allows:
  • Audit trail of deleted documents
  • Potential document recovery
  • Compliance with data retention policies
See Version History for accessing deleted document versions.

Audit Logging

Deletion is logged with full document snapshot:
auditEvent := &audit.AuditEvent{
    TenantID:   authCtx.TenantID,
    UserID:     authCtx.UserID,
    Action:     "delete",
    Collection: collection,
    DocID:      id,
    Before:     doc, // Full document snapshot
    Success:    true,
}
h.audit.Log(ctx, auditEvent)

Error Responses

400 Bad Request

Returned when:
  • Collection name is missing
  • Document ID is missing or invalid
  • Pre-delete hook fails
{
  "error": "Pre-delete hook failed",
  "code": "bad_request",
  "details": {
    "error": "Cannot delete document with active references"
  }
}

401 Unauthorized

{
  "error": "Authentication required"
}

403 Forbidden

Returned when user doesn’t have delete permission:
{
  "error": "You don't have permission to perform this action",
  "code": "forbidden",
  "details": {
    "action": "delete",
    "collection": "users"
  }
}

404 Not Found

Returned when document or collection doesn’t exist:
{
  "error": "Document not found",
  "code": "document_not_found",
  "details": {
    "collection": "users",
    "id": "507f1f77bcf86cd799439011"
  }
}

500 Internal Server Error

{
  "error": "Failed to delete document",
  "code": "internal_error",
  "details": {
    "error": "database write failed"
  }
}

Hooks

Pre-Delete Hook

Executed before deletion. Can prevent deletion by throwing an error:
function preDelete(event) {
  // event.before = document to be deleted
  
  // Check if safe to delete
  if (event.before.has_active_subscriptions) {
    throw new Error('Cannot delete user with active subscriptions');
  }
  
  // Check for references
  const orderCount = db.orders.countDocuments({ user_id: event.before._id });
  if (orderCount > 0) {
    throw new Error(`Cannot delete user with ${orderCount} orders`);
  }
  
  return event;
}

Post-Delete Hook

Executed after successful deletion (best-effort, won’t fail the request):
function postDelete(event) {
  // event.before = deleted document
  
  // Clean up related data
  db.sessions.deleteMany({ user_id: event.before._id });
  db.notifications.deleteMany({ user_id: event.before._id });
  
  // Trigger notifications
  sendAccountDeletionEmail(event.before.email);
  
  // Update analytics
  analytics.track('user_deleted', {
    user_id: event.before._id,
    deleted_by: event.user_id
  });
}

Use Cases

Delete User Account

curl -X DELETE https://api.example.com/users/507f1f77bcf86cd799439011 \
  -H "Authorization: Bearer YOUR_TOKEN"

Remove Draft

curl -X DELETE https://api.example.com/drafts/draft_456 \
  -H "Authorization: Bearer YOUR_TOKEN"

Cancel Order

curl -X DELETE https://api.example.com/orders/ORD-12345 \
  -H "Authorization: Bearer YOUR_TOKEN"

Safety Considerations

Soft Delete Alternative

For important data, consider soft deletes instead of hard deletes:
# Instead of DELETE, use UPDATE to mark as deleted
curl -X PUT https://api.example.com/users/507f1f77bcf86cd799439011 \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "deleted": true,
    "deleted_at": "2024-03-16T10:30:00Z"
  }'
Implement soft delete in schema:
fields:
  deleted:
    type: boolean
    default: false
  deleted_at:
    type: date
    optional: true
Filter out soft-deleted documents in queries:
policies:
  users:
    member:
      when: "resource.deleted != true"

Cascade Deletion

Use hooks to handle cascading deletes:
function postDelete(event) {
  const userId = event.before._id;
  
  // Delete related documents
  db.posts.deleteMany({ author_id: userId });
  db.comments.deleteMany({ user_id: userId });
  db.likes.deleteMany({ user_id: userId });
}

Prevent Accidental Deletion

Require confirmation or additional checks:
function preDelete(event) {
  // Require special permission for production data
  if (event.before.environment === 'production' && 
      !event.metadata.confirmed) {
    throw new Error('Deletion requires explicit confirmation');
  }
}

Recovery

If versioning is enabled, deleted documents can be recovered:
# List versions (including deleted)
curl -X GET "https://api.example.com/users/507f1f77bcf86cd799439011/versions" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Restore from version
curl -X POST "https://api.example.com/users/507f1f77bcf86cd799439011/restore" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"version_id": "version_123"}'

Build docs developers (and LLMs) love