Batteries-included FastAPI building blocks: auth, database, events, permissions, email, pagination, Redis, rate limiting, health checks.
Project description
qstack
Batteries-included building blocks for FastAPI: auth with account activation, async SQLAlchemy, role-based permissions, an in-process event bus, pagination, Redis utilities, email, health checks.
What is this?
Most FastAPI projects rebuild the same foundation from scratch: JWT auth, database layer, exception handling, pagination, Redis helpers, middleware. qstack ships all of it as composable primitives you drop into any async project. It's not a framework — no magic, no required directory layout, no hidden global state. Every component is a factory you wire up yourself.
Features
| Feature | Description |
|---|---|
| App factory | create_app() pre-wires exception handlers, CORS, request logging |
| Settings | QSettings via pydantic-settings with .env and SMTP mixin |
| Async SQLAlchemy | Base with id/created_at/updated_at, engine/session factories |
| Repository | BaseRepository[T] CRUD + filtering + pagination + optional event emission |
| Service layer | BaseService[T] with ownership checks |
| Auth | JWT, bcrypt, brute-force protection, /auth/register, /auth/login, /auth/me |
| Account activation | Redis-backed UUID tokens, auto-email, /auth/activate/{token}, /auth/resend-activation |
| Events | In-process EventBus with sync+async listeners, auto-emitted from repo/service |
| Permissions | Role/Permission models, PolicyEnforcer, require_permission / require_role deps |
| Exceptions | QException hierarchy auto-converts to JSON |
| Pagination | PaginationParams dependency + generic PaginatedResponse[T] |
| Redis | Client factory, JSON cache, rate limiter, token blacklist |
EmailService (SMTP + Jinja2 templates), send, send_template, send_bulk |
|
| Health | /health endpoint verifying DB connectivity |
| Logging middleware | Method, path, status, duration per request |
Installation
pip install qstack # Core only
pip install qstack[postgres] # + asyncpg, psycopg2-binary
pip install qstack[redis] # + redis
pip install qstack[all] # All extras
pip install qstack[all,dev] # + pytest, ruff, fakeredis, aiosqlite
Python 3.10+.
Quick Start
# main.py
from contextlib import asynccontextmanager
from fastapi import FastAPI
from qstack.app import create_app
from qstack.auth.models import UserMixin
from qstack.auth.router import create_auth_router
from qstack.config import QSettings
from qstack.database.base import Base
from qstack.database.dependencies import get_db as _get_db
from qstack.database.session import create_engine, create_session_factory
from qstack.health.router import create_health_router
class User(UserMixin, Base):
__tablename__ = "users"
settings = QSettings(DATABASE_URL="sqlite+aiosqlite:///./app.db")
engine = create_engine(settings.DATABASE_URL)
SessionLocal = create_session_factory(engine)
async def get_db():
async for s in _get_db(SessionLocal):
yield s
def user_factory(email, username, hashed_password):
return User(email=email, username=username, hashed_password=hashed_password)
@asynccontextmanager
async def lifespan(app: FastAPI):
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
await engine.dispose()
app = create_app(settings, title="Demo", lifespan=lifespan)
app.include_router(create_auth_router(
user_model=User,
get_db=get_db,
jwt_secret=settings.JWT_SECRET,
jwt_algorithm=settings.JWT_ALGORITHM,
access_token_expire_minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES,
user_factory=user_factory,
))
app.include_router(create_health_router(engine))
pip install "qstack[all]" uvicorn aiosqlite
uvicorn main:app --reload
Available: POST /auth/register, POST /auth/login, GET /auth/me, GET /health.
To enable account activation add redis_client=redis, email_service=email, require_activation=True to create_auth_router. See DOCUMENTATION.md — Account Activation.
Project Structure
my_api/
├── app/
│ ├── main.py # create_app, mount routers
│ ├── config.py # Settings(QSettings)
│ ├── database.py # engine, SessionLocal, get_db
│ ├── models.py # SQLAlchemy models (Base subclasses)
│ ├── repositories.py # BaseRepository subclasses
│ ├── services.py # BaseService subclasses
│ ├── routers/ # domain routes
│ └── policies/ # PolicyEnforcer classes
├── alembic/
├── requirements.txt
└── .env
See DOCUMENTATION.md.
Documentation
DOCUMENTATION.md— full library API reference, examples, integration guide.CLI.md—qstackCLI reference (project scaffolding, code generation, Alembic wrappers, dev/test/doctor).
CLI (qstack)
pip install qstack-cli
qstack new my-api # interactive project scaffold
qstack generate crud Task # model + schema + repo + service + router
qstack db migrate "init" # alembic autogenerate wrapper
qstack dev # docker compose up + uvicorn --reload
qstack doctor # health check
Full reference: CLI.md.
Configuration
QSettings loads from env vars / .env:
| Variable | Default | Notes |
|---|---|---|
DATABASE_URL |
postgresql+asyncpg://... |
async DB URL |
DATABASE_SYNC_URL |
postgresql+psycopg2://... |
for Alembic |
JWT_SECRET |
change-me |
sign tokens |
JWT_ALGORITHM |
HS256 |
|
JWT_ACCESS_TOKEN_EXPIRE_MINUTES |
30 |
|
JWT_REFRESH_TOKEN_EXPIRE_MINUTES |
10080 |
7 days |
CORS_ORIGINS |
["*"] |
|
REDIS_URL |
redis://localhost:6379/0 |
|
SMTP_HOST |
localhost |
|
SMTP_PORT |
587 |
|
SMTP_USERNAME |
"" |
|
SMTP_PASSWORD |
"" |
|
SMTP_FROM_EMAIL |
noreply@example.com |
|
SMTP_USE_TLS |
True |
Subclass to add project-specific fields:
from qstack.config import QSettings
class Settings(QSettings):
STRIPE_API_KEY: str = ""
settings = Settings()
Contributing
git clone https://github.com/yourname/qstack.git
cd qstack
pip install -e ".[all,dev]"
pytest
ruff check qstack/
License
MIT
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 qstack-0.2.1.tar.gz.
File metadata
- Download URL: qstack-0.2.1.tar.gz
- Upload date:
- Size: 35.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f371804c591456d6126ea83ceae195fe0eeb2e08f2d9913c00cacd19a5e3e1d
|
|
| MD5 |
84fb789125691456e2011406afb0a471
|
|
| BLAKE2b-256 |
f5339dd631639d26814c9fb00425dade1cac19247ffef9eb667491d398825a17
|
Provenance
The following attestation bundles were made for qstack-0.2.1.tar.gz:
Publisher:
workflow.yml on QukzScpz/fastapi-q-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qstack-0.2.1.tar.gz -
Subject digest:
7f371804c591456d6126ea83ceae195fe0eeb2e08f2d9913c00cacd19a5e3e1d - Sigstore transparency entry: 1319878097
- Sigstore integration time:
-
Permalink:
QukzScpz/fastapi-q-core@c4653e1f2959161a78124c937420fb97af928fa6 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/QukzScpz
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@c4653e1f2959161a78124c937420fb97af928fa6 -
Trigger Event:
push
-
Statement type:
File details
Details for the file qstack-0.2.1-py3-none-any.whl.
File metadata
- Download URL: qstack-0.2.1-py3-none-any.whl
- Upload date:
- Size: 55.4 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 |
856ceef5f0ca699daf7aef575d45741117519f101bdbcdc944d52e5b116b5e74
|
|
| MD5 |
277c95670e7d3f9e6494f9b0ee06d3ce
|
|
| BLAKE2b-256 |
61ed03eb9304df25c9efa5669972bd411db8d52d6fa0af58c340524e75a8e424
|
Provenance
The following attestation bundles were made for qstack-0.2.1-py3-none-any.whl:
Publisher:
workflow.yml on QukzScpz/fastapi-q-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
qstack-0.2.1-py3-none-any.whl -
Subject digest:
856ceef5f0ca699daf7aef575d45741117519f101bdbcdc944d52e5b116b5e74 - Sigstore transparency entry: 1319878210
- Sigstore integration time:
-
Permalink:
QukzScpz/fastapi-q-core@c4653e1f2959161a78124c937420fb97af928fa6 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/QukzScpz
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@c4653e1f2959161a78124c937420fb97af928fa6 -
Trigger Event:
push
-
Statement type: