DELETE Statements

Remove records from your database safely and precisely.

Basic DELETE

basic.ts
// Delete by ID
cook`yeet:users sus:id=1`
// → DELETE FROM users WHERE id = $1

// Delete by condition
cook`yeet:sessions sus:expired=true`
// → DELETE FROM sessions WHERE expired = $1

With RETURNING

returning.ts
// Return deleted row
cook`yeet:users sus:id=1 flex:*`
// → DELETE FROM users WHERE id = $1 RETURNING *

// Return specific columns
cook`yeet:users sus:id=1 flex:id,email`
// → DELETE FROM users WHERE id = $1 RETURNING id, email

Conditional Deletes

conditional.ts
// Multiple conditions
cook`yeet:posts sus:status=draft sus:created_at<2024-01-01`
// → DELETE FROM posts WHERE status = $1 AND created_at < $2

// OR conditions
cook`yeet:notifications sus:read=true|created_at<2024-01-01`
// → DELETE FROM notifications WHERE read = $1 OR created_at < $2

// Using IN
cook`yeet:users sus:id.in(1,2,3,4,5)`
// → DELETE FROM users WHERE id IN ($1, $2, $3, $4, $5)

Soft Deletes

Instead of hard deletes, mark records as deleted:

soft-delete.ts
// Configure soft deletes in schema
const schema = defineSchema({
  users: {
    // ... columns
    deleted_at: 'timestamp?'
  }
}, {
  softDelete: {
    column: 'deleted_at',
    tables: ['users', 'posts']
  }
});

// This becomes an UPDATE instead of DELETE
cook`yeet:users sus:id=1`
// → UPDATE users SET deleted_at = NOW() WHERE id = $1

// Force hard delete when needed
cook`yeet:users sus:id=1 hard:true`
// → DELETE FROM users WHERE id = $1

Cascade Deletes

cascade.ts
// With relations defined, cascade deletes child records
await db.delete(
  cook`yeet:users sus:id=1`,
  { cascade: ['posts', 'comments'] }
);
// Deletes user and all their posts and comments

Danger Zone

DELETE without WHERE will remove all rows. genaql requires explicit confirmation or can be configured to always require WHERE clauses.

Best Practice

Use soft deletes for user data, transactions within deletes for consistency, and always test delete queries with SELECT first.