# How to Set Up Soft Delete with FastCRUD: Complete Implementation Guide

> Learn how to set up soft delete with FastCRUD. This guide shows you how FastCRUD updates boolean and timestamp fields instead of hard deleting records. Implement safer data management.

- Repository: [Benav Labs/fastcrud](https://github.com/benavlabs/fastcrud)
- Tags: how-to-guide
- Published: 2026-02-26

---

**FastCRUD implements soft delete by detecting specific boolean and timestamp columns on your SQLAlchemy model and updating those fields instead of executing a hard `DELETE` statement.**

Setting up soft delete with FastCRUD requires minimal configuration. The library, maintained in the `benavlabs/fastcrud` repository, automatically detects soft-delete columns by checking for the presence of `is_deleted` and `deleted_at` attributes on your model. When these columns exist, the `FastCRUD.delete()` method switches from hard deletion to updating these flag fields, allowing you to retain historical data while filtering out "deleted" records from standard queries.

## How Soft Delete Works in FastCRUD

The soft-delete mechanism in FastCRUD operates through runtime column detection. In [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py), the `delete()` method (around lines 3004-3014) checks whether the target database row has the configured soft-delete attribute using `hasattr(db_row, self.is_deleted_column)`.

If the attribute exists, FastCRUD performs an `UPDATE` operation that sets the boolean flag to `True` and optionally populates the timestamp column with the current UTC time. If the columns are absent, the method falls back to a standard SQL `DELETE` statement. This design makes soft delete entirely opt-in based on your model schema.

## Setting Up Soft Delete in Your Model

To enable soft delete, you must add the appropriate columns to your SQLAlchemy model. FastCRUD uses default column names of `is_deleted` (Boolean) and `deleted_at` (DateTime), though these are configurable.

### Basic Model Configuration

Add the soft-delete columns to your SQLAlchemy declarative base model:

```python

# models/item.py

from sqlalchemy import Column, Integer, String, Boolean, DateTime
from fastcrud.database import Base
from datetime import datetime

class Item(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, nullable=False)
    description = Column(String, nullable=True)

    # Soft delete columns using default names

    is_deleted = Column(Boolean, default=False, nullable=False)
    deleted_at = Column(DateTime, nullable=True, default=None)

```

With these columns present, any `FastCRUD` instance created for the `Item` model will automatically perform soft deletes.

## Configuring FastCRUD for Soft Delete

Once your model includes the soft-delete columns, instantiate `FastCRUD` with or without custom column names depending on your schema.

### Using Default Column Names

If your model uses `is_deleted` and `deleted_at`, no additional configuration is required:

```python

# crud/item.py

from fastcrud import FastCRUD
from models.item import Item

item_crud = FastCRUD(Item)

# This will perform a soft delete

# UPDATE items SET is_deleted = true, deleted_at = NOW() WHERE id = 42

```

### Customizing Column Names

If your existing database schema uses different column names, such as `archived` and `archived_at`, pass these to the `FastCRUD` constructor. The constructor parameters are defined in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py) (lines 88-90):

```python

# crud/product.py

from fastcrud import FastCRUD
from models.product import Product

product_crud = FastCRUD(
    Product,
    is_deleted_column="archived",      # Custom boolean flag column

    deleted_at_column="archived_at",   # Custom timestamp column

)

```

The `EndpointCreator` class in [`fastcrud/endpoint/endpoint_creator.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/endpoint/endpoint_creator.py) (lines 81-83) also accepts these parameters, allowing you to propagate custom column names through your entire API layer.

## Advanced Soft Delete Configuration

For applications requiring audit trails, FastCRUD provides `DeleteConfig` to inject additional metadata during soft-delete operations.

### Injecting Metadata with DeleteConfig

The `DeleteConfig` class, located in [`fastcrud/core/config/crud_configs.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/core/config/crud_configs.py) (lines 97-133), supports an `auto_fields` dictionary. This allows you to automatically populate fields like `deleted_by` using FastAPI's dependency injection system:

```python

# crud/document.py

from fastcrud import FastCRUD, DeleteConfig
from fastapi import Depends, Cookie
from datetime import datetime
from models.document import Document

async def get_current_user_id(session_token: str = Cookie(None)):
    # Your authentication logic here

    return 123

def utc_now():
    return datetime.utcnow()

delete_config = DeleteConfig(
    auto_fields={
        "deleted_by": get_current_user_id,  # Resolved per-request via DI

        "deleted_at": utc_now,               # Custom timestamp function

    }
)

document_crud = FastCRUD(
    Document,
    delete_config=delete_config,
)

```

When `document_crud.delete(db, id=1)` is called, FastCRUD automatically resolves `get_current_user_id` and `utc_now`, injecting the results into the `deleted_by` and `deleted_at` columns before committing the update.

## Implementing Soft Delete in FastAPI Endpoints

To expose soft-delete functionality through REST endpoints, use the `EndpointCreator` or `crud_router` utility. These tools accept the same soft-delete configuration parameters and generate standard DELETE routes that perform soft deletion when configured.

```python

# api/item_router.py

from fastapi import APIRouter, Depends
from fastcrud import EndpointCreator
from database import async_session
from models.item import Item
from schemas.item import CreateItemSchema, UpdateItemSchema

router = APIRouter()

item_endpoint = EndpointCreator(
    session=async_session,
    model=Item,
    create_schema=CreateItemSchema,
    update_schema=UpdateItemSchema,
    # Optional: override default column names

    # is_deleted_column="is_archived",

    # deleted_at_column="archived_timestamp",

)

item_endpoint.add_routes_to_router()
router.include_router(item_endpoint.router, prefix="/items", tags=["items"])

```

The generated `DELETE /items/{id}` endpoint will now invoke the soft-delete logic, setting `is_deleted` to `True` and populating `deleted_at` rather than removing the record from the database.

## Summary

- **FastCRUD** implements soft delete by detecting specific boolean and timestamp columns on your SQLAlchemy models.
- **Default columns** are `is_deleted` and `deleted_at`, but you can customize these via the `is_deleted_column` and `deleted_at_column` parameters in `FastCRUD` or `EndpointCreator`.
- **Automatic fallback** occurs if soft-delete columns are missing; FastCRUD performs a hard delete instead.
- **DeleteConfig** enables advanced audit trails by injecting auto-populated fields like `deleted_by` using FastAPI dependency injection.
- **EndpointCreator** propagates soft-delete settings to REST endpoints automatically, requiring no manual route logic.

## Frequently Asked Questions

### What happens if my model doesn't have the soft-delete columns?

If your SQLAlchemy model lacks the `is_deleted` column (or your custom-named equivalent), FastCRUD automatically falls back to a hard delete. The `delete()` method in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py) checks for the attribute using `hasattr()`; when absent, it executes a standard SQL `DELETE` statement instead of updating flag columns.

### Can I use different column names for soft delete?

Yes. Both `FastCRUD` and `EndpointCreator` accept `is_deleted_column` and `deleted_at_column` parameters to match existing schemas. For example, if your database uses `archived` and `archived_at`, pass these strings when initializing your CRUD instance. These parameters are defined in [`fastcrud/crud/fast_crud.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/crud/fast_crud.py) (lines 88-90) and [`fastcrud/endpoint/endpoint_creator.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/endpoint/endpoint_creator.py) (lines 81-83).

### How do I track who deleted a record?

Use the `DeleteConfig` class with the `auto_fields` dictionary. Located in [`fastcrud/core/config/crud_configs.py`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/core/config/crud_configs.py) (lines 97-133), this configuration allows you to map column names to callable dependencies. For example, set `"deleted_by": get_current_user_id` where `get_current_user_id` is a FastAPI dependency that resolves the current user's ID. FastCRUD automatically injects these values when performing the soft delete.

### Does soft delete affect my query results?

FastCRUD's soft-delete mechanism only modifies how the `delete()` method behaves; it does not automatically filter deleted records from `get` or `get_multi` operations. To exclude soft-deleted rows from your queries, you must manually add filters such as `is_deleted=False` (or your custom column) when calling read methods. This gives you explicit control over whether to include archived data in your results.