Soft Deletes
Soft Deletes
Soft deletes mark records as deleted without removing them from the database.
Auto-Detection
Add a deletedAt column to your schema:
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
// ... other fields
deletedAt: integer('deleted_at', { mode: 'timestamp' }),
})
Supported column names (auto-detected):
deletedAtdeleted_atdeletedDate
Behavior
Delete Operation
DELETE /api/posts/123
With deletedAt column:
{
"success": true,
"softDeleted": true,
"message": "Record marked as deleted"
}
Without deletedAt column:
{
"success": true,
"softDeleted": false,
"message": "Record permanently deleted"
}
List Operation
Soft-deleted records are automatically filtered:
GET /api/posts
// Returns only non-deleted posts
Include Deleted (Admin Only)
GET /api/posts?includeDeleted=true
// Requires 'admin' permission
Get Operation
Soft-deleted records return 404 (unless admin):
GET /api/posts/123
// 404 if deleted (unless user has 'admin' permission)
Restore Endpoint
Restore soft-deleted records:
POST /api/posts/123/restore
Response:
{
"data": { ... },
"restored": true
}
Requirements:
- User must have
adminpermission (by default) - Record must be soft-deleted
- Table must support soft deletes
Custom Authorization
Override restore authorization in a custom handler:
// server/api/posts/[id]/restore.post.ts
import { defineAutoApiHandler } from '@websideproject/nuxt-auto-api/utils'
export default defineAutoApiHandler({
async execute(context) {
const postId = context.params.id
const post = await context.db.query.posts.findFirst({
where: and(
eq(posts.id, postId),
isNotNull(posts.deletedAt)
),
})
// Custom authorization: allow post owner to restore
if (post.userId !== context.user.id && !context.permissions.includes('admin')) {
throw createError({ statusCode: 403 })
}
// Restore
const [restored] = await context.db
.update(posts)
.set({ deletedAt: null })
.where(eq(posts.id, postId))
.returning()
return { data: restored, restored: true }
},
})
Migration Example
Add deletedAt to existing table:
// migrations/0001_add_soft_delete.sql
ALTER TABLE posts ADD COLUMN deleted_at INTEGER;
CREATE INDEX idx_posts_deleted_at ON posts(deleted_at);
Best Practices
- Index deletedAt for query performance
- Use for user data - allows recovery if needed
- Purge old deleted records - periodic cleanup job
- Document restore policy - who can restore, when to purge
- Consider GDPR - may need hard delete for user requests
Hard Delete Override
Force hard delete even with deletedAt column:
// Custom handler for permanent deletion
export default defineAutoApiHandler({
async execute(context) {
const postId = context.params.id
// Permanently delete (ignore soft delete)
await context.db.delete(posts).where(eq(posts.id, postId))
return { success: true, hardDeleted: true }
},
})
Pagination
@websideproject/nuxt-auto-api supports both offset-based and cursor-based pagination.
Authentication & Authorization
@websideproject/nuxt-auto-api provides a flexible multi-tier authorization system that works with any authentication provider. You control user context and permissions, while the module enforces authorization rules.