Service layer base class with domain error hierarchy
Project description
csrd.service
Service-layer boilerplate for the CSRD pattern (Controller → Service → Repository / Delegate).
What it provides
| Component | Purpose |
|---|---|
BaseService |
Lightweight base class with per-class logger and request-context accessors (current_user(), current_request_id()) |
ServiceError |
Base domain exception — keeps business logic free of HTTPException |
NotFoundError |
404 — resource doesn't exist |
ConflictError |
409 — duplicate, version mismatch, etc. |
ValidationError |
422 — business-rule validation (not schema validation) |
AuthorizationError |
403 — authenticated user lacks permission |
DownstreamError |
502 — a delegate call to another service failed |
service_exception_handler |
FastAPI handler that maps any ServiceError → structured APIErrorResponse JSON |
Dependency tier
Tier 1 (standalone) csrd.models · csrd.lifespan · csrd.context
Tier 2 (uses Tier 1) csrd.delegate · csrd.repository · csrd.service ← NEW
Tier 3 (uses Tier 1+2) csrd.versioning
csrd.service depends on csrd.context and csrd.models only. It does not import csrd.repository or csrd.delegate — your services accept those as constructor params.
Quick start
from csrd.service import BaseService, NotFoundError, DownstreamError
class OrderService(BaseService):
def __init__(self, repo: OrderRepository, payments: PaymentsDelegate):
super().__init__()
self._repo = repo
self._payments = payments
async def get_order(self, order_id: int) -> Order:
order = await self._repo.get_by_id(order_id)
if order is None:
raise NotFoundError("Order not found", detail=f"order_id={order_id}")
return order
async def place_order(self, cart: Cart) -> Order:
order = await self._repo.create(cart)
try:
await self._payments.charge(order)
except Exception as exc:
raise DownstreamError("Payment failed", cause=exc) from exc
return order
Register the exception handler
With csrd.versioning:
from csrd.service import ServiceError, service_exception_handler
from csrd.versioning import VersionedApiConfig
configure_versioned_api(
app,
version_mapping=VERSIONS,
config=VersionedApiConfig(
ex_handlers=[(ServiceError, service_exception_handler)],
),
)
Or on a plain FastAPI app:
app.add_exception_handler(ServiceError, service_exception_handler)
Wire into FastAPI DI
from functools import cache
from typing import Annotated
from fastapi import Depends
@cache
def order_service_factory(repo: OrderRepository, payments: PaymentsDelegate):
return OrderService(repo, payments)
OrderServiceDep = Annotated[OrderService, Depends(order_service_factory)]
# In your endpoint:
@router.get("/orders/{order_id}")
async def get_order(service: OrderServiceDep, order_id: int):
return await service.get_order(order_id)
Installation
uv add csrd-service # or: pip install csrd-service
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file csrd_service-0.2.0.tar.gz.
File metadata
- Download URL: csrd_service-0.2.0.tar.gz
- Upload date:
- Size: 7.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ebad9fcbe4e0a836cebd27c4523edb16f854af2fd773bdfdbc1efb0eca8ab62
|
|
| MD5 |
ca1572510a8e42c449cbe4c8bbc38f89
|
|
| BLAKE2b-256 |
babd0154d80c32ee9781eeb255fff0101cfc6c611b7cb33e993df9a6561be357
|
Provenance
The following attestation bundles were made for csrd_service-0.2.0.tar.gz:
Publisher:
release.yml on csrd-api/fastapi-common
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
csrd_service-0.2.0.tar.gz -
Subject digest:
3ebad9fcbe4e0a836cebd27c4523edb16f854af2fd773bdfdbc1efb0eca8ab62 - Sigstore transparency entry: 1399324855
- Sigstore integration time:
-
Permalink:
csrd-api/fastapi-common@95e9dcebda181af776d7d696cc536fc7d91ad079 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/csrd-api
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@95e9dcebda181af776d7d696cc536fc7d91ad079 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file csrd_service-0.2.0-py3-none-any.whl.
File metadata
- Download URL: csrd_service-0.2.0-py3-none-any.whl
- Upload date:
- Size: 6.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be2a121231c0905852fe1e5423ea8bd384307733ed81cf059bf6b44357134d84
|
|
| MD5 |
5c8b9d286b4056c9183c8e5b97fbbec4
|
|
| BLAKE2b-256 |
272cccb2af0026b8e2627ef78f05b0805d59eed560b8088a01456a78c97b5607
|
Provenance
The following attestation bundles were made for csrd_service-0.2.0-py3-none-any.whl:
Publisher:
release.yml on csrd-api/fastapi-common
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
csrd_service-0.2.0-py3-none-any.whl -
Subject digest:
be2a121231c0905852fe1e5423ea8bd384307733ed81cf059bf6b44357134d84 - Sigstore transparency entry: 1399324930
- Sigstore integration time:
-
Permalink:
csrd-api/fastapi-common@95e9dcebda181af776d7d696cc536fc7d91ad079 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/csrd-api
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@95e9dcebda181af776d7d696cc536fc7d91ad079 -
Trigger Event:
workflow_dispatch
-
Statement type: