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

> Learn how to implement dependency injection in FastCRUD endpoints. This guide shows how to inject auth, logging, and custom logic into auto-generated CRUD routes.

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

---

**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`](https://github.com/benavlabs/fastcrud/blob/main/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:

```python
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`:

```python
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`](https://github.com/benavlabs/fastcrud/blob/main/fastapi_dependencies.py)) converts your dependency list into FastAPI-compatible `Depends` objects:

```python
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`](https://github.com/benavlabs/fastcrud/blob/main/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:

```python
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`:

```python

# 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"}

```

```python

# 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:

```python

# 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}")

```

```python

# 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:

```python

# 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(),
    }
)

```

```python

# 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`](https://github.com/benavlabs/fastcrud/blob/main/fastapi_dependencies.py) (lines 111-133). This decorator injects path parameters for each primary key column without manual endpoint signature declaration:

```python
@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:

```python

# 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`](https://github.com/benavlabs/fastcrud/blob/main/fastcrud/fastapi_dependencies.py) (factories and wrappers) and [`fastcrud/endpoint/endpoint_creator.py`](https://github.com/benavlabs/fastcrud/blob/main/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`](https://github.com/benavlabs/fastcrud/blob/main/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.