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
The name of the collection containing the document
The unique identifier (_id) of the document to delete
Bearer token for authentication
Response
Always true if the deletion was successful
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
- Extract collection and ID from URL path (handlers_crud.go:465-466)
- Validate parameters - collection and ID required (handlers_crud.go:468-475)
- Authenticate request and get auth context (handlers_crud.go:478)
- Validate collection exists in schema (handlers_crud.go:484)
- Fetch document to verify existence and check permissions (handlers_crud.go:497)
- Handle not found - return 404 if document doesn’t exist (handlers_crud.go:499)
- Check RBAC delete permission on specific document (handlers_crud.go:519)
- Execute pre-delete hooks - allow cancellation (handlers_crud.go:528)
- Save version snapshot - final snapshot if versioning enabled (handlers_crud.go:547)
- Delete from MongoDB (handlers_crud.go:552)
- Execute post-delete hooks - trigger side effects (handlers_crud.go:561)
- Log audit event with document snapshot (handlers_crud.go:576)
- 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"}'