How to Implement Dependency Injection in FastCRUD Endpoints: A Complete Guide

FastCRUD implements dependency injection by composing FastAPI dependencies across three layers—dependency factories, wrapper helpers, and endpoint creators—allowing you to inject authentication, logging, and custom logic into auto-generated CRUD routes.

Dependency injection in FastCRUD endpoints leverages FastAPI's native Depends system while abstracting the boilerplate of CRUD route creation. The benavlabs/fastcrud library generates routes dynamically but exposes explicit hooks for custom dependencies at every operation level, ensuring your authentication, validation, and middleware logic executes before any database interaction.

Understanding FastCRUD's Dependency Injection Architecture

FastCRUD organizes dependency injection into three distinct layers that work together to resolve dependencies before endpoint execution.

Layer 1: Dependency Factories in fastapi_dependencies.py

The core dependency logic resides in fastcrud/fastapi_dependencies.py. This file contains factory functions that generate callable dependencies based on your configuration.

Auto-Field Injection

The create_auto_field_injector function (lines 27-74) inspects your CreateConfig or UpdateConfig and builds a resolver that automatically populates fields like timestamps or user IDs:

def create_auto_field_injector(config):
    # Returns a function with a dynamic signature that FastAPI can resolve

    # each auto-field becomes a Depends(...)

    return auto_fields_resolver

Dynamic Filter Injection

The create_dynamic_filters function (lines 98-166) generates dependencies for query-parameter filtering based on your FilterConfig:

def create_dynamic_filters(filter_config, column_types):
    # Generates a function that FastAPI calls for each declared filter key.

    # Each key becomes a Query or Depends parameter.

    return filters

These factories dynamically attach __signature__ attributes to the generated functions, allowing FastAPI's dependency injection system to introspect parameters at runtime.

Layer 2: The inject_dependencies Wrapper

The inject_dependencies helper (lines 74-86 in fastapi_dependencies.py) converts your dependency list into FastAPI-compatible Depends objects:

def inject_dependencies(funcs=None):
    if funcs is None:
        return None
    return [Depends(func) for func in funcs]

This wrapper ensures that any callable you provide—whether authentication checks, logging middleware, or custom validators—is properly wrapped before route registration.

Layer 3: EndpointCreator Integration

The EndpointCreator class in fastcrud/endpoint/endpoint_creator.py orchestrates the final injection. The add_routes_to_router method (lines 77-88 and 96-108) accepts specific dependency lists for each CRUD operation:

self.router.add_api_route(
    self._get_endpoint_path(operation="create"),
    self._create_item(),
    methods=["POST"],
    dependencies=inject_dependencies(create_deps),
    # ...

)

The endpoint methods (_create_item, _read_item, _update_item, etc.) already include Depends calls for auto-fields and dynamic filters. Your custom dependencies are added to the route's dependencies parameter, ensuring they execute first.

Implementing Custom Dependencies in FastCRUD Endpoints

Adding Authentication Dependencies

To protect specific CRUD operations, pass your authentication dependency to the relevant parameter in add_routes_to_router:


# app/dependencies.py

from fastapi import Depends, HTTPException, status

def get_current_user(token: str = Depends(...)):
    if token != "secret-token":
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
    return {"id": 1, "username": "alice"}

# app/main.py

from fastapi import FastAPI
from fastcrud import EndpointCreator, FilterConfig
from app.models import Item
from app.schemas import ItemCreate, ItemUpdate, ItemSelect
from app.dependencies import get_current_user
from app.database import async_session

app = FastAPI()

endpoint_creator = EndpointCreator(
    session=async_session,
    model=Item,
    create_schema=ItemCreate,
    update_schema=ItemUpdate,
    select_schema=ItemSelect,
    filter_config=FilterConfig(filters={"name": None, "price__gte": None}),
)

# Inject authentication only on read and update endpoints

endpoint_creator.add_routes_to_router(
    read_deps=[get_current_user],
    update_deps=[get_current_user],
)

app.include_router(endpoint_creator.router, prefix="/items")

FastAPI executes get_current_user before the CRUD logic. You can access the returned user object by modifying the endpoint function signature if needed.

Implementing Request Logging

To log all incoming requests across every CRUD operation, create a logging dependency and apply it universally:


# app/logging.py

import logging
from fastapi import Request, Depends

logger = logging.getLogger("fastcrud")

async def log_request(request: Request):
    logger.info(f"{request.method} {request.url.path}")

# Apply to all operations

endpoint_creator.add_routes_to_router(
    create_deps=[log_request],
    read_deps=[log_request],
    read_multi_deps=[log_request],
    update_deps=[log_request],
    delete_deps=[log_request],
    db_delete_deps=[log_request],
)

Since log_request does not return a value, FastAPI executes it for side effects and continues to the endpoint logic.

Configuring Auto-Field Injection

FastCRUD automatically injects fields like timestamps through the CreateConfig and UpdateConfig classes. These configurations drive the create_auto_field_injector factory:


# app/config.py

from datetime import datetime
from fastcrud.types import CreateConfig

create_cfg = CreateConfig(
    auto_fields={
        "created_at": lambda: datetime.utcnow(),
        "updated_at": lambda: datetime.utcnow(),
    }
)

# Pass configuration to EndpointCreator

endpoint_creator = EndpointCreator(
    session=async_session,
    model=Item,
    create_schema=ItemCreate,
    update_schema=ItemUpdate,
    select_schema=ItemSelect,
    create_config=create_cfg,
)

The factory inspects auto_fields and generates a resolver function with a dynamic signature. FastAPI treats this resolver as a dependency, calling it before the endpoint executes and merging the returned values into the database model.

Advanced Dependency Patterns

Primary Key Injection with apply_model_pk

FastCRUD handles composite primary keys automatically through the apply_model_pk decorator in fastapi_dependencies.py (lines 111-133). This decorator injects path parameters for each primary key column without manual endpoint signature declaration:

@apply_model_pk(**self._primary_keys_types)
async def _read_item(...):
    ...

The decorator is applied internally within _read_item, _update_item, and other single-resource endpoints. It inspects the model's primary key columns, generates the appropriate path parameter types, and ensures FastAPI validates and injects them before your dependency chain executes.

Dynamic Filter Dependencies

The create_dynamic_filters factory generates dependencies that transform query parameters into SQLAlchemy filter expressions. When you provide a FilterConfig to EndpointCreator, the factory builds a function that FastAPI calls with each declared filter:


# Generated dynamically based on FilterConfig

async def dynamic_filters(
    name: Optional[str] = Query(None),
    price__gte: Optional[float] = Query(None)
):
    # Returns dict of SQLAlchemy filter expressions

    return {"name": name, "price__gte": price__gte}

This generated function is injected into read_multi endpoints via Depends, allowing you to filter collections without writing custom endpoint logic.

Summary

  • Three-layer architecture: FastCRUD uses dependency factories (create_auto_field_injector, create_dynamic_filters), the inject_dependencies wrapper, and EndpointCreator to compose FastAPI dependencies.
  • Custom dependencies: Pass callables to add_routes_to_router parameters (create_deps, read_deps, etc.) to inject authentication, logging, or validation logic.
  • Auto-injection: CreateConfig and UpdateConfig drive automatic field population through dynamically generated dependency signatures.
  • Primary key handling: The apply_model_pk decorator automatically injects composite primary key path parameters without manual endpoint configuration.
  • Source locations: Core logic lives in fastcrud/fastapi_dependencies.py (factories and wrappers) and fastcrud/endpoint/endpoint_creator.py (route registration).

Frequently Asked Questions

How do I add authentication to only specific CRUD operations in FastCRUD?

Pass your authentication dependency to the specific operation parameters in add_routes_to_router. For example, set read_deps=[get_current_user] and update_deps=[get_current_user] while leaving create_deps and delete_deps empty. FastCRUD will only protect the read and update endpoints, allowing public access to create and delete operations.

Can I use async dependencies with FastCRUD endpoints?

Yes. FastCRUD passes all dependencies through inject_dependencies, which wraps them in FastAPI's Depends. FastAPI natively supports both sync and async callables in dependencies. Whether your dependency is a standard function or an async def coroutine, FastAPI will await it appropriately before executing the CRUD endpoint logic.

What is the difference between auto_fields and custom dependencies in FastCRUD?

auto_fields in CreateConfig or UpdateConfig are specifically designed to automatically populate model columns (like timestamps or user IDs) before database insertion. FastCRUD generates a hidden dependency for these fields using create_auto_field_injector. Custom dependencies passed to add_routes_to_router are user-defined callables for cross-cutting concerns like authentication, logging, or transaction management that execute before the endpoint logic but don't automatically map to model fields.

How does FastCRUD handle composite primary keys in dependency injection?

FastCRUD uses the apply_model_pk decorator defined in fastcrud/fastapi_dependencies.py (lines 111-133) to automatically inject path parameters for composite primary keys. When EndpointCreator builds single-resource endpoints like _read_item or _update_item, it applies this decorator, which inspects the model's primary key columns and generates the appropriate path parameter signatures. This eliminates the need to manually declare each primary key field in your endpoint signatures while maintaining full FastAPI validation and OpenAPI documentation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →