{"config":{"separator":"[\\s\\-_,:!=\\[\\]()\\\\\"`/]+|\\.(?!\\d)"},"items":[{"location":"","level":1,"title":"FastAPI Toolsets","text":"
A modular collection of production-ready utilities for FastAPI. Install only what you need — from async CRUD and database helpers to CLI tooling, Prometheus metrics, and pytest fixtures. Each module is independently installable via optional extras, keeping your dependency footprint minimal.
Documentation: https://fastapi-toolsets.d3vyce.fr
Source Code: https://github.com/d3vyce/fastapi-toolsets
","path":["FastAPI Toolsets"],"tags":[]},{"location":"#installation","level":2,"title":"Installation","text":"The base package includes the core modules (CRUD, database, schemas, exceptions, fixtures, dependencies, logging):
uv add fastapi-toolsets\n Install only the extras you need:
uv add \"fastapi-toolsets[cli]\" # CLI (typer)\nuv add \"fastapi-toolsets[metrics]\" # Prometheus metrics (prometheus_client)\nuv add \"fastapi-toolsets[pytest]\" # Pytest helpers (httpx, pytest-xdist)\n Or install everything:
uv add \"fastapi-toolsets[all]\"\n","path":["FastAPI Toolsets"],"tags":[]},{"location":"#features","level":2,"title":"Features","text":"","path":["FastAPI Toolsets"],"tags":[]},{"location":"#core","level":3,"title":"Core","text":"CrudFactory, built-in search with relationship traversalPathDependency, BodyDependency) for automatic DB lookups from path or body parametersResponse, PaginatedResponse, and PydanticBaseconfigure_logging and get_loggerpytest-xdist support, and table cleanup utilitiesMIT License - see LICENSE for details.
","path":["FastAPI Toolsets"],"tags":[]},{"location":"#contributing","level":2,"title":"Contributing","text":"Contributions are welcome! Please feel free to submit issues and pull requests.
","path":["FastAPI Toolsets"],"tags":[]},{"location":"module/cli/","level":1,"title":"CLI","text":"Typer-based command-line interface for managing your FastAPI application, with built-in fixture commands integration.
","path":["Module","CLI"],"tags":[]},{"location":"module/cli/#installation","level":2,"title":"Installation","text":"uvpipuv add \"fastapi-toolsets[cli]\"\n pip install \"fastapi-toolsets[cli]\"\n","path":["Module","CLI"],"tags":[]},{"location":"module/cli/#overview","level":2,"title":"Overview","text":"The cli module provides a manager entry point built with Typer. It allow custom commands to be added in addition of the fixture commands when a FixtureRegistry and a database context are configured.
Configure the CLI in your pyproject.toml:
[tool.fastapi-toolsets]\ncli = \"myapp.cli:cli\" # Custom Typer app\nfixtures = \"myapp.fixtures:registry\" # FixtureRegistry instance\ndb_context = \"myapp.db:db_context\" # Async context manager for sessions\n All fields are optional. Without configuration, the manager command still works but no command are available.
# Manager commands\nmanager --help\n\n Usage: manager [OPTIONS] COMMAND [ARGS]...\n\n FastAPI utilities CLI.\n\n╭─ Options ────────────────────────────────────────────────────────────────────────╮\n│ --install-completion Install completion for the current shell. │\n│ --show-completion Show completion for the current shell, to copy it │\n│ or customize the installation. │\n│ --help Show this message and exit. │\n╰──────────────────────────────────────────────────────────────────────────────────╯\n╭─ Commands ───────────────────────────────────────────────────────────────────────╮\n│ check-db │\n│ fixtures Manage database fixtures. │\n╰──────────────────────────────────────────────────────────────────────────────────╯\n\n# Fixtures commands\nmanager fixtures --help\n\n Usage: manager fixtures [OPTIONS] COMMAND [ARGS]...\n\n Manage database fixtures.\n\n╭─ Options ────────────────────────────────────────────────────────────────────────╮\n│ --help Show this message and exit. │\n╰──────────────────────────────────────────────────────────────────────────────────╯\n╭─ Commands ───────────────────────────────────────────────────────────────────────╮\n│ list List all registered fixtures. │\n│ load Load fixtures into the database. │\n╰──────────────────────────────────────────────────────────────────────────────────╯\n","path":["Module","CLI"],"tags":[]},{"location":"module/cli/#custom-cli","level":2,"title":"Custom CLI","text":"You can extend the CLI by providing your own Typer app. The manager entry point will merge your app's commands with the built-in ones:
# myapp/cli.py\nimport typer\n\ncli = typer.Typer()\n\n@cli.command()\ndef hello():\n print(\"Hello from my app!\")\n [tool.fastapi-toolsets]\ncli = \"myapp.cli:cli\"\n API Reference
","path":["Module","CLI"],"tags":[]},{"location":"module/crud/","level":1,"title":"CRUD","text":"Generic async CRUD operations for SQLAlchemy models with search, pagination, and many-to-many support.
Info
This module has been coded and tested to be compatible with PostgreSQL only.
","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#overview","level":2,"title":"Overview","text":"The crud module provides AsyncCrud, an abstract base class with a full suite of async database operations, and CrudFactory, a convenience function to instantiate it for a given model.
from fastapi_toolsets.crud import CrudFactory\nfrom myapp.models import User\n\nUserCrud = CrudFactory(model=User)\n CrudFactory dynamically creates a class named AsyncUserCrud with User as its model.
# Create\nuser = await UserCrud.create(session=session, obj=UserCreateSchema(username=\"alice\"))\n\n# Get one (raises NotFoundError if not found)\nuser = await UserCrud.get(session=session, filters=[User.id == user_id])\n\n# Get first or None\nuser = await UserCrud.first(session=session, filters=[User.email == email])\n\n# Get multiple\nusers = await UserCrud.get_multi(session=session, filters=[User.is_active == True])\n\n# Update\nuser = await UserCrud.update(session=session, obj=UserUpdateSchema(username=\"bob\"), filters=[User.id == user_id])\n\n# Delete\nawait UserCrud.delete(session=session, filters=[User.id == user_id])\n\n# Count / exists\ncount = await UserCrud.count(session=session, filters=[User.is_active == True])\nexists = await UserCrud.exists(session=session, filters=[User.email == email])\n","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#pagination","level":2,"title":"Pagination","text":"Added in v1.1 (only offset_pagination via paginate if <v1.1)
Two pagination strategies are available. Both return a PaginatedResponse but differ in how they navigate through results.
offset_paginate cursor_paginate Total count Yes No Jump to arbitrary page Yes No Performance on deep pages Degrades Constant Stable under concurrent inserts No Yes Search compatible Yes Yes Use case Admin panels, numbered pagination Feeds, APIs, infinite scroll","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#offset-pagination","level":3,"title":"Offset pagination","text":"@router.get(\n \"\",\n response_model=PaginatedResponse[User],\n)\nasync def get_users(\n session: SessionDep,\n items_per_page: int = 50,\n page: int = 1,\n):\n return await crud.UserCrud.offset_paginate(\n session=session,\n items_per_page=items_per_page,\n page=page,\n )\n The offset_paginate method returns a PaginatedResponse whose pagination field is an OffsetPagination object:
{\n \"status\": \"SUCCESS\",\n \"data\": [\"...\"],\n \"pagination\": {\n \"total_count\": 100,\n \"page\": 1,\n \"items_per_page\": 20,\n \"has_more\": true\n }\n}\n Deprecated: paginate
The paginate function is a backward-compatible alias for offset_paginate. This function is deprecated and will be removed in v2.0.
@router.get(\n \"\",\n response_model=PaginatedResponse[UserRead],\n)\nasync def list_users(\n session: SessionDep,\n cursor: str | None = None,\n items_per_page: int = 20,\n):\n return await UserCrud.cursor_paginate(\n session=session,\n cursor=cursor,\n items_per_page=items_per_page,\n )\n The cursor_paginate method returns a PaginatedResponse whose pagination field is a CursorPagination object:
{\n \"status\": \"SUCCESS\",\n \"data\": [\"...\"],\n \"pagination\": {\n \"next_cursor\": \"eyJ2YWx1ZSI6ICIzZjQ3YWM2OS0uLi4ifQ==\",\n \"prev_cursor\": null,\n \"items_per_page\": 20,\n \"has_more\": true\n }\n}\n Pass next_cursor as the cursor query parameter on the next request to advance to the next page. prev_cursor is set on pages 2+ and points back to the first item of the current page. Both are null when there is no adjacent page.
The cursor column is set once on CrudFactory via the cursor_column parameter. It must be monotonically ordered for stable results:
Warning
Random UUID v4 PKs are not suitable as cursor columns because their ordering is non-deterministic.
Note
cursor_column is required. Calling cursor_paginate on a CRUD class that has no cursor_column configured raises a ValueError.
The cursor value is base64-encoded when returned to the client and decoded back to the correct Python type on the next request. The following SQLAlchemy column types are supported:
SQLAlchemy type Python typeInteger, BigInteger, SmallInteger int Uuid uuid.UUID DateTime datetime.datetime Date datetime.date Float, Numeric decimal.Decimal # Paginate by the primary key\nPostCrud = CrudFactory(model=Post, cursor_column=Post.id)\n\n# Paginate by a timestamp column instead\nPostCrud = CrudFactory(model=Post, cursor_column=Post.created_at)\n","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#search","level":2,"title":"Search","text":"Declare searchable fields on the CRUD class. Relationship traversal is supported via tuples:
PostCrud = CrudFactory(\n model=Post,\n searchable_fields=[\n Post.title,\n Post.content,\n (Post.author, User.username), # search across relationship\n ],\n)\n This allows searching with both offset_paginate and cursor_paginate:
@router.get(\n \"\",\n response_model=PaginatedResponse[User],\n)\nasync def get_users(\n session: SessionDep,\n items_per_page: int = 50,\n page: int = 1,\n search: str | None = None,\n):\n return await crud.UserCrud.offset_paginate(\n session=session,\n items_per_page=items_per_page,\n page=page,\n search=search,\n )\n @router.get(\n \"\",\n response_model=PaginatedResponse[User],\n)\nasync def get_users(\n session: SessionDep,\n cursor: str | None = None,\n items_per_page: int = 50,\n search: str | None = None,\n):\n return await crud.UserCrud.cursor_paginate(\n session=session,\n items_per_page=items_per_page,\n cursor=cursor,\n search=search,\n )\n","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#relationship-loading","level":2,"title":"Relationship loading","text":"Added in v1.1
By default, SQLAlchemy relationships are not loaded unless explicitly requested. Instead of using lazy=\"selectin\" on model definitions (which is implicit and applies globally), define a default_load_options on the CRUD class to control loading strategy explicitly.
Warning
Avoid using lazy=\"selectin\" on model relationships. It fires silently on every query, cannot be disabled per-call, and can cause unexpected cascading loads through deep relationship chains. Use default_load_options instead.
from sqlalchemy.orm import selectinload\n\nArticleCrud = CrudFactory(\n model=Article,\n default_load_options=[\n selectinload(Article.category),\n selectinload(Article.tags),\n ],\n)\n default_load_options applies automatically to all read operations (get, first, get_multi, offset_paginate, cursor_paginate). When load_options is passed at call-site, it fully replaces default_load_options for that query — giving you precise per-call control:
# Only loads category, tags are not loaded\narticle = await ArticleCrud.get(\n session=session,\n filters=[Article.id == article_id],\n load_options=[selectinload(Article.category)],\n)\n\n# Loads nothing — useful for write-then-refresh flows or lightweight checks\narticles = await ArticleCrud.get_multi(session=session, load_options=[])\n","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#many-to-many-relationships","level":2,"title":"Many-to-many relationships","text":"Use m2m_fields to map schema fields containing lists of IDs to SQLAlchemy relationships. The CRUD class resolves and validates all IDs before persisting:
PostCrud = CrudFactory(\n model=Post,\n m2m_fields={\"tag_ids\": Post.tags},\n)\n\npost = await PostCrud.create(session=session, obj=PostCreateSchema(title=\"Hello\", tag_ids=[1, 2, 3]))\n","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#upsert","level":2,"title":"Upsert","text":"Atomic INSERT ... ON CONFLICT DO UPDATE using PostgreSQL:
await UserCrud.upsert(\n session=session,\n obj=UserCreateSchema(email=\"alice@example.com\", username=\"alice\"),\n index_elements=[User.email],\n set_={\"username\"},\n)\n","path":["Module","CRUD"],"tags":[]},{"location":"module/crud/#schema-typed-response-serialization","level":2,"title":"schema — typed response serialization","text":"Added in v1.1
Pass a Pydantic schema class to create, get, update, or offset_paginate to serialize the result directly into that schema and wrap it in a Response[schema] or PaginatedResponse[schema]:
class UserRead(PydanticBase):\n id: UUID\n username: str\n\n@router.get(\n \"/{uuid}\",\n responses=generate_error_responses(NotFoundError),\n)\nasync def get_user(session: SessionDep, uuid: UUID) -> Response[UserRead]:\n return await crud.UserCrud.get(\n session=session,\n filters=[User.id == uuid],\n schema=UserRead,\n )\n\n@router.get(\"\")\nasync def list_users(session: SessionDep, page: int = 1) -> PaginatedResponse[UserRead]:\n return await crud.UserCrud.offset_paginate(\n session=session,\n page=page,\n schema=UserRead,\n )\n The schema must have from_attributes=True (or inherit from PydanticBase) so it can be built from SQLAlchemy model instances.
Deprecated: as_response
The as_response=True parameter is deprecated and will be removed in v2.0. Replace it with schema=YourSchema.
API Reference
","path":["Module","CRUD"],"tags":[]},{"location":"module/db/","level":1,"title":"DB","text":"SQLAlchemy async session management with transactions, table locking, and row-change polling.
Info
This module has been coded and tested to be compatible with PostgreSQL only.
","path":["Module","DB"],"tags":[]},{"location":"module/db/#overview","level":2,"title":"Overview","text":"The db module provides helpers to create FastAPI dependencies and context managers for AsyncSession, along with utilities for nested transactions, table lock and polling for row changes.
Use create_db_dependency to create a FastAPI dependency that yields a session and auto-commits on success:
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker\nfrom fastapi_toolsets.db import create_db_dependency\n\nengine = create_async_engine(url=\"postgresql+asyncpg://...\", future=True)\nsession_maker = async_sessionmaker(bind=engine, expire_on_commit=False)\n\nget_db = create_db_dependency(session_maker=session_maker)\n\n@router.get(\"/users\")\nasync def list_users(session: AsyncSession = Depends(get_db)):\n ...\n","path":["Module","DB"],"tags":[]},{"location":"module/db/#session-context-manager","level":2,"title":"Session context manager","text":"Use create_db_context for sessions outside request handlers (e.g. background tasks, CLI commands):
from fastapi_toolsets.db import create_db_context\n\ndb_context = create_db_context(session_maker=session_maker)\n\nasync def seed():\n async with db_context() as session:\n ...\n","path":["Module","DB"],"tags":[]},{"location":"module/db/#nested-transactions","level":2,"title":"Nested transactions","text":"get_transaction handles savepoints automatically, allowing safe nesting:
from fastapi_toolsets.db import get_transaction\n\nasync def create_user_with_role(session=session):\n async with get_transaction(session=session):\n ...\n async with get_transaction(session=session): # uses savepoint\n ...\n","path":["Module","DB"],"tags":[]},{"location":"module/db/#table-locking","level":2,"title":"Table locking","text":"lock_tables acquires PostgreSQL table-level locks before executing critical sections:
from fastapi_toolsets.db import lock_tables\n\nasync with lock_tables(session=session, tables=[User], mode=\"EXCLUSIVE\"):\n # No other transaction can modify User until this block exits\n ...\n Available lock modes are defined in LockMode: ACCESS_SHARE, ROW_SHARE, ROW_EXCLUSIVE, SHARE_UPDATE_EXCLUSIVE, SHARE, SHARE_ROW_EXCLUSIVE, EXCLUSIVE, ACCESS_EXCLUSIVE.
wait_for_row_change polls a row until a specific column changes value, useful for waiting on async side effects:
from fastapi_toolsets.db import wait_for_row_change\n\n# Wait up to 30s for order.status to change\nawait wait_for_row_change(\n session=session,\n model=Order,\n pk_value=order_id,\n columns=[Order.status],\n interval=1.0,\n timeout=30.0,\n)\n API Reference
","path":["Module","DB"],"tags":[]},{"location":"module/dependencies/","level":1,"title":"Dependencies","text":"FastAPI dependency factories for automatic model resolution from path and body parameters.
","path":["Module","Dependencies"],"tags":[]},{"location":"module/dependencies/#overview","level":2,"title":"Overview","text":"The dependencies module provides two factory functions that create FastAPI dependencies to fetch a model instance from the database automatically — either from a path parameter or from a request body field — and inject it directly into your route handler.
PathDependency","text":"PathDependency resolves a model from a URL path parameter and injects it into the route handler. Raises NotFoundError automatically if the record does not exist.
from fastapi_toolsets.dependencies import PathDependency\n\nUserDep = PathDependency(model=User, field=User.id, session_dep=get_db)\n\n@router.get(\"/users/{user_id}\")\nasync def get_user(user: User = UserDep):\n return user\n By default the parameter name is inferred from the field (user_id for User.id). You can override it:
UserDep = PathDependency(model=User, field=User.id, session_dep=get_db, param_name=\"id\")\n\n@router.get(\"/users/{id}\")\nasync def get_user(user: User = UserDep):\n return user\n","path":["Module","Dependencies"],"tags":[]},{"location":"module/dependencies/#bodydependency","level":2,"title":"BodyDependency","text":"BodyDependency resolves a model from a field in the request body. Useful when a body contains a foreign key and you want the full object injected:
from fastapi_toolsets.dependencies import BodyDependency\n\nRoleDep = BodyDependency(model=Role, field=Role.id, session_dep=get_db, body_field=\"role_id\")\n\n@router.post(\"/users\")\nasync def create_user(body: UserCreateSchema, role: Role = RoleDep):\n user = User(username=body.username, role=role)\n ...\n API Reference
","path":["Module","Dependencies"],"tags":[]},{"location":"module/exceptions/","level":1,"title":"Exceptions","text":"Structured API exceptions with consistent error responses and automatic OpenAPI documentation.
","path":["Module","Exceptions"],"tags":[]},{"location":"module/exceptions/#overview","level":2,"title":"Overview","text":"The exceptions module provides a set of pre-built HTTP exceptions and a FastAPI exception handler that formats all errors — including validation errors — into a uniform ErrorResponse.
Register the exception handlers on your FastAPI app at startup:
from fastapi import FastAPI\nfrom fastapi_toolsets.exceptions import init_exceptions_handlers\n\napp = FastAPI()\ninit_exceptions_handlers(app=app)\n This registers handlers for:
ApiException — all custom exceptions belowRequestValidationError — Pydantic request validation (422)ResponseValidationError — Pydantic response validation (422)Exception — unhandled errors (500)UnauthorizedError 401 Unauthorized ForbiddenError 403 Forbidden NotFoundError 404 Not found ConflictError 409 Conflict NoSearchableFieldsError 400 No searchable fields from fastapi_toolsets.exceptions import NotFoundError\n\n@router.get(\"/users/{id}\")\nasync def get_user(id: int, session: AsyncSession = Depends(get_db)):\n user = await UserCrud.first(session=session, filters=[User.id == id])\n if not user:\n raise NotFoundError\n return user\n","path":["Module","Exceptions"],"tags":[]},{"location":"module/exceptions/#custom-exceptions","level":2,"title":"Custom exceptions","text":"Subclass ApiException and define an api_error class variable:
from fastapi_toolsets.exceptions import ApiException\nfrom fastapi_toolsets.schemas import ApiError\n\nclass PaymentRequiredError(ApiException):\n api_error = ApiError(\n code=402,\n msg=\"Payment required\",\n desc=\"Your subscription has expired.\",\n err_code=\"PAYMENT_REQUIRED\",\n )\n","path":["Module","Exceptions"],"tags":[]},{"location":"module/exceptions/#openapi-response-documentation","level":2,"title":"OpenAPI response documentation","text":"Use generate_error_responses to add error schemas to your endpoint's OpenAPI spec:
from fastapi_toolsets.exceptions import generate_error_responses, NotFoundError, ForbiddenError\n\n@router.get(\n \"/users/{id}\",\n responses=generate_error_responses(NotFoundError, ForbiddenError),\n)\nasync def get_user(...): ...\n Info
The pydantic validation error is automatically added by FastAPI.
API Reference
","path":["Module","Exceptions"],"tags":[]},{"location":"module/fixtures/","level":1,"title":"Fixtures","text":"Dependency-aware database seeding with context-based loading strategies.
","path":["Module","Fixtures"],"tags":[]},{"location":"module/fixtures/#overview","level":2,"title":"Overview","text":"The fixtures module lets you define named fixtures with dependencies between them, then load them into the database in the correct order. Fixtures can be scoped to contexts (e.g. base data, testing data) so that only the relevant ones are loaded for each environment.
from fastapi_toolsets.fixtures import FixtureRegistry, Context\n\nfixtures = FixtureRegistry()\n\n@fixtures.register\ndef roles():\n return [\n Role(id=1, name=\"admin\"),\n Role(id=2, name=\"user\"),\n ]\n\n@fixtures.register(depends_on=[\"roles\"], contexts=[Context.TESTING])\ndef test_users():\n return [\n User(id=1, username=\"alice\", role_id=1),\n User(id=2, username=\"bob\", role_id=2),\n ]\n Dependencies declared via depends_on are resolved topologically — roles will always be loaded before test_users.
By context with load_fixtures_by_context:
from fastapi_toolsets.fixtures import load_fixtures_by_context\n\nasync with db_context() as session:\n await load_fixtures_by_context(session=session, registry=fixtures, context=Context.TESTING)\n Directly with load_fixtures:
from fastapi_toolsets.fixtures import load_fixtures\n\nasync with db_context() as session:\n await load_fixtures(session=session, registry=fixtures)\n","path":["Module","Fixtures"],"tags":[]},{"location":"module/fixtures/#contexts","level":2,"title":"Contexts","text":"Context is an enum with predefined values:
Context.BASE Core data required in all environments Context.TESTING Data only loaded during tests Context.PRODUCTION Data only loaded in production A fixture with no contexts defined takes Context.BASE by default.
LoadStrategy controls how the fixture loader handles rows that already exist:
LoadStrategy.INSERT Insert only, fail on duplicates LoadStrategy.UPSERT Insert or update on conflict LoadStrategy.SKIP Skip rows that already exist","path":["Module","Fixtures"],"tags":[]},{"location":"module/fixtures/#merging-registries","level":2,"title":"Merging registries","text":"Split fixtures definitions across modules and merge them:
from myapp.fixtures.dev import dev_fixtures\nfrom myapp.fixtures.prod import prod_fixtures\n\nfixtures = fixturesRegistry()\nfixtures.include_registry(registry=dev_fixtures)\nfixtures.include_registry(registry=prod_fixtures)\n\n## Pytest integration\n\nUse [`register_fixtures`](../reference/pytest.md#fastapi_toolsets.pytest.plugin.register_fixtures) to expose each fixture in your registry as an injectable pytest fixture named `fixture_{name}` by default:\n\n```python\n# conftest.py\nimport pytest\nfrom fastapi_toolsets.pytest import create_db_session, register_fixtures\nfrom app.fixtures import registry\nfrom app.models import Base\n\nDATABASE_URL = \"postgresql+asyncpg://user:pass@localhost/test_db\"\n\n@pytest.fixture\nasync def db_session():\n async with create_db_session(database_url=DATABASE_URL, base=Base, cleanup=True) as session:\n yield session\n\nregister_fixtures(registry=registry, namespace=globals())\n # test_users.py\nasync def test_user_can_login(fixture_users: list[User], fixture_roles: list[Role]):\n ...\n The load order is resolved automatically from the depends_on declarations in your registry. Each generated fixture receives db_session as a dependency and returns the list of loaded model instances.
Fixtures can be triggered from the CLI. See the CLI module for setup instructions.
API Reference
","path":["Module","Fixtures"],"tags":[]},{"location":"module/logger/","level":1,"title":"Logger","text":"Lightweight logging utilities with consistent formatting and uvicorn integration.
","path":["Module","Logger"],"tags":[]},{"location":"module/logger/#overview","level":2,"title":"Overview","text":"The logger module provides two helpers: one to configure the root logger (and uvicorn loggers) at startup, and one to retrieve a named logger anywhere in your codebase.
Call configure_logging once at application startup:
from fastapi_toolsets.logger import configure_logging\n\nconfigure_logging(level=\"INFO\")\n This sets up a stdout handler with a consistent format and also configures uvicorn's access and error loggers so all log output shares the same style.
","path":["Module","Logger"],"tags":[]},{"location":"module/logger/#getting-a-logger","level":2,"title":"Getting a logger","text":"from fastapi_toolsets.logger import get_logger\n\nlogger = get_logger(name=__name__)\nlogger.info(\"User created\")\n When called without arguments, get_logger auto-detects the caller's module name via frame inspection:
# Equivalent to get_logger(name=__name__)\nlogger = get_logger()\n API Reference
","path":["Module","Logger"],"tags":[]},{"location":"module/metrics/","level":1,"title":"Metrics","text":"Prometheus metrics integration with a decorator-based registry and multi-process support.
","path":["Module","Metrics"],"tags":[]},{"location":"module/metrics/#installation","level":2,"title":"Installation","text":"uvpipuv add \"fastapi-toolsets[metrics]\"\n pip install \"fastapi-toolsets[metrics]\"\n","path":["Module","Metrics"],"tags":[]},{"location":"module/metrics/#overview","level":2,"title":"Overview","text":"The metrics module provides a MetricsRegistry to declare Prometheus metrics with decorators, and an init_metrics function to mount a /metrics endpoint on your FastAPI app.
from fastapi import FastAPI\nfrom fastapi_toolsets.metrics import MetricsRegistry, init_metrics\n\napp = FastAPI()\nmetrics = MetricsRegistry()\n\ninit_metrics(app=app, registry=metrics)\n This mounts the /metrics endpoint that Prometheus can scrape.
Providers are called once at startup and register metrics that are updated externally (e.g. counters, histograms):
from prometheus_client import Counter, Histogram\n\n@metrics.register\ndef http_requests():\n return Counter(\"http_requests_total\", \"Total HTTP requests\", [\"method\", \"status\"])\n\n@metrics.register\ndef request_duration():\n return Histogram(\"request_duration_seconds\", \"Request duration\")\n","path":["Module","Metrics"],"tags":[]},{"location":"module/metrics/#collectors","level":3,"title":"Collectors","text":"Collectors are called on every scrape. Use them for metrics that reflect current state (e.g. gauges):
@metrics.register(collect=True)\ndef queue_depth():\n gauge = Gauge(\"queue_depth\", \"Current queue depth\")\n gauge.set(get_current_queue_depth())\n","path":["Module","Metrics"],"tags":[]},{"location":"module/metrics/#merging-registries","level":2,"title":"Merging registries","text":"Split metrics definitions across modules and merge them:
from myapp.metrics.http import http_metrics\nfrom myapp.metrics.db import db_metrics\n\nmetrics = MetricsRegistry()\nmetrics.include_registry(registry=http_metrics)\nmetrics.include_registry(registry=db_metrics)\n","path":["Module","Metrics"],"tags":[]},{"location":"module/metrics/#multi-process-mode","level":2,"title":"Multi-process mode","text":"Multi-process support is enabled automatically when the PROMETHEUS_MULTIPROC_DIR environment variable is set. No code changes are required.
Environment variable name
The correct variable is PROMETHEUS_MULTIPROC_DIR (not PROMETHEUS_MULTIPROCESS_DIR).
API Reference
","path":["Module","Metrics"],"tags":[]},{"location":"module/pytest/","level":1,"title":"Pytest","text":"Testing helpers for FastAPI applications with async client, database sessions, and parallel worker support.
","path":["Module","Pytest"],"tags":[]},{"location":"module/pytest/#installation","level":2,"title":"Installation","text":"uvpipuv add \"fastapi-toolsets[pytest]\"\n pip install \"fastapi-toolsets[pytest]\"\n","path":["Module","Pytest"],"tags":[]},{"location":"module/pytest/#overview","level":2,"title":"Overview","text":"The pytest module provides utilities for setting up async test clients, managing test database sessions, and supporting parallel test execution with pytest-xdist.
Use create_async_client to get an httpx.AsyncClient configured for your FastAPI app:
from fastapi_toolsets.pytest import create_async_client\n\n@pytest.fixture\nasync def http_client(db_session):\n async def _override_get_db():\n yield db_session\n\n async with create_async_client(\n app=app,\n base_url=\"http://127.0.0.1/api/v1\",\n dependency_overrides={get_db: _override_get_db},\n ) as c:\n yield c\n","path":["Module","Pytest"],"tags":[]},{"location":"module/pytest/#database-sessions-in-tests","level":2,"title":"Database sessions in tests","text":"Use create_db_session to create an isolated AsyncSession for a test:
from fastapi_toolsets.pytest import create_db_session, create_worker_database\n\n@pytest.fixture(scope=\"session\")\nasync def worker_db_url():\n async with create_worker_database(\n database_url=str(settings.SQLALCHEMY_DATABASE_URI)\n ) as url:\n yield url\n\n\n@pytest.fixture\nasync def db_session(worker_db_url):\n async with create_db_session(\n database_url=worker_db_url, base=Base, cleanup=True\n ) as session:\n yield session\n Info
In this example, the database is reset between each test using the argument cleanup=True.
The examples above are already compatible with parallel test execution with pytest-xdist.
If you want to manually clean up a database you can use cleanup_tables, this will truncates all tables between tests for fast isolation:
from fastapi_toolsets.pytest import cleanup_tables\n\n@pytest.fixture(autouse=True)\nasync def clean(db_session):\n yield\n await cleanup_tables(session=db_session, base=Base)\n API Reference
","path":["Module","Pytest"],"tags":[]},{"location":"module/schemas/","level":1,"title":"Schemas","text":"Standardized Pydantic response models for consistent API responses across your FastAPI application.
","path":["Module","Schemas"],"tags":[]},{"location":"module/schemas/#overview","level":2,"title":"Overview","text":"The schemas module provides generic response wrappers that enforce a uniform response structure. All models use from_attributes=True for ORM compatibility and validate_assignment=True for runtime type safety.
Response[T]","text":"The most common wrapper for a single resource response.
from fastapi_toolsets.schemas import Response\n\n@router.get(\"/users/{id}\")\nasync def get_user(user: User = UserDep) -> Response[UserSchema]:\n return Response(data=user, message=\"User retrieved\")\n","path":["Module","Schemas"],"tags":[]},{"location":"module/schemas/#paginatedresponset","level":3,"title":"PaginatedResponse[T]","text":"Wraps a list of items with pagination metadata.
from fastapi_toolsets.schemas import PaginatedResponse, Pagination\n\n@router.get(\"/users\")\nasync def list_users() -> PaginatedResponse[UserSchema]:\n return PaginatedResponse(\n data=users,\n pagination=Pagination(\n total_count=100,\n items_per_page=10,\n page=1,\n has_more=True,\n ),\n )\n","path":["Module","Schemas"],"tags":[]},{"location":"module/schemas/#errorresponse","level":3,"title":"ErrorResponse","text":"Returned automatically by the exceptions handler.
API Reference
","path":["Module","Schemas"],"tags":[]},{"location":"reference/cli/","level":1,"title":"cli","text":"Here's the reference for the CLI configuration helpers used to load settings from pyproject.toml.
You can import them directly from fastapi_toolsets.cli.config:
from fastapi_toolsets.cli.config import (\n import_from_string,\n get_config_value,\n get_fixtures_registry,\n get_db_context,\n get_custom_cli,\n)\n","path":["Reference","cli"],"tags":[]},{"location":"reference/cli/#fastapi_toolsets.cli.config.import_from_string","level":2,"title":"fastapi_toolsets.cli.config.import_from_string(import_path)","text":"Import an object from a dotted string path.
Parameters:
Name Type Description Defaultimport_path str Import path in \"module.submodule:attribute\" format
Returns:
Type DescriptionAny The imported attribute
Raises:
Type DescriptionBadParameter If the import path is invalid or import fails
","path":["Reference","cli"],"tags":[]},{"location":"reference/cli/#fastapi_toolsets.cli.config.get_config_value","level":2,"title":"fastapi_toolsets.cli.config.get_config_value(key, required=False)","text":"get_config_value(key: str, required: Literal[True]) -> Any\nget_config_value(\n key: str, required: bool = False\n) -> Any | None\n Get a configuration value from pyproject.toml.
Parameters:
Name Type Description Defaultkey str The configuration key in [tool.fastapi-toolsets].
requiredrequired bool If True, raises an error when the key is missing.
False Returns:
Type DescriptionAny | None The configuration value, or None if not found and not required.
Raises:
Type DescriptionBadParameter If required=True and the key is missing.
","path":["Reference","cli"],"tags":[]},{"location":"reference/cli/#fastapi_toolsets.cli.config.get_fixtures_registry","level":2,"title":"fastapi_toolsets.cli.config.get_fixtures_registry()","text":"Import and return the fixtures registry from config.
","path":["Reference","cli"],"tags":[]},{"location":"reference/cli/#fastapi_toolsets.cli.config.get_db_context","level":2,"title":"fastapi_toolsets.cli.config.get_db_context()","text":"Import and return the db_context function from config.
","path":["Reference","cli"],"tags":[]},{"location":"reference/cli/#fastapi_toolsets.cli.config.get_custom_cli","level":2,"title":"fastapi_toolsets.cli.config.get_custom_cli()","text":"Import and return the custom CLI Typer instance from config.
","path":["Reference","cli"],"tags":[]},{"location":"reference/cli/#fastapi_toolsets.cli.utils.async_command","level":2,"title":"fastapi_toolsets.cli.utils.async_command(func)","text":"Decorator to run an async function as a sync CLI command.
Example@fixture_cli.command(\"load\")\n@async_command\nasync def load(ctx: typer.Context) -> None:\n async with get_db_context() as session:\n await load_fixtures(session, registry)\n","path":["Reference","cli"],"tags":[]},{"location":"reference/crud/","level":1,"title":"crud","text":"Here's the reference for the CRUD classes, factory, and search utilities.
You can import the main symbols from fastapi_toolsets.crud:
from fastapi_toolsets.crud import CrudFactory, AsyncCrud\nfrom fastapi_toolsets.crud.search import SearchConfig, get_searchable_fields, build_search_filters\n","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud","level":2,"title":"fastapi_toolsets.crud.factory.AsyncCrud","text":" Bases: Generic[ModelType]
Generic async CRUD operations for SQLAlchemy models.
Subclass this and set the model class variable, or use CrudFactory.
count(session, filters=None, *, joins=None, outer_join=False) async classmethod","text":"Count records matching the filters.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredfilters list[Any] | None List of SQLAlchemy filter conditions
None joins JoinType | None List of (model, condition) tuples for joining related tables
None outer_join bool Use LEFT OUTER JOIN instead of INNER JOIN
False Returns:
Type Descriptionint Number of matching records
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.create","level":3,"title":"create(session, obj, *, as_response=False, schema=None) async classmethod","text":"create(\n session: AsyncSession,\n obj: BaseModel,\n *,\n schema: type[SchemaType],\n as_response: bool = ...,\n) -> Response[SchemaType]\ncreate(\n session: AsyncSession,\n obj: BaseModel,\n *,\n as_response: Literal[True],\n schema: None = ...,\n) -> Response[ModelType]\ncreate(\n session: AsyncSession,\n obj: BaseModel,\n *,\n as_response: Literal[False] = ...,\n schema: None = ...,\n) -> ModelType\n Create a new record in the database.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredobj BaseModel Pydantic model with data to create
requiredas_response bool Deprecated. Use schema instead. Will be removed in v2.0.
False schema type[BaseModel] | None Pydantic schema to serialize the result into. When provided, the result is automatically wrapped in a Response[schema].
None Returns:
Type DescriptionModelType | Response[ModelType] | Response[Any] Created model instance, or Response[schema] when schema is given,
ModelType | Response[ModelType] | Response[Any] or Response[ModelType] when as_response=True (deprecated).
cursor_paginate(session, *, cursor=None, filters=None, joins=None, outer_join=False, load_options=None, order_by=None, items_per_page=20, search=None, search_fields=None, schema=None) async classmethod","text":"cursor_paginate(\n session: AsyncSession,\n *,\n cursor: str | None = None,\n filters: list[Any] | None = None,\n joins: JoinType | None = None,\n outer_join: bool = False,\n load_options: list[ExecutableOption] | None = None,\n order_by: Any | None = None,\n items_per_page: int = 20,\n search: str | SearchConfig | None = None,\n search_fields: Sequence[SearchFieldType] | None = None,\n schema: type[SchemaType],\n) -> PaginatedResponse[SchemaType]\ncursor_paginate(\n session: AsyncSession,\n *,\n cursor: str | None = None,\n filters: list[Any] | None = None,\n joins: JoinType | None = None,\n outer_join: bool = False,\n load_options: list[ExecutableOption] | None = None,\n order_by: Any | None = None,\n items_per_page: int = 20,\n search: str | SearchConfig | None = None,\n search_fields: Sequence[SearchFieldType] | None = None,\n schema: None = ...,\n) -> PaginatedResponse[ModelType]\n Get paginated results using cursor-based pagination.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session.
requiredcursor str | None Cursor string from a previous CursorPagination. Omit (or pass None) to start from the beginning.
None filters list[Any] | None List of SQLAlchemy filter conditions.
None joins JoinType | None List of (model, condition) tuples for joining related tables.
None outer_join bool Use LEFT OUTER JOIN instead of INNER JOIN.
False load_options list[ExecutableOption] | None SQLAlchemy loader options. Falls back to default_load_options when not provided.
None order_by Any | None Additional ordering applied after the cursor column.
None items_per_page int Number of items per page (default 20).
20 search str | SearchConfig | None Search query string or SearchConfig object.
None search_fields Sequence[SearchFieldType] | None Fields to search in (overrides class default).
None schema type[BaseModel] | None Optional Pydantic schema to serialize each item into.
None Returns:
Type DescriptionPaginatedResponse[ModelType] | PaginatedResponse[Any] PaginatedResponse with CursorPagination metadata
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.delete","level":3,"title":"delete(session, filters, *, as_response=False) async classmethod","text":"delete(\n session: AsyncSession,\n filters: list[Any],\n *,\n as_response: Literal[True],\n) -> Response[None]\ndelete(\n session: AsyncSession,\n filters: list[Any],\n *,\n as_response: Literal[False] = ...,\n) -> bool\n Delete records from the database.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredfilters list[Any] List of SQLAlchemy filter conditions
requiredas_response bool Deprecated. Will be removed in v2.0. When True, returns Response[None] instead of bool.
False Returns:
Type Descriptionbool | Response[None] True if deletion was executed, or Response[None] when
bool | Response[None] as_response=True (deprecated).
exists(session, filters, *, joins=None, outer_join=False) async classmethod","text":"Check if a record exists.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredfilters list[Any] List of SQLAlchemy filter conditions
requiredjoins JoinType | None List of (model, condition) tuples for joining related tables
None outer_join bool Use LEFT OUTER JOIN instead of INNER JOIN
False Returns:
Type Descriptionbool True if at least one record matches
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.first","level":3,"title":"first(session, filters=None, *, joins=None, outer_join=False, load_options=None) async classmethod","text":"Get the first matching record, or None.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredfilters list[Any] | None List of SQLAlchemy filter conditions
None joins JoinType | None List of (model, condition) tuples for joining related tables
None outer_join bool Use LEFT OUTER JOIN instead of INNER JOIN
False load_options list[ExecutableOption] | None SQLAlchemy loader options
None Returns:
Type DescriptionModelType | None Model instance or None
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.get","level":3,"title":"get(session, filters, *, joins=None, outer_join=False, with_for_update=False, load_options=None, as_response=False, schema=None) async classmethod","text":"get(\n session: AsyncSession,\n filters: list[Any],\n *,\n joins: JoinType | None = None,\n outer_join: bool = False,\n with_for_update: bool = False,\n load_options: list[ExecutableOption] | None = None,\n schema: type[SchemaType],\n as_response: bool = ...,\n) -> Response[SchemaType]\nget(\n session: AsyncSession,\n filters: list[Any],\n *,\n joins: JoinType | None = None,\n outer_join: bool = False,\n with_for_update: bool = False,\n load_options: list[ExecutableOption] | None = None,\n as_response: Literal[True],\n schema: None = ...,\n) -> Response[ModelType]\nget(\n session: AsyncSession,\n filters: list[Any],\n *,\n joins: JoinType | None = None,\n outer_join: bool = False,\n with_for_update: bool = False,\n load_options: list[ExecutableOption] | None = None,\n as_response: Literal[False] = ...,\n schema: None = ...,\n) -> ModelType\n Get exactly one record. Raises NotFoundError if not found.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredfilters list[Any] List of SQLAlchemy filter conditions
requiredjoins JoinType | None List of (model, condition) tuples for joining related tables
None outer_join bool Use LEFT OUTER JOIN instead of INNER JOIN
False with_for_update bool Lock the row for update
False load_options list[ExecutableOption] | None SQLAlchemy loader options (e.g., selectinload)
None as_response bool Deprecated. Use schema instead. Will be removed in v2.0.
False schema type[BaseModel] | None Pydantic schema to serialize the result into. When provided, the result is automatically wrapped in a Response[schema].
None Returns:
Type DescriptionModelType | Response[ModelType] | Response[Any] Model instance, or Response[schema] when schema is given,
ModelType | Response[ModelType] | Response[Any] or Response[ModelType] when as_response=True (deprecated).
Raises:
Type DescriptionNotFoundError If no record found
MultipleResultsFound If more than one record found
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.get_multi","level":3,"title":"get_multi(session, *, filters=None, joins=None, outer_join=False, load_options=None, order_by=None, limit=None, offset=None) async classmethod","text":"Get multiple records from the database.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredfilters list[Any] | None List of SQLAlchemy filter conditions
None joins JoinType | None List of (model, condition) tuples for joining related tables
None outer_join bool Use LEFT OUTER JOIN instead of INNER JOIN
False load_options list[ExecutableOption] | None SQLAlchemy loader options
None order_by Any | None Column or list of columns to order by
None limit int | None Max number of rows to return
None offset int | None Rows to skip
None Returns:
Type DescriptionSequence[ModelType] List of model instances
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.offset_paginate","level":3,"title":"offset_paginate(session, *, filters=None, joins=None, outer_join=False, load_options=None, order_by=None, page=1, items_per_page=20, search=None, search_fields=None, schema=None) async classmethod","text":"offset_paginate(\n session: AsyncSession,\n *,\n filters: list[Any] | None = None,\n joins: JoinType | None = None,\n outer_join: bool = False,\n load_options: list[ExecutableOption] | None = None,\n order_by: Any | None = None,\n page: int = 1,\n items_per_page: int = 20,\n search: str | SearchConfig | None = None,\n search_fields: Sequence[SearchFieldType] | None = None,\n schema: type[SchemaType],\n) -> PaginatedResponse[SchemaType]\noffset_paginate(\n session: AsyncSession,\n *,\n filters: list[Any] | None = None,\n joins: JoinType | None = None,\n outer_join: bool = False,\n load_options: list[ExecutableOption] | None = None,\n order_by: Any | None = None,\n page: int = 1,\n items_per_page: int = 20,\n search: str | SearchConfig | None = None,\n search_fields: Sequence[SearchFieldType] | None = None,\n schema: None = ...,\n) -> PaginatedResponse[ModelType]\n Get paginated results using offset-based pagination.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredfilters list[Any] | None List of SQLAlchemy filter conditions
None joins JoinType | None List of (model, condition) tuples for joining related tables
None outer_join bool Use LEFT OUTER JOIN instead of INNER JOIN
False load_options list[ExecutableOption] | None SQLAlchemy loader options
None order_by Any | None Column or list of columns to order by
None page int Page number (1-indexed)
1 items_per_page int Number of items per page
20 search str | SearchConfig | None Search query string or SearchConfig object
None search_fields Sequence[SearchFieldType] | None Fields to search in (overrides class default)
None schema type[BaseModel] | None Optional Pydantic schema to serialize each item into.
None Returns:
Type DescriptionPaginatedResponse[ModelType] | PaginatedResponse[Any] PaginatedResponse with OffsetPagination metadata
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.update","level":3,"title":"update(session, obj, filters, *, exclude_unset=True, exclude_none=False, as_response=False, schema=None) async classmethod","text":"update(\n session: AsyncSession,\n obj: BaseModel,\n filters: list[Any],\n *,\n exclude_unset: bool = True,\n exclude_none: bool = False,\n schema: type[SchemaType],\n as_response: bool = ...,\n) -> Response[SchemaType]\nupdate(\n session: AsyncSession,\n obj: BaseModel,\n filters: list[Any],\n *,\n exclude_unset: bool = True,\n exclude_none: bool = False,\n as_response: Literal[True],\n schema: None = ...,\n) -> Response[ModelType]\nupdate(\n session: AsyncSession,\n obj: BaseModel,\n filters: list[Any],\n *,\n exclude_unset: bool = True,\n exclude_none: bool = False,\n as_response: Literal[False] = ...,\n schema: None = ...,\n) -> ModelType\n Update a record in the database.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredobj BaseModel Pydantic model with update data
requiredfilters list[Any] List of SQLAlchemy filter conditions
requiredexclude_unset bool Exclude fields not explicitly set in the schema
True exclude_none bool Exclude fields with None value
False as_response bool Deprecated. Use schema instead. Will be removed in v2.0.
False schema type[BaseModel] | None Pydantic schema to serialize the result into. When provided, the result is automatically wrapped in a Response[schema].
None Returns:
Type DescriptionModelType | Response[ModelType] | Response[Any] Updated model instance, or Response[schema] when schema is given,
ModelType | Response[ModelType] | Response[Any] or Response[ModelType] when as_response=True (deprecated).
Raises:
Type DescriptionNotFoundError If no record found
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.AsyncCrud.upsert","level":3,"title":"upsert(session, obj, index_elements, *, set_=None, where=None) async classmethod","text":"Create or update a record (PostgreSQL only).
Uses INSERT ... ON CONFLICT for atomic upsert.
Parameters:
Name Type Description Defaultsession AsyncSession DB async session
requiredobj BaseModel Pydantic model with data
requiredindex_elements list[str] Columns for ON CONFLICT (unique constraint)
requiredset_ BaseModel | None Pydantic model for ON CONFLICT DO UPDATE SET
None where WhereHavingRole | None WHERE clause for ON CONFLICT DO UPDATE
None Returns:
Type DescriptionModelType | None Model instance
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.factory.CrudFactory","level":2,"title":"fastapi_toolsets.crud.factory.CrudFactory(model, *, searchable_fields=None, m2m_fields=None, default_load_options=None, cursor_column=None)","text":"Create a CRUD class for a specific model.
Parameters:
Name Type Description Defaultmodel type[ModelType] SQLAlchemy model class
requiredsearchable_fields Sequence[SearchFieldType] | None Optional list of searchable fields
None m2m_fields M2MFieldType | None Optional mapping for many-to-many relationships. Maps schema field names (containing lists of IDs) to SQLAlchemy relationship attributes.
None default_load_options list[ExecutableOption] | None Default SQLAlchemy loader options applied to all read queries when no explicit load_options are passed. Use this instead of lazy=\"selectin\" on the model so that loading strategy is explicit and per-CRUD. Overridden entirely (not merged) when load_options is provided at call-site.
None cursor_column Any | None Required to call cursor_paginate. Must be monotonically ordered (e.g. integer PK, UUID v7, timestamp). See the cursor pagination docs for supported column types.
None Returns:
Type Descriptiontype[AsyncCrud[ModelType]] AsyncCrud subclass bound to the model
Examplefrom fastapi_toolsets.crud import CrudFactory\nfrom myapp.models import User, Post\n\nUserCrud = CrudFactory(User)\nPostCrud = CrudFactory(Post)\n\n# With searchable fields:\nUserCrud = CrudFactory(\n User,\n searchable_fields=[User.username, User.email, (User.role, Role.name)]\n)\n\n# With many-to-many fields:\n# Schema has `tag_ids: list[UUID]`, model has `tags` relationship to Tag\nPostCrud = CrudFactory(\n Post,\n m2m_fields={\"tag_ids\": Post.tags},\n)\n\n# With a fixed cursor column for cursor_paginate:\nPostCrud = CrudFactory(\n Post,\n cursor_column=Post.created_at,\n)\n\n# With default load strategy (replaces lazy=\"selectin\" on the model):\nArticleCrud = CrudFactory(\n Article,\n default_load_options=[selectinload(Article.category), selectinload(Article.tags)],\n)\n\n# Override default_load_options for a specific call:\narticle = await ArticleCrud.get(\n session,\n [Article.id == 1],\n load_options=[selectinload(Article.category)], # tags won't load\n)\n\n# Usage\nuser = await UserCrud.get(session, [User.id == 1])\nposts = await PostCrud.get_multi(session, filters=[Post.user_id == user.id])\n\n# Create with M2M - tag_ids are automatically resolved\npost = await PostCrud.create(session, PostCreate(title=\"Hello\", tag_ids=[id1, id2]))\n\n# With search\nresult = await UserCrud.offset_paginate(session, search=\"john\")\n\n# With joins (inner join by default):\nusers = await UserCrud.get_multi(\n session,\n joins=[(Post, Post.user_id == User.id)],\n filters=[Post.published == True],\n)\n\n# With outer join:\nusers = await UserCrud.get_multi(\n session,\n joins=[(Post, Post.user_id == User.id)],\n outer_join=True,\n)\n","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.search.SearchConfig","level":2,"title":"fastapi_toolsets.crud.search.SearchConfig dataclass","text":"Advanced search configuration.
Attributes:
Name Type Descriptionquery str The search string
fields Sequence[SearchFieldType] | None Fields to search (columns or tuples for relationships)
case_sensitive bool Case-sensitive search (default: False)
match_mode Literal['any', 'all'] \"any\" (OR) or \"all\" (AND) to combine fields
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.search.get_searchable_fields","level":2,"title":"fastapi_toolsets.crud.search.get_searchable_fields(model, *, include_relationships=True, max_depth=1)","text":"Auto-detect String fields on a model and its relationships.
Parameters:
Name Type Description Defaultmodel type[DeclarativeBase] SQLAlchemy model class
requiredinclude_relationships bool Include fields from many-to-one/one-to-one relationships
True max_depth int Max depth for relationship traversal (default: 1)
1 Returns:
Type Descriptionlist[SearchFieldType] List of columns and tuples (relationship, column)
","path":["Reference","crud"],"tags":[]},{"location":"reference/crud/#fastapi_toolsets.crud.search.build_search_filters","level":2,"title":"fastapi_toolsets.crud.search.build_search_filters(model, search, search_fields=None, default_fields=None)","text":"Build SQLAlchemy filter conditions for search.
Parameters:
Name Type Description Defaultmodel type[DeclarativeBase] SQLAlchemy model class
requiredsearch str | SearchConfig Search string or SearchConfig
requiredsearch_fields Sequence[SearchFieldType] | None Fields specified per-call (takes priority)
None default_fields Sequence[SearchFieldType] | None Default fields (from ClassVar)
None Returns:
Type Descriptiontuple[list[ColumnElement[bool]], list[InstrumentedAttribute[Any]]] Tuple of (filter_conditions, joins_needed)
","path":["Reference","crud"],"tags":[]},{"location":"reference/db/","level":1,"title":"db","text":"Here's the reference for all database session utilities, transaction helpers, and locking functions.
You can import them directly from fastapi_toolsets.db:
from fastapi_toolsets.db import (\n LockMode,\n create_db_dependency,\n create_db_context,\n get_transaction,\n lock_tables,\n wait_for_row_change,\n)\n","path":["Reference","db"],"tags":[]},{"location":"reference/db/#fastapi_toolsets.db.LockMode","level":2,"title":"fastapi_toolsets.db.LockMode","text":" Bases: str, Enum
PostgreSQL table lock modes.
See: https://www.postgresql.org/docs/current/explicit-locking.html
","path":["Reference","db"],"tags":[]},{"location":"reference/db/#fastapi_toolsets.db.create_db_dependency","level":2,"title":"fastapi_toolsets.db.create_db_dependency(session_maker)","text":"Create a FastAPI dependency for database sessions.
Creates a dependency function that yields a session and auto-commits if a transaction is active when the request completes.
Parameters:
Name Type Description Defaultsession_maker async_sessionmaker[AsyncSession] Async session factory from create_session_factory()
requiredReturns:
Type DescriptionCallable[[], AsyncGenerator[AsyncSession, None]] An async generator function usable with FastAPI's Depends()
Examplefrom fastapi import Depends\nfrom sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker\nfrom fastapi_toolsets.db import create_db_dependency\n\nengine = create_async_engine(\"postgresql+asyncpg://...\")\nSessionLocal = async_sessionmaker(engine, expire_on_commit=False)\nget_db = create_db_dependency(SessionLocal)\n\n@app.get(\"/users\")\nasync def list_users(session: AsyncSession = Depends(get_db)):\n ...\n","path":["Reference","db"],"tags":[]},{"location":"reference/db/#fastapi_toolsets.db.create_db_context","level":2,"title":"fastapi_toolsets.db.create_db_context(session_maker)","text":"Create a context manager for database sessions.
Creates a context manager for use outside of FastAPI request handlers, such as in background tasks, CLI commands, or tests.
Parameters:
Name Type Description Defaultsession_maker async_sessionmaker[AsyncSession] Async session factory from create_session_factory()
requiredReturns:
Type DescriptionCallable[[], AbstractAsyncContextManager[AsyncSession]] An async context manager function
Examplefrom sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker\nfrom fastapi_toolsets.db import create_db_context\n\nengine = create_async_engine(\"postgresql+asyncpg://...\")\nSessionLocal = async_sessionmaker(engine, expire_on_commit=False)\nget_db_context = create_db_context(SessionLocal)\n\nasync def background_task():\n async with get_db_context() as session:\n user = await UserCrud.get(session, [User.id == 1])\n ...\n","path":["Reference","db"],"tags":[]},{"location":"reference/db/#fastapi_toolsets.db.get_transaction","level":2,"title":"fastapi_toolsets.db.get_transaction(session) async","text":"Get a transaction context, handling nested transactions.
If already in a transaction, creates a savepoint (nested transaction). Otherwise, starts a new transaction.
Parameters:
Name Type Description Defaultsession AsyncSession AsyncSession instance
requiredYields:
Type DescriptionAsyncGenerator[AsyncSession, None] The session within the transaction context
Exampleasync with get_transaction(session):\n session.add(model)\n # Auto-commits on exit, rolls back on exception\n","path":["Reference","db"],"tags":[]},{"location":"reference/db/#fastapi_toolsets.db.lock_tables","level":2,"title":"fastapi_toolsets.db.lock_tables(session, tables, *, mode=LockMode.SHARE_UPDATE_EXCLUSIVE, timeout='5s') async","text":"Lock PostgreSQL tables for the duration of a transaction.
Acquires table-level locks that are held until the transaction ends. Useful for preventing concurrent modifications during critical operations.
Parameters:
Name Type Description Defaultsession AsyncSession AsyncSession instance
requiredtables list[type[DeclarativeBase]] List of SQLAlchemy model classes to lock
requiredmode LockMode Lock mode (default: SHARE UPDATE EXCLUSIVE)
SHARE_UPDATE_EXCLUSIVE timeout str Lock timeout (default: \"5s\")
'5s' Yields:
Type DescriptionAsyncGenerator[AsyncSession, None] The session with locked tables
Raises:
Type DescriptionSQLAlchemyError If lock cannot be acquired within timeout
Examplefrom fastapi_toolsets.db import lock_tables, LockMode\n\nasync with lock_tables(session, [User, Account]):\n # Tables are locked with SHARE UPDATE EXCLUSIVE mode\n user = await UserCrud.get(session, [User.id == 1])\n user.balance += 100\n\n# With custom lock mode\nasync with lock_tables(session, [Order], mode=LockMode.EXCLUSIVE):\n # Exclusive lock - no other transactions can access\n await process_order(session, order_id)\n","path":["Reference","db"],"tags":[]},{"location":"reference/db/#fastapi_toolsets.db.wait_for_row_change","level":2,"title":"fastapi_toolsets.db.wait_for_row_change(session, model, pk_value, *, columns=None, interval=0.5, timeout=None) async","text":"Poll a database row until a change is detected.
Queries the row every interval seconds and returns the model instance once a change is detected in any column (or only the specified columns).
Parameters:
Name Type Description Defaultsession AsyncSession AsyncSession instance
requiredmodel type[_M] SQLAlchemy model class
requiredpk_value Any Primary key value of the row to watch
requiredcolumns list[str] | None Optional list of column names to watch. If None, all columns are watched.
None interval float Polling interval in seconds (default: 0.5)
0.5 timeout float | None Maximum time to wait in seconds. None means wait forever.
None Returns:
Type Description_M The refreshed model instance with updated values
Raises:
Type DescriptionLookupError If the row does not exist or is deleted during polling
TimeoutError If timeout expires before a change is detected
Examplefrom fastapi_toolsets.db import wait_for_row_change\n\n# Wait for any column to change\nupdated = await wait_for_row_change(session, User, user_id)\n\n# Watch specific columns with a timeout\nupdated = await wait_for_row_change(\n session, User, user_id,\n columns=[\"status\", \"email\"],\n interval=1.0,\n timeout=30.0,\n)\n","path":["Reference","db"],"tags":[]},{"location":"reference/dependencies/","level":1,"title":"dependencies","text":"Here's the reference for the FastAPI dependency factory functions.
You can import them directly from fastapi_toolsets.dependencies:
from fastapi_toolsets.dependencies import PathDependency, BodyDependency\n","path":["Reference","dependencies"],"tags":[]},{"location":"reference/dependencies/#fastapi_toolsets.dependencies.PathDependency","level":2,"title":"fastapi_toolsets.dependencies.PathDependency(model, field, *, session_dep, param_name=None)","text":"Create a dependency that fetches a DB object from a path parameter.
Parameters:
Name Type Description Defaultmodel type[ModelType] SQLAlchemy model class
requiredfield Any Model field to filter by (e.g., User.id)
requiredsession_dep SessionDependency Session dependency function (e.g., get_db)
requiredparam_name str | None Path parameter name (defaults to model_field, e.g., user_id)
None Returns:
Type DescriptionModelType A Depends() instance that resolves to the model instance
Raises:
Type DescriptionNotFoundError If no matching record is found
ExampleUserDep = PathDependency(User, User.id, session_dep=get_db)\n\n@router.get(\"/user/{id}\")\nasync def get(\n user: User = UserDep,\n): ...\n","path":["Reference","dependencies"],"tags":[]},{"location":"reference/dependencies/#fastapi_toolsets.dependencies.BodyDependency","level":2,"title":"fastapi_toolsets.dependencies.BodyDependency(model, field, *, session_dep, body_field)","text":"Create a dependency that fetches a DB object from a body field.
Parameters:
Name Type Description Defaultmodel type[ModelType] SQLAlchemy model class
requiredfield Any Model field to filter by (e.g., User.id)
requiredsession_dep SessionDependency Session dependency function (e.g., get_db)
requiredbody_field str Name of the field in the request body
requiredReturns:
Type DescriptionModelType A Depends() instance that resolves to the model instance
Raises:
Type DescriptionNotFoundError If no matching record is found
ExampleUserDep = BodyDependency(\n User, User.ctfd_id, session_dep=get_db, body_field=\"user_id\"\n)\n\n@router.post(\"/assign\")\nasync def assign(\n user: User = UserDep,\n): ...\n","path":["Reference","dependencies"],"tags":[]},{"location":"reference/exceptions/","level":1,"title":"exceptions","text":"Here's the reference for all exception classes and handler utilities.
You can import them directly from fastapi_toolsets.exceptions:
from fastapi_toolsets.exceptions import (\n ApiException,\n UnauthorizedError,\n ForbiddenError,\n NotFoundError,\n ConflictError,\n NoSearchableFieldsError,\n generate_error_responses,\n init_exceptions_handlers,\n)\n","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.ApiException","level":2,"title":"fastapi_toolsets.exceptions.exceptions.ApiException","text":" Bases: Exception
Base exception for API errors with structured response.
Subclass this to create custom API exceptions with consistent error format. The exception handler will use api_error to generate the response.
Exampleclass CustomError(ApiException):\n api_error = ApiError(\n code=400,\n msg=\"Bad Request\",\n desc=\"The request was invalid.\",\n err_code=\"CUSTOM-400\",\n )\n","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.ApiException.__init__","level":3,"title":"__init__(detail=None)","text":"Initialize the exception.
Parameters:
Name Type Description Defaultdetail str | None Optional override for the error message
None","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.UnauthorizedError","level":2,"title":"fastapi_toolsets.exceptions.exceptions.UnauthorizedError","text":" Bases: ApiException
HTTP 401 - User is not authenticated.
","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.UnauthorizedError.__init__","level":3,"title":"__init__(detail=None)","text":"Initialize the exception.
Parameters:
Name Type Description Defaultdetail str | None Optional override for the error message
None","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.ForbiddenError","level":2,"title":"fastapi_toolsets.exceptions.exceptions.ForbiddenError","text":" Bases: ApiException
HTTP 403 - User lacks required permissions.
","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.ForbiddenError.__init__","level":3,"title":"__init__(detail=None)","text":"Initialize the exception.
Parameters:
Name Type Description Defaultdetail str | None Optional override for the error message
None","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.NotFoundError","level":2,"title":"fastapi_toolsets.exceptions.exceptions.NotFoundError","text":" Bases: ApiException
HTTP 404 - Resource not found.
","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.NotFoundError.__init__","level":3,"title":"__init__(detail=None)","text":"Initialize the exception.
Parameters:
Name Type Description Defaultdetail str | None Optional override for the error message
None","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.ConflictError","level":2,"title":"fastapi_toolsets.exceptions.exceptions.ConflictError","text":" Bases: ApiException
HTTP 409 - Resource conflict.
","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.ConflictError.__init__","level":3,"title":"__init__(detail=None)","text":"Initialize the exception.
Parameters:
Name Type Description Defaultdetail str | None Optional override for the error message
None","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.NoSearchableFieldsError","level":2,"title":"fastapi_toolsets.exceptions.exceptions.NoSearchableFieldsError","text":" Bases: ApiException
Raised when search is requested but no searchable fields are available.
","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.NoSearchableFieldsError.__init__","level":3,"title":"__init__(model)","text":"Initialize the exception.
Parameters:
Name Type Description Defaultmodel type The SQLAlchemy model class that has no searchable fields
required","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.exceptions.generate_error_responses","level":2,"title":"fastapi_toolsets.exceptions.exceptions.generate_error_responses(*errors)","text":"Generate OpenAPI response documentation for exceptions.
Use this to document possible error responses for an endpoint.
Parameters:
Name Type Description Default*errors type[ApiException] Exception classes that inherit from ApiException
() Returns:
Type Descriptiondict[int | str, dict[str, Any]] Dict suitable for FastAPI's responses parameter
Examplefrom fastapi_toolsets.exceptions import generate_error_responses, UnauthorizedError, ForbiddenError\n\n@app.get(\n \"/admin\",\n responses=generate_error_responses(UnauthorizedError, ForbiddenError)\n)\nasync def admin_endpoint():\n ...\n","path":["Reference","exceptions"],"tags":[]},{"location":"reference/exceptions/#fastapi_toolsets.exceptions.handler.init_exceptions_handlers","level":2,"title":"fastapi_toolsets.exceptions.handler.init_exceptions_handlers(app)","text":"Register exception handlers and custom OpenAPI schema on a FastAPI app.
Installs handlers for :class:ApiException, validation errors, and unhandled exceptions, and replaces the default 422 schema with a consistent error format.
Parameters:
Name Type Description Defaultapp FastAPI FastAPI application instance
requiredReturns:
Type DescriptionFastAPI The same FastAPI instance (for chaining)
Examplefrom fastapi import FastAPI\nfrom fastapi_toolsets.exceptions import init_exceptions_handlers\n\napp = FastAPI()\ninit_exceptions_handlers(app)\n","path":["Reference","exceptions"],"tags":[]},{"location":"reference/fixtures/","level":1,"title":"fixtures","text":"Here's the reference for the fixture registry, enums, and loading utilities.
You can import them directly from fastapi_toolsets.fixtures:
from fastapi_toolsets.fixtures import (\n Context,\n LoadStrategy,\n Fixture,\n FixtureRegistry,\n load_fixtures,\n load_fixtures_by_context,\n get_obj_by_attr,\n)\n","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.Context","level":2,"title":"fastapi_toolsets.fixtures.enum.Context","text":" Bases: str, Enum
Predefined fixture contexts.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.Context.BASE","level":3,"title":"BASE = 'base' class-attribute instance-attribute","text":"Base fixtures loaded in all environments.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.Context.DEVELOPMENT","level":3,"title":"DEVELOPMENT = 'development' class-attribute instance-attribute","text":"Development fixtures.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.Context.PRODUCTION","level":3,"title":"PRODUCTION = 'production' class-attribute instance-attribute","text":"Production-only fixtures.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.Context.TESTING","level":3,"title":"TESTING = 'testing' class-attribute instance-attribute","text":"Test fixtures.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.LoadStrategy","level":2,"title":"fastapi_toolsets.fixtures.enum.LoadStrategy","text":" Bases: str, Enum
Strategy for loading fixtures into the database.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.LoadStrategy.INSERT","level":3,"title":"INSERT = 'insert' class-attribute instance-attribute","text":"Insert new records. Fails if record already exists.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.LoadStrategy.MERGE","level":3,"title":"MERGE = 'merge' class-attribute instance-attribute","text":"Insert or update based on primary key (SQLAlchemy merge).
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.enum.LoadStrategy.SKIP_EXISTING","level":3,"title":"SKIP_EXISTING = 'skip_existing' class-attribute instance-attribute","text":"Insert only if record doesn't exist (based on primary key).
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.Fixture","level":2,"title":"fastapi_toolsets.fixtures.registry.Fixture dataclass","text":"A fixture definition with metadata.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry","level":2,"title":"fastapi_toolsets.fixtures.registry.FixtureRegistry","text":"Registry for managing fixtures with dependencies.
Examplefrom fastapi_toolsets.fixtures import FixtureRegistry, Context\n\nfixtures = FixtureRegistry()\n\n@fixtures.register\ndef roles():\n return [\n Role(id=1, name=\"admin\"),\n Role(id=2, name=\"user\"),\n ]\n\n@fixtures.register(depends_on=[\"roles\"])\ndef users():\n return [\n User(id=1, username=\"admin\", role_id=1),\n ]\n\n@fixtures.register(depends_on=[\"users\"], contexts=[Context.TESTING])\ndef test_data():\n return [\n Post(id=1, title=\"Test\", user_id=1),\n ]\n","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.get","level":3,"title":"get(name)","text":"Get a fixture by name.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_all","level":3,"title":"get_all()","text":"Get all registered fixtures.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.get_by_context","level":3,"title":"get_by_context(*contexts)","text":"Get fixtures for specific contexts.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.include_registry","level":3,"title":"include_registry(registry)","text":"Include another FixtureRegistry in the same current FixtureRegistry.
Parameters:
Name Type Description Defaultregistry FixtureRegistry The FixtureRegistry to include
Raises:
Type DescriptionValueError If a fixture name already exists in the current registry
Exampleregistry = FixtureRegistry()\ndev_registry = FixtureRegistry()\n\n@dev_registry.register\ndef dev_data():\n return [...]\n\nregistry.include_registry(registry=dev_registry)\n","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.register","level":3,"title":"register(func=None, *, name=None, depends_on=None, contexts=None)","text":"Register a fixture function.
Can be used as a decorator with or without arguments.
Parameters:
Name Type Description Defaultfunc Callable[[], Sequence[DeclarativeBase]] | None Fixture function returning list of model instances
None name str | None Fixture name (defaults to function name)
None depends_on list[str] | None List of fixture names this depends on
None contexts list[str | Context] | None List of contexts this fixture belongs to
None Example @fixtures.register\ndef roles():\n return [Role(id=1, name=\"admin\")]\n\n@fixtures.register(depends_on=[\"roles\"], contexts=[Context.TESTING])\ndef test_users():\n return [User(id=1, username=\"test\", role_id=1)]\n","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.resolve_context_dependencies","level":3,"title":"resolve_context_dependencies(*contexts)","text":"Resolve all fixtures for contexts with dependencies.
Parameters:
Name Type Description Default*contexts str | Context Contexts to load
() Returns:
Type Descriptionlist[str] List of fixture names in load order
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.registry.FixtureRegistry.resolve_dependencies","level":3,"title":"resolve_dependencies(*names)","text":"Resolve fixture dependencies in topological order.
Parameters:
Name Type Description Default*names str Fixture names to resolve
() Returns:
Type Descriptionlist[str] List of fixture names in load order (dependencies first)
Raises:
Type DescriptionKeyError If a fixture is not found
ValueError If circular dependency detected
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.utils.load_fixtures","level":2,"title":"fastapi_toolsets.fixtures.utils.load_fixtures(session, registry, *names, strategy=LoadStrategy.MERGE) async","text":"Load specific fixtures by name with dependencies.
Parameters:
Name Type Description Defaultsession AsyncSession Database session
requiredregistry FixtureRegistry Fixture registry
required*names str Fixture names to load (dependencies auto-resolved)
() strategy LoadStrategy How to handle existing records
MERGE Returns:
Type Descriptiondict[str, list[DeclarativeBase]] Dict mapping fixture names to loaded instances
Example# Loads 'roles' first (dependency), then 'users'\nresult = await load_fixtures(session, fixtures, \"users\")\nprint(result[\"users\"]) # [User(...), ...]\n","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.utils.load_fixtures_by_context","level":2,"title":"fastapi_toolsets.fixtures.utils.load_fixtures_by_context(session, registry, *contexts, strategy=LoadStrategy.MERGE) async","text":"Load all fixtures for specific contexts.
Parameters:
Name Type Description Defaultsession AsyncSession Database session
requiredregistry FixtureRegistry Fixture registry
required*contexts str | Context Contexts to load (e.g., Context.BASE, Context.TESTING)
() strategy LoadStrategy How to handle existing records
MERGE Returns:
Type Descriptiondict[str, list[DeclarativeBase]] Dict mapping fixture names to loaded instances
Example# Load base + testing fixtures\nawait load_fixtures_by_context(\n session, fixtures,\n Context.BASE, Context.TESTING\n)\n","path":["Reference","fixtures"],"tags":[]},{"location":"reference/fixtures/#fastapi_toolsets.fixtures.utils.get_obj_by_attr","level":2,"title":"fastapi_toolsets.fixtures.utils.get_obj_by_attr(fixtures, attr_name, value)","text":"Get a SQLAlchemy model instance by matching an attribute value.
Parameters:
Name Type Description Defaultfixtures Callable[[], Sequence[T]] A fixture function registered via @registry.register that returns a sequence of SQLAlchemy model instances.
attr_name str Name of the attribute to match against.
requiredvalue Any Value to match.
requiredReturns:
Type DescriptionT The first model instance where the attribute matches the given value.
Raises:
Type DescriptionStopIteration If no matching object is found in the fixture group.
","path":["Reference","fixtures"],"tags":[]},{"location":"reference/logger/","level":1,"title":"logger","text":"Here's the reference for the logging utilities.
You can import them directly from fastapi_toolsets.logger:
from fastapi_toolsets.logger import configure_logging, get_logger\n","path":["Reference","logger"],"tags":[]},{"location":"reference/logger/#fastapi_toolsets.logger.configure_logging","level":2,"title":"fastapi_toolsets.logger.configure_logging(level='INFO', fmt=DEFAULT_FORMAT, logger_name=None)","text":"Configure logging with a stdout handler and consistent format.
Sets up a :class:~logging.StreamHandler writing to stdout with the given format and level. Also configures the uvicorn loggers so that FastAPI access logs use the same format.
Calling this function multiple times is safe -- existing handlers are replaced rather than duplicated.
Parameters:
Name Type Description Defaultlevel LogLevel | int Log level (e.g. \"DEBUG\", \"INFO\", or logging.DEBUG).
'INFO' fmt str Log format string. Defaults to \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\".
DEFAULT_FORMAT logger_name str | None Logger name to configure. None (the default) configures the root logger so all loggers inherit the settings.
None Returns:
Type DescriptionLogger The configured Logger instance.
Examplefrom fastapi_toolsets.logger import configure_logging\n\nlogger = configure_logging(\"DEBUG\")\nlogger.info(\"Application started\")\n","path":["Reference","logger"],"tags":[]},{"location":"reference/logger/#fastapi_toolsets.logger.get_logger","level":2,"title":"fastapi_toolsets.logger.get_logger(name=_SENTINEL)","text":"Return a logger with the given name.
A thin convenience wrapper around :func:logging.getLogger that keeps logging imports consistent across the codebase.
When called without arguments, the caller's __name__ is used automatically, so get_logger() in a module is equivalent to logging.getLogger(__name__). Pass None explicitly to get the root logger.
Parameters:
Name Type Description Defaultname str | None Logger name. Defaults to the caller's __name__. Pass None to get the root logger.
_SENTINEL Returns:
Type DescriptionLogger A Logger instance.
Examplefrom fastapi_toolsets.logger import get_logger\n\nlogger = get_logger() # uses caller's __name__\nlogger = get_logger(\"myapp\") # explicit name\nlogger = get_logger(None) # root logger\n","path":["Reference","logger"],"tags":[]},{"location":"reference/metrics/","level":1,"title":"metrics","text":"Here's the reference for the Prometheus metrics registry and endpoint handler.
You can import them directly from fastapi_toolsets.metrics:
from fastapi_toolsets.metrics import Metric, MetricsRegistry, init_metrics\n","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.registry.Metric","level":2,"title":"fastapi_toolsets.metrics.registry.Metric dataclass","text":"A metric definition with metadata.
","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.registry.MetricsRegistry","level":2,"title":"fastapi_toolsets.metrics.registry.MetricsRegistry","text":"Registry for managing Prometheus metric providers and collectors.
Examplefrom prometheus_client import Counter, Gauge\nfrom fastapi_toolsets.metrics import MetricsRegistry\n\nmetrics = MetricsRegistry()\n\n@metrics.register\ndef http_requests():\n return Counter(\"http_requests_total\", \"Total HTTP requests\", [\"method\", \"status\"])\n\n@metrics.register(name=\"db_pool\")\ndef database_pool_size():\n return Gauge(\"db_pool_size\", \"Database connection pool size\")\n\n@metrics.register(collect=True)\ndef collect_queue_depth(gauge=Gauge(\"queue_depth\", \"Current queue depth\")):\n gauge.set(get_current_queue_depth())\n","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.registry.MetricsRegistry.get_all","level":3,"title":"get_all()","text":"Get all registered metric definitions.
","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.registry.MetricsRegistry.get_collectors","level":3,"title":"get_collectors()","text":"Get collectors (called on each scrape).
","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.registry.MetricsRegistry.get_providers","level":3,"title":"get_providers()","text":"Get metric providers (called once at init).
","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.registry.MetricsRegistry.include_registry","level":3,"title":"include_registry(registry)","text":"Include another :class:MetricsRegistry into this one.
Parameters:
Name Type Description Defaultregistry MetricsRegistry The registry to merge in.
requiredRaises:
Type DescriptionValueError If a metric name already exists in the current registry.
Examplemain = MetricsRegistry()\nsub = MetricsRegistry()\n\n@sub.register\ndef sub_metric():\n return Counter(\"sub_total\", \"Sub counter\")\n\nmain.include_registry(sub)\n","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.registry.MetricsRegistry.register","level":3,"title":"register(func=None, *, name=None, collect=False)","text":"Register a metric provider or collector function.
Can be used as a decorator with or without arguments.
Parameters:
Name Type Description Defaultfunc Callable[..., Any] | None The metric function to register.
None name str | None Metric name (defaults to function name).
None collect bool If True, the function is called on every scrape. If False (default), called once at init time.
False Example @metrics.register\ndef my_counter():\n return Counter(\"my_counter\", \"A counter\")\n\n@metrics.register(collect=True, name=\"queue\")\ndef collect_queue_depth():\n gauge.set(compute_depth())\n","path":["Reference","metrics"],"tags":[]},{"location":"reference/metrics/#fastapi_toolsets.metrics.handler.init_metrics","level":2,"title":"fastapi_toolsets.metrics.handler.init_metrics(app, registry, *, path='/metrics')","text":"Register a Prometheus /metrics endpoint on a FastAPI app.
Parameters:
Name Type Description Defaultapp FastAPI FastAPI application instance.
requiredregistry MetricsRegistry A :class:MetricsRegistry containing providers and collectors.
path str URL path for the metrics endpoint (default /metrics).
'/metrics' Returns:
Type DescriptionFastAPI The same FastAPI instance (for chaining).
Examplefrom fastapi import FastAPI\nfrom fastapi_toolsets.metrics import MetricsRegistry, init_metrics\n\nmetrics = MetricsRegistry()\napp = FastAPI()\ninit_metrics(app, registry=metrics)\n","path":["Reference","metrics"],"tags":[]},{"location":"reference/pytest/","level":1,"title":"pytest","text":"Here's the reference for all testing utilities and pytest fixtures.
You can import them directly from fastapi_toolsets.pytest:
from fastapi_toolsets.pytest import (\n register_fixtures,\n create_async_client,\n create_db_session,\n worker_database_url,\n create_worker_database,\n cleanup_tables,\n)\n","path":["Reference","pytest"],"tags":[]},{"location":"reference/pytest/#fastapi_toolsets.pytest.plugin.register_fixtures","level":2,"title":"fastapi_toolsets.pytest.plugin.register_fixtures(registry, namespace, *, prefix='fixture_', session_fixture='db_session', strategy=LoadStrategy.MERGE)","text":"Register pytest fixtures from a FixtureRegistry.
Automatically creates pytest fixtures for each fixture in the registry. Dependencies are resolved via pytest fixture dependencies.
Parameters:
Name Type Description Defaultregistry FixtureRegistry The FixtureRegistry containing fixtures
requirednamespace dict[str, Any] The module's globals() dict to add fixtures to
requiredprefix str Prefix for generated fixture names (default: \"fixture_\")
'fixture_' session_fixture str Name of the db session fixture (default: \"db_session\")
'db_session' strategy LoadStrategy Loading strategy for fixtures (default: MERGE)
MERGE Returns:
Type Descriptionlist[str] List of created fixture names
Example# conftest.py\nfrom app.fixtures import fixtures\nfrom fastapi_toolsets.pytest_plugin import register_fixtures\n\nregister_fixtures(fixtures, globals())\n\n# Creates fixtures like:\n# - fixture_roles\n# - fixture_users (depends on fixture_roles if users depends on roles)\n# - fixture_posts (depends on fixture_users if posts depends on users)\n","path":["Reference","pytest"],"tags":[]},{"location":"reference/pytest/#fastapi_toolsets.pytest.utils.create_async_client","level":2,"title":"fastapi_toolsets.pytest.utils.create_async_client(app, base_url='http://test', dependency_overrides=None) async","text":"Create an async httpx client for testing FastAPI applications.
Parameters:
Name Type Description Defaultapp Any FastAPI application instance.
requiredbase_url str Base URL for requests. Defaults to \"http://test\".
'http://test' dependency_overrides dict[Callable[..., Any], Callable[..., Any]] | None Optional mapping of original dependencies to their test replacements. Applied via app.dependency_overrides before yielding and cleaned up after.
None Yields:
Type DescriptionAsyncGenerator[AsyncClient, None] An AsyncClient configured for the app.
Examplefrom fastapi import FastAPI\nfrom fastapi_toolsets.pytest import create_async_client\n\napp = FastAPI()\n\n@pytest.fixture\nasync def client():\n async with create_async_client(app) as c:\n yield c\n\nasync def test_endpoint(client: AsyncClient):\n response = await client.get(\"/health\")\n assert response.status_code == 200\n Example with dependency overrides from fastapi_toolsets.pytest import create_async_client, create_db_session\nfrom app.db import get_db\n\n@pytest.fixture\nasync def db_session():\n async with create_db_session(DATABASE_URL, Base, cleanup=True) as session:\n yield session\n\n@pytest.fixture\nasync def client(db_session):\n async def override():\n yield db_session\n\n async with create_async_client(\n app, dependency_overrides={get_db: override}\n ) as c:\n yield c\n","path":["Reference","pytest"],"tags":[]},{"location":"reference/pytest/#fastapi_toolsets.pytest.utils.create_db_session","level":2,"title":"fastapi_toolsets.pytest.utils.create_db_session(database_url, base, *, echo=False, expire_on_commit=False, drop_tables=True, cleanup=False) async","text":"Create a database session for testing.
Creates tables before yielding the session and optionally drops them after. Each call creates a fresh engine and session for test isolation.
Parameters:
Name Type Description Defaultdatabase_url str Database connection URL (e.g., \"postgresql+asyncpg://...\").
requiredbase type[DeclarativeBase] SQLAlchemy DeclarativeBase class containing model metadata.
requiredecho bool Enable SQLAlchemy query logging. Defaults to False.
False expire_on_commit bool Expire objects after commit. Defaults to False.
False drop_tables bool Drop tables after test. Defaults to True.
True cleanup bool Truncate all tables after test using :func:cleanup_tables. Defaults to False.
False Yields:
Type DescriptionAsyncGenerator[AsyncSession, None] An AsyncSession ready for database operations.
Examplefrom fastapi_toolsets.pytest import create_db_session\nfrom app.models import Base\n\nDATABASE_URL = \"postgresql+asyncpg://user:pass@localhost/test_db\"\n\n@pytest.fixture\nasync def db_session():\n async with create_db_session(\n DATABASE_URL, Base, cleanup=True\n ) as session:\n yield session\n\nasync def test_create_user(db_session: AsyncSession):\n user = User(name=\"test\")\n db_session.add(user)\n await db_session.commit()\n","path":["Reference","pytest"],"tags":[]},{"location":"reference/pytest/#fastapi_toolsets.pytest.utils.worker_database_url","level":2,"title":"fastapi_toolsets.pytest.utils.worker_database_url(database_url, default_test_db)","text":"Derive a per-worker database URL for pytest-xdist parallel runs.
Appends _{worker_name} to the database name so each xdist worker operates on its own database. When not running under xdist, _{default_test_db} is appended instead.
The worker name is read from the PYTEST_XDIST_WORKER environment variable (set automatically by xdist in each worker process).
Parameters:
Name Type Description Defaultdatabase_url str Original database connection URL.
requireddefault_test_db str Suffix appended to the database name when PYTEST_XDIST_WORKER is not set.
Returns:
Type Descriptionstr A database URL with a worker- or default-specific database name.
Example# With PYTEST_XDIST_WORKER=\"gw0\":\nurl = worker_database_url(\n \"postgresql+asyncpg://user:pass@localhost/test_db\",\n default_test_db=\"test\",\n)\n# \"postgresql+asyncpg://user:pass@localhost/test_db_gw0\"\n\n# Without PYTEST_XDIST_WORKER:\nurl = worker_database_url(\n \"postgresql+asyncpg://user:pass@localhost/test_db\",\n default_test_db=\"test\",\n)\n# \"postgresql+asyncpg://user:pass@localhost/test_db_test\"\n","path":["Reference","pytest"],"tags":[]},{"location":"reference/pytest/#fastapi_toolsets.pytest.utils.create_worker_database","level":2,"title":"fastapi_toolsets.pytest.utils.create_worker_database(database_url, default_test_db='test_db') async","text":"Create and drop a per-worker database for pytest-xdist isolation.
Intended for use as a session-scoped fixture. Connects to the server using the original database_url (with AUTOCOMMIT isolation for DDL), creates a dedicated database for the worker, and yields the worker-specific URL. On cleanup the worker database is dropped.
When running under xdist the database name is suffixed with the worker name (e.g. _gw0). Otherwise it is suffixed with default_test_db.
Parameters:
Name Type Description Defaultdatabase_url str Original database connection URL.
requireddefault_test_db str Suffix appended to the database name when PYTEST_XDIST_WORKER is not set. Defaults to \"test_db\".
'test_db' Yields:
Type DescriptionAsyncGenerator[str, None] The worker-specific database URL.
Examplefrom fastapi_toolsets.pytest import (\n create_worker_database, create_db_session,\n)\n\nDATABASE_URL = \"postgresql+asyncpg://postgres:postgres@localhost/test_db\"\n\n@pytest.fixture(scope=\"session\")\nasync def worker_db_url():\n async with create_worker_database(DATABASE_URL) as url:\n yield url\n\n@pytest.fixture\nasync def db_session(worker_db_url):\n async with create_db_session(\n worker_db_url, Base, cleanup=True\n ) as session:\n yield session\n","path":["Reference","pytest"],"tags":[]},{"location":"reference/pytest/#fastapi_toolsets.pytest.utils.cleanup_tables","level":2,"title":"fastapi_toolsets.pytest.utils.cleanup_tables(session, base) async","text":"Truncate all tables for fast between-test cleanup.
Executes a single TRUNCATE … RESTART IDENTITY CASCADE statement across every table in base's metadata, which is significantly faster than dropping and re-creating tables between tests.
This is a no-op when the metadata contains no tables.
Parameters:
Name Type Description Defaultsession AsyncSession An active async database session.
requiredbase type[DeclarativeBase] SQLAlchemy DeclarativeBase class containing model metadata.
required Example@pytest.fixture\nasync def db_session(worker_db_url):\n async with create_db_session(worker_db_url, Base) as session:\n yield session\n await cleanup_tables(session, Base)\n","path":["Reference","pytest"],"tags":[]},{"location":"reference/schemas/","level":1,"title":"schemas module","text":"Here's the reference for all response models and types provided by the schemas module.
You can import them directly from fastapi_toolsets.schemas:
from fastapi_toolsets.schemas import (\n PydanticBase,\n ResponseStatus,\n ApiError,\n BaseResponse,\n Response,\n ErrorResponse,\n Pagination,\n PaginatedResponse,\n)\n","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.PydanticBase","level":2,"title":"fastapi_toolsets.schemas.PydanticBase","text":" Bases: BaseModel
Base class for all Pydantic models with common configuration.
","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.ResponseStatus","level":2,"title":"fastapi_toolsets.schemas.ResponseStatus","text":" Bases: str, Enum
Standard API response status.
","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.ApiError","level":2,"title":"fastapi_toolsets.schemas.ApiError","text":" Bases: PydanticBase
Structured API error definition.
Used to define standard error responses with consistent format.
Attributes:
Name Type Descriptioncode int HTTP status code
msg str Short error message
desc str Detailed error description
err_code str Application-specific error code (e.g., \"AUTH-401\")
","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.BaseResponse","level":2,"title":"fastapi_toolsets.schemas.BaseResponse","text":" Bases: PydanticBase
Base response structure for all API responses.
Attributes:
Name Type Descriptionstatus ResponseStatus SUCCESS or FAIL
message str Human-readable message
error_code str | None Error code if status is FAIL, None otherwise
","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.Response","level":2,"title":"fastapi_toolsets.schemas.Response","text":" Bases: BaseResponse, Generic[DataT]
Generic API response with data payload.
ExampleResponse[UserRead](data=user, message=\"User retrieved\")\n","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.ErrorResponse","level":2,"title":"fastapi_toolsets.schemas.ErrorResponse","text":" Bases: BaseResponse
Error response with additional description field.
Used for error responses that need more context.
","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.Pagination","level":2,"title":"fastapi_toolsets.schemas.Pagination = OffsetPagination module-attribute","text":"","path":["Reference","schemas module"],"tags":[]},{"location":"reference/schemas/#fastapi_toolsets.schemas.PaginatedResponse","level":2,"title":"fastapi_toolsets.schemas.PaginatedResponse","text":" Bases: BaseResponse, Generic[DataT]
Paginated API response for list endpoints.
","path":["Reference","schemas module"],"tags":[]}]}