Skip to content

Delete Single Object

Delete a specific device (object) from the database, identified by UID, OID, or related OID. This endpoint allows selective deletion of individual objects rather than deleting all objects at once.

Request

http
DELETE /api/object

Authentication Required: Must include JWT token in Authorization header.

Required Scope: objects

Query Parameters:

  • uid (string, optional): UID of the device to delete (hex format)
  • oid (string, optional): OID of the device to delete (hex format)
  • related_oid (string, optional): Related OID for deletion (hex format)

Parameter Logic:

  • UID Priority: When uid is provided, oid is ignored (UID takes precedence)
  • OID Fallback: oid is only used when uid is empty/not provided
  • Related Object Deletion: related_oid works with whichever identifier is active:
    • uid + related_oid → Deletes related object from the UID device
    • oid + related_oid → Deletes related object from the OID device (only if uid is empty)
  • At least one of uid or oid must be provided

Request Examples

Delete by UID

bash
curl -X DELETE "http://your-server-ip:port/api/object?uid=ABCDEF123456" \
     -H "Authorization: Bearer YOUR_JWT_TOKEN"

Delete by OID

bash
curl -X DELETE "http://your-server-ip:port/api/object?oid=1234567890" \
     -H "Authorization: Bearer YOUR_JWT_TOKEN"

Delete by UID (OID Ignored)

bash
# When both uid and oid are provided, only uid is used (oid is ignored)
curl -X DELETE "http://your-server-ip:port/api/object?uid=ABCDEF123456&oid=1234567890" \
     -H "Authorization: Bearer YOUR_JWT_TOKEN"
# This will delete by UID ABCDEF123456 only
bash
curl -X DELETE "http://your-server-ip:port/api/object?uid=ABCDEF123456&related_oid=9876543210" \
     -H "Authorization: Bearer YOUR_JWT_TOKEN"
bash
curl -X DELETE "http://your-server-ip:port/api/object?oid=1234567890&related_oid=9876543210" \
     -H "Authorization: Bearer YOUR_JWT_TOKEN"

Response

Success (200 OK)

json
{
  "success": true
}

Error Responses

All error responses are returned as JSON:

400 Bad Request

json
{
  "error": "uid and/or oid must not be empty"
}
json
{
  "error": "Invalid uid"
}
json
{
  "error": "Invalid oid"
}
json
{
  "error": "Invalid related_oid"
}

401 Unauthorized

json
{
  "error": "NOT_LOGGED_IN"
}

403 Forbidden

json
{
  "error": "FORBIDDEN"
}

405 Method Not Allowed

json
{
  "error": "Method Not Allowed"
}

500 Internal Server Error

json
{
  "error": "Failed to delete object: Database error occurred"
}

503 Service Unavailable

json
{
  "error": "Database is not ready"
}

Parameter Priority Rules:

  1. UID Priority: When uid is provided, oid is completely ignored
  2. OID Fallback: oid is only used when uid is empty or not provided
  3. Related Object Logic: related_oid works with the active identifier

Deletion Behavior:

  • uid=ABC123 → Deletes entire device ABC123 (oid ignored if present)
  • oid=123456 (no uid) → Deletes entire device with OID 123456
  • uid=ABC123&oid=999&related_oid=XYZ789 → Deletes only related object XYZ789 from device ABC123 (oid 999 is ignored)
  • oid=123456&related_oid=XYZ789 (no uid) → Deletes only related object XYZ789 from device with OID 123456

Important Notes:

  • When related_oid is used, only the related object is deleted, not the main device
  • The main device (identified by UID or OID) remains in the system
  • This is useful for managing device components, sensors, or modules independently

Usage Examples

Basic Object Deletion

javascript
async function deleteObject(uid = null, oid = null, relatedOid = null) {
  // Build query parameters
  const params = new URLSearchParams();
  if (uid) params.append('uid', uid);
  if (oid) params.append('oid', oid);
  if (relatedOid) params.append('related_oid', relatedOid);
  
  if (params.toString() === '' && !relatedOid) {
    throw new Error('At least one of uid or oid must be provided');
  }
  
  try {
    const response = await fetch(`/api/object?${params.toString()}`, {
      method: 'DELETE',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(`HTTP ${response.status}: ${error.error}`);
    }
    
    const result = await response.json();
    console.log('Object deleted successfully');
    return result;
  } catch (error) {
    console.error('Failed to delete object:', error);
    throw error;
  }
}

Object Management Class

javascript
class ObjectManager {
  constructor(token) {
    this.token = token;
  }
  
  async deleteByUid(uid) {
    return await this.deleteObject({ uid });
  }
  
  async deleteByOid(oid) {
    return await this.deleteObject({ oid });
  }
  
  async deleteByBoth(uid, oid) {
    // Note: When both are provided, UID takes precedence (OID is ignored)
    console.log(`Deleting by UID: ${uid} (OID ${oid} will be ignored)`);
    return await this.deleteObject({ uid, oid });
  }
  
  async deleteRelatedObjectByUid(parentUid, relatedOid) {
    console.log(`Deleting related object ${relatedOid} from device UID ${parentUid}`);
    return await this.deleteObject({ uid: parentUid, relatedOid });
  }
  
  async deleteRelatedObjectByOid(parentOid, relatedOid) {
    console.log(`Deleting related object ${relatedOid} from device OID ${parentOid}`);
    return await this.deleteObject({ oid: parentOid, relatedOid });
  }
  
  async deleteRelatedObject(parentUid, relatedOid) {
    // Legacy method - defaults to UID-based deletion
    return await this.deleteRelatedObjectByUid(parentUid, relatedOid);
  }
  
  async deleteObject({ uid = null, oid = null, relatedOid = null }) {
    const params = new URLSearchParams();
    if (uid) params.append('uid', uid);
    if (oid) params.append('oid', oid);
    if (relatedOid) params.append('related_oid', relatedOid);
    
    const response = await fetch(`/api/object?${params.toString()}`, {
      method: 'DELETE',
      headers: {
        'Authorization': `Bearer ${this.token}`,
        'Content-Type': 'application/json'
      }
    });
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(`Deletion failed: ${error.error}`);
    }
    
    return await response.json();
  }
}

// Usage
const objectManager = new ObjectManager(authToken);

// Delete entire device
await objectManager.deleteByUid('ABC123');

// Delete device by OID (only when UID is not provided)
await objectManager.deleteByOid('1234567890');

// Delete only a related object by UID
await objectManager.deleteRelatedObjectByUid('ABC123', 'SENSOR001');

// Delete only a related object by OID (when UID is not available)
await objectManager.deleteRelatedObjectByOid('1234567890', 'SENSOR001');

Batch Deletion with Confirmation

javascript
class BatchObjectDeletion {
  constructor(token) {
    this.token = token;
    this.objectManager = new ObjectManager(token);
  }
  
  async deleteMultipleObjects(objects, confirmCallback = null) {
    const results = [];
    
    for (const obj of objects) {
      try {
        // Optional confirmation for each deletion
        if (confirmCallback && !await confirmCallback(obj)) {
          results.push({
            object: obj,
            status: 'skipped',
            reason: 'User cancelled'
          });
          continue;
        }
        
        const result = await this.objectManager.deleteObject(obj);
        results.push({
          object: obj,
          status: 'success',
          result: result
        });
        
        console.log(`✓ Deleted object: ${obj.uid || obj.oid}`);
        
      } catch (error) {
        results.push({
          object: obj,
          status: 'error',
          error: error.message
        });
        
        console.error(`✗ Failed to delete object: ${obj.uid || obj.oid} - ${error.message}`);
      }
    }
    
    return results;
  }
  
  async deleteObjectsByFilter(objects, filterFn) {
    const objectsToDelete = objects.filter(filterFn);
    return await this.deleteMultipleObjects(objectsToDelete);
  }
}

// Usage
const batchDeletion = new BatchObjectDeletion(authToken);

// Delete multiple objects with confirmation
const objectsToDelete = [
  { uid: 'ABC123' },
  { oid: '1234567890' },
  { uid: 'DEF456', relatedOid: 'SENSOR001' }
];

const confirmDeletion = async (obj) => {
  return confirm(`Delete object ${obj.uid || obj.oid}?`);
};

const results = await batchDeletion.deleteMultipleObjects(objectsToDelete, confirmDeletion);

Error Handling and Validation

javascript
class ObjectDeletionValidator {
  static validateUid(uid) {
    if (!uid) return true; // Optional parameter
    
    // Check hex format
    if (!/^[0-9A-Fa-f]+$/.test(uid)) {
      throw new Error('UID must be in hexadecimal format');
    }
    
    // Check length (adjust based on your system requirements)
    if (uid.length < 6 || uid.length > 16) {
      throw new Error('UID length must be between 6 and 16 characters');
    }
    
    return true;
  }
  
  static validateOid(oid) {
    if (!oid) return true; // Optional parameter
    
    // Check if numeric string
    if (!/^\d+$/.test(oid)) {
      throw new Error('OID must be numeric');
    }
    
    // Check reasonable range
    const numericOid = parseInt(oid);
    if (numericOid <= 0 || numericOid > 9999999999) {
      throw new Error('OID must be between 1 and 9999999999');
    }
    
    return true;
  }
  
  static validateParameters(uid, oid, relatedOid) {
    // At least one of uid or oid required
    if (!uid && !oid) {
      throw new Error('At least one of uid or oid must be provided');
    }
    
    // Warn if both UID and OID provided (OID will be ignored)
    if (uid && oid) {
      console.warn('Both UID and OID provided - OID will be ignored, using UID only');
    }
    
    this.validateUid(uid);
    this.validateOid(oid);
    this.validateOid(relatedOid); // Same format as OID
    
    return true;
  }
}

// Usage with validation
async function safeDeleteObject(uid, oid, relatedOid) {
  try {
    // Validate parameters
    ObjectDeletionValidator.validateParameters(uid, oid, relatedOid);
    
    // Proceed with deletion
    return await deleteObject(uid, oid, relatedOid);
  } catch (error) {
    console.error('Validation failed:', error.message);
    throw error;
  }
}
javascript
class RelatedObjectManager {
  constructor(token) {
    this.token = token;
    this.objectManager = new ObjectManager(token);
  }
  
  async removeDeviceComponent(deviceUid, componentType, componentId) {
    const relatedOid = `${componentType}_${componentId}`;
    
    try {
      console.log(`Removing ${componentType} ${componentId} from device ${deviceUid}`);
      
      const result = await this.objectManager.deleteRelatedObject(deviceUid, relatedOid);
      
      console.log(`✓ Successfully removed ${componentType} from device ${deviceUid}`);
      return result;
      
    } catch (error) {
      console.error(`✗ Failed to remove ${componentType} from device ${deviceUid}:`, error.message);
      throw error;
    }
  }
  
  async removeSensor(deviceUid, sensorId) {
    return await this.removeDeviceComponent(deviceUid, 'SENSOR', sensorId);
  }
  
  async removeModule(deviceUid, moduleId) {
    return await this.removeDeviceComponent(deviceUid, 'MODULE', moduleId);
  }
  
  async removeAllComponents(deviceUid, components) {
    const results = [];
    
    for (const component of components) {
      try {
        const result = await this.removeDeviceComponent(
          deviceUid, 
          component.type, 
          component.id
        );
        results.push({ component, status: 'success', result });
      } catch (error) {
        results.push({ component, status: 'error', error: error.message });
      }
    }
    
    return results;
  }
}

// Usage
const relatedManager = new RelatedObjectManager(authToken);

// Remove specific components
await relatedManager.removeSensor('ABC123', 'TEMP001');
await relatedManager.removeModule('ABC123', 'COMM001');

// Remove multiple components
const components = [
  { type: 'SENSOR', id: 'TEMP001' },
  { type: 'SENSOR', id: 'HUM001' },
  { type: 'MODULE', id: 'GPS001' }
];

const results = await relatedManager.removeAllComponents('ABC123', components);

Security Considerations

Deletion Confirmation

javascript
async function confirmAndDelete(uid, oid, relatedOid) {
  let message;
  
  if (relatedOid) {
    message = `Delete related object ${relatedOid} from device ${uid || oid}?`;
  } else {
    message = `Delete entire device ${uid || oid}? This action cannot be undone.`;
  }
  
  if (!confirm(message)) {
    console.log('Deletion cancelled by user');
    return false;
  }
  
  return await deleteObject(uid, oid, relatedOid);
}

Audit Logging

javascript
class DeletionAuditLogger {
  static async logDeletion(uid, oid, relatedOid, success, error = null) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      action: 'object_deletion',
      target: {
        uid: uid || null,
        oid: oid || null,
        related_oid: relatedOid || null
      },
      success: success,
      error: error,
      user: this.getCurrentUser()
    };
    
    console.log('DELETION_AUDIT:', JSON.stringify(logEntry));
    
    // Send to audit service
    try {
      await fetch('/api/audit/log', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(logEntry)
      });
    } catch (auditError) {
      console.error('Failed to log audit entry:', auditError);
    }
  }
  
  static getCurrentUser() {
    try {
      const token = localStorage.getItem('authToken');
      const payload = JSON.parse(atob(token.split('.')[1]));
      return payload.username || payload.sub;
    } catch {
      return 'unknown';
    }
  }
}

// Usage with audit logging
async function auditedDeleteObject(uid, oid, relatedOid) {
  try {
    const result = await deleteObject(uid, oid, relatedOid);
    await DeletionAuditLogger.logDeletion(uid, oid, relatedOid, true);
    return result;
  } catch (error) {
    await DeletionAuditLogger.logDeletion(uid, oid, relatedOid, false, error.message);
    throw error;
  }
}

Important Notes

  • Scope Required: Users must have the objects scope to access this endpoint
  • Selective Deletion: Unlike /api/objects (DELETE all), this endpoint targets specific objects
  • Related Objects: Using related_oid deletes only the related object, not the main device
  • Parameter Validation: UID must be hex format, OID must be numeric
  • JSON Errors: All error responses are in JSON format (unlike some other endpoints)

Released under the MIT License.