Typed CRUD

Delete with a deliberate boundary

Delete only the intended row, consider soft deletion honestly, and verify the effect instead of assuming a successful query changed data.

8 minute lesson

~~~

Delete is another mutation where a missing condition can destroy the complete table. Keep the boundary visible.

Use the same ID and owner condition as the update. returning() tells us whether a row was deleted. A soft-delete timestamp can help recovery, but it adds a filter to every read and does not replace backups or retention rules.

const [deleted] = await db
  .delete(notes)
  .where(and(eq(notes.id, noteId), eq(notes.authorId, userId)))
  .returning({ id: notes.id })

Some teams forbid update or delete without where() through linting or a wrapper. That guard is useful, but it cannot tell whether the condition is correct. A condition such as id > 0 still matches every normal row.

Choose hard delete, archive, or retention based on the product. Do not add soft deletion automatically and then forget that private data remains stored.

Delete one owned note and try the same operation as another user. Prove the second attempt changes nothing, then explain whether this product needs hard deletion, archiving, or time-limited recovery.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →