github benavlabs/fastcrud v0.19.0
0.19.0

latest releases: v0.23.0, v0.22.3, v0.22.2...
12 months ago

FastCRUD 0.19.0 Release Notes

Warning

The fastcrud.paginated module is deprecated and will be removed in the next major version. Import pagination utilities directly from fastcrud instead:

# OLD (deprecated, but still works)
from fastcrud.paginated import PaginatedListResponse

# NEW (recommended)
from fastcrud import PaginatedListResponse

FastCRUD 0.19.0 represents the largest architectural overhaul in the project's history, moving from a helper-based architecture to a protocol-based six-layer system. Most changes are internal - the public API remains largely the same with full backward compatibility, but users will benefit from enhanced type safety, improved performance, and more reliable operations.

Summary

What's New for Users:

  • Enhanced Type Safety: Method overloads eliminate manual isinstance() checks - your IDE now knows exact return types
  • New return_as_model Parameter: 7 methods gained this parameter for consistent dict/Pydantic model control
  • New Configuration Classes: CreateConfig, UpdateConfig, DeleteConfig for server-side field injection and security
  • Performance Improvements: Query caching, memory optimization, faster SQLAlchemy introspection
  • Bug Fixes: Cartesian product issue in complex joins resolved
  • Better Imports: All utilities now available from main fastcrud import

What's New Internally:

  • Complete Architectural Rewrite: From 2 helper files to 6-layer protocol-based architecture
  • Protocol-Based Design: 8 new protocols eliminate circular dependencies, enable clean testing
  • Core Module System: 15+ new specialized modules replace monolithic helper files
  • 99.31% Test Coverage: Strategic testing with comprehensive edge case coverage
  • Framework Independence: Core logic separated from FastAPI - ready for other frameworks

Migration Required: None - full backward compatibility maintained. Only deprecation: import pagination utilities from fastcrud instead of fastcrud.paginated.

🔧 Public API Changes

Enhanced Type Safety

Method Overloads for Better Developer Experience

  • Enhanced Methods: All major CRUD methods now have @overload signatures for type-safe return types
  • New Parameter: Added return_as_model: bool = False to: create(), get(), update(), upsert(), upsert_multi(), get_multi_by_cursor(), get_multi_joined()
  • Type Inference: IDE automatically infers return types - no more manual isinstance() checks needed
# Type automatically inferred as UserRead
user = await crud.get(db, id=1, return_as_model=True)
name = user.name  # Type-safe access

# Type automatically inferred as dict[str, Any]
user_dict = await crud.get(db, id=1, return_as_model=False)
name = user_dict["name"]  # Dictionary access

New Configuration Classes

Server-Side Field Injection

  • New Exports: CreateConfig, UpdateConfig, DeleteConfig available from main import
  • Auto Fields: Automatic field injection with auto_fields parameter
  • Security: Exclude sensitive fields from API schemas with exclude_from_schema
from fastcrud import CreateConfig

config = CreateConfig(
    auto_fields={"created_at": lambda: datetime.utcnow()},
    exclude_from_schema={"created_at"}
)

Enhanced Import Structure

Centralized Imports

  • New Exports: CountConfig, JoinConfig, FilterConfig now available from main import
  • Pagination: PaginatedListResponse, PaginatedRequestQuery centralized
# All available from main import
from fastcrud import (
    FastCRUD, CreateConfig, UpdateConfig, DeleteConfig,
    CountConfig, JoinConfig, PaginatedListResponse
)

Performance Improvements

Noticeable Performance Gains

  • Query Caching: Complex filter parsing memoized - same queries execute faster
  • Memory Optimization: Reduced object allocation through strategic reuse
  • Instance Caching: SQLAlchemy introspection cached per model instance

Bug Fixes

Cartesian Product Fix

  • Fixed: get_joined with multiple one-to-many joins and nest_joins=True no longer produces duplicate results
  • Impact: More reliable data in complex join scenarios

Deprecation

Module Deprecation

  • Deprecated: fastcrud.paginated module (imports still work but show warning)
  • Migration: Import pagination utilities directly from fastcrud instead

🏗️ Internal Architecture Changes

Complete Architectural Overhaul

Six-Layer Protocol-Based Architecture

FastCRUD now follows a strict six-level dependency hierarchy that solves real problems in maintaining and scaling CRUD applications:

Level 6: Framework Layer (fastapi_dependencies.py, endpoint/)
Level 5: CRUD Layer (crud/fast_crud.py, validation.py, execution.py)
Level 4: Integration Layer (core/data/formatting.py, join_processing.py)
Level 3: Orchestration Layer (core/query/, field_management.py, data/nesting.py)
Level 2: Core Logic Layer (core/config/, filtering/, data/transforms.py, pagination.py)
Level 1: Foundation Layer (core/protocols.py, introspection.py, types.py)

Benefits of Layer Separation:

  • Debugging: When join operations break, check Integration layer. Filter parsing issues? Start in Orchestration
  • Testing: Each layer can be tested in isolation with focused unit tests
  • Evolution: Change query building logic? Only affects Level 3 and above
  • Framework Independence: Everything below Level 6 could work with Flask, Django, or other frameworks

Protocol-Based Design

Eliminates Circular Dependencies

  • 8 New Protocols: CRUDInstance, ModelIntrospector, DataProcessor, FilterProcessor, QueryBuilder, ResponseFormatter, DatabaseAdapter, ValidationProcessor
  • Duck Typing Made Explicit: Protocol interfaces define what methods a class should have without implementation
  • Clean Dependency Flow: Lower levels define interfaces, higher levels implement them
# OLD: Circular import hell
from ..crud.fast_crud import FastCRUD
def some_function(crud: FastCRUD):  # Creates circular imports
    pass

# NEW: Protocol-based design
from ..core.protocols import CRUDInstance
def some_function(crud: CRUDInstance):  # Clean, no circular imports
    pass

Protocol Implementation Pattern

  1. Foundation Layer: Define the interface (CRUDInstance protocol)
  2. Higher Layers: Use the protocol (execution layer imports protocol, not concrete class)
  3. Implementation Layer: Automatically satisfies protocol (FastCRUD implements the methods)

This creates clean dependency flow with no runtime dependency resolution or magic - just type-safe interfaces.

New Core Module Structure

Complete File Reorganization

fastcrud/core/
├── config/              # Configuration management
│   ├── crud_configs.py  # CreateConfig, UpdateConfig, DeleteConfig
│   └── join_configs.py  # JoinConfig, CountConfig
├── data/               # Data processing
│   ├── formatting.py   # Response formatting with join processing
│   ├── nesting.py     # Data nesting with introspection
│   └── transforms.py  # Pure data transformation functions
├── filtering/          # Filter processing
│   ├── operators.py   # SUPPORTED_FILTERS, get_sqlalchemy_filter
│   ├── processor.py   # FilterProcessor class
│   └── validators.py  # Filter validation functions
├── query/             # SQL query building
│   ├── builder.py     # SQLQueryBuilder class
│   ├── joins.py       # JoinBuilder class
│   └── sorting.py     # SortProcessor class
├── field_management.py # Schema and field operations
├── introspection.py   # Model introspection with caching
├── join_processing.py # Join processing logic
├── pagination.py      # Pagination utilities
└── protocols.py       # Protocol interfaces

Removed Legacy Files:

  • fastcrud/crud/helper.py - Functionality moved to specialized core modules
  • fastcrud/endpoint/helper.py - Consolidated into fastcrud/fastapi_dependencies.py

Enhanced Type System

Strategic Type Improvements

  • New Types: FilterValueType, FilterValue, FilterValueSequence for precise database operations
  • Response Types: GetMultiResponseDict, GetMultiResponseModel, UpsertMultiResponseDict, UpsertMultiResponseModel
  • Type Guards: Proper type guards in filtering modules with isinstance() checks
# Enhanced filter typing
FilterValue = Union[str, int, float, bool, datetime, Decimal, None]
FilterValueSequence = Union[list[FilterValue], tuple[FilterValue, ...], set[FilterValue]]
FilterValueType = Union[FilterValue, FilterValueSequence, dict[str, FilterValue]]

Performance Optimizations

Instance-Level Caching Strategy

class ModelIntrospector:
    def __init__(self, model: type[ModelType]):
        self._inspector: Optional[Inspector] = None
        self._primary_keys: Optional[list[str]] = None  # Cached results

    @property
    def inspector(self) -> Inspector:
        if self._inspector is None:
            self._inspector = sa_inspect(self.model)
        return self._inspector

Multi-Level Caching Implementation

  • SQLAlchemy Introspection: Cached per model instance to avoid redundant reflection
  • Schema Modifications: Function-level caching for schema field extraction
  • Query Building: Complex filter parsing memoized - same queries reuse parsed conditions
  • Memory Management: Strategic object reuse through cached inspectors and processors

Pure Function Architecture Pattern

def handle_one_to_many(data: List[Dict], nested_key: str) -> List[Dict]:
    """Pure function - same input always produces same output."""
    # No side effects, no database calls, no global state changes
    # Easy to test: assert handle_one_to_many(input_data) == expected_output

Data Transformation Optimization

  • Common Case Fast Path: Simple operations (no joins, basic pagination) take optimized route
  • Complex Case Comprehensive Path: Multiple joins and nested data use full processing pipeline
  • Pattern: Optimize for the common case, make the complex case possible but not necessarily fast

Quality Improvements

Enhanced Testing and Coverage

  • 99.31% Coverage: Achieved through strategic testing and appropriate pragma usage for defensive programming
  • Layer-Isolated Testing: Foundation layer = pure unit tests, Core Logic = complex fixtures, CRUD layer = database fixtures
  • Protocol Testing: Each protocol can be tested independently without concrete implementations

Layer-Specific Error Handling Strategy

# Foundation layer - low-level issues
class ModelIntrospectionError(Exception): pass

# Core Logic layer - business rule violations
class InvalidFilterError(Exception): pass

# CRUD layer - operation-specific problems
class JoinConfigurationError(Exception): pass

Fail-Fast Validation Pattern

# Validate early, before expensive operations
async def complex_operation(params):
    validate_params(params)  # Fast validation upfront
    result = await expensive_database_operation(params)  # Only if validation passes
    return result

Context-Preserving Error Handling

try:
    result = await complex_operation()
except SQLAlchemyError as e:
    raise CRUDOperationError(f"Failed to execute joined query: {e}") from e
    # Preserves both high-level context AND low-level details

When something fails, the exception type tells you which layer to investigate. Better to catch problems in validation than in a failed database transaction.

Technical Implementation Patterns

Delegation Pattern Implementation

# FastCRUD (Level 5) delegates to execution layer
async def get_multi_joined(self, db, **kwargs):
    return await handle_joined_filters_delegation(self, db, **kwargs)

# Execution (Level 5) delegates to join processing
async def handle_joined_filters_delegation(crud_instance, db, **kwargs):
    validated_params = validate_joined_query_params(**kwargs)
    return await execute_joined_query(db, crud_instance.model, validated_params)

Framework Separation Strategy

  • All FastAPI-specific code lives in Level 6: fastapi_dependencies.py and endpoint/
  • Everything below Level 6 is framework-agnostic: Could work with Flask, Django, GraphQL
  • Clean separation enables: CLI tools, different web frameworks, batch processing - all reusing the same core logic

Pure Function Composition Pattern

def format_response(data):
    return apply_field_selection(
        apply_sorting(
            apply_pagination(data)
        )
    )
# Build complex operations from simple, testable functions

Protocol Dispatch Performance

  • Zero Runtime Overhead: Python protocols compile to regular method calls
  • Type Safety: Full compile-time checking with no performance cost
  • No Magic: No runtime dependency resolution or dynamic dispatch

Breaking Changes

⚠️ None - This release maintains full backward compatibility with 0.18.x

Deprecation Only:

  • fastcrud.paginated module deprecated (still works with warning)

What's Changed

Full Changelog: v0.18.1...v0.19.0

Don't miss a new fastcrud release

NewReleases is sending notifications on new releases.