Quick Start
From an empty project to a running, migrated Invoice API — in under 5 minutes. You'll see exactly what gets generated and how to wire it up.
Step 1 — Generate a Module
Run fastkit make module with a model name. The CLI generates all six files and registers the model in Alembic automatically:
fastkit make module Invoice
Step 2 — Explore the Generated Files
Six files are created in modules/invoices/. Each file is complete and immediately runnable — it just needs your domain-specific fields:
SQLAlchemy model using FastKit Core mixins. Add your fields in the marked section:
# modules/invoices/models.py
from fastkit_core.database import BaseWithTimestamps, IntIdMixin
# from fastkit_core.database import UUIDMixin, SoftDeleteMixin, SlugMixin
class Invoice(BaseWithTimestamps, IntIdMixin):
__tablename__ = "invoices"
# Define your fields here
Commented imports show available mixins — uncomment what you need: UUIDMixin for UUID primary keys, SoftDeleteMixin for soft deletes, SlugMixin for URL slugs.
Pydantic schemas for Create, Update, and Response — three schemas ready to fill in:
# modules/invoices/schemas.py
from fastkit_core.validation import BaseSchema
class InvoiceCreate(BaseSchema):
pass # Define your fields here
class InvoiceUpdate(BaseSchema):
pass # All fields optional for partial updates
class InvoiceResponse(BaseSchema):
id: int
model_config = {"from_attributes": True}
Repository class wired to the model — extends FastKit Core's Repository:
# modules/invoices/repository.py
from fastkit_core.database import Repository
from sqlalchemy.orm import Session
from .models import Invoice
class InvoiceRepository(Repository[Invoice]):
def __init__(self, session: Session):
super().__init__(Invoice, session)
Service class extending BaseCrudService — add your business logic here:
# modules/invoices/service.py
from fastkit_core.services import BaseCrudService
from sqlalchemy.orm import Session
from .models import Invoice
from .schemas import InvoiceCreate, InvoiceUpdate, InvoiceResponse
from .repository import InvoiceRepository
class InvoiceService(BaseCrudService[Invoice, InvoiceCreate, InvoiceUpdate, InvoiceResponse]):
def __init__(self, session: Session):
super().__init__(
InvoiceRepository(session),
response_schema=InvoiceResponse
)
FastAPI router with five CRUD endpoints — immediately usable after you define your schemas:
# modules/invoices/router.py
from fastapi import APIRouter, Depends
from fastkit_core.database import get_db
from fastkit_core.http import success_response, paginated_response
from sqlalchemy.orm import Session
from .schemas import InvoiceCreate, InvoiceUpdate
from .service import InvoiceService
router = APIRouter(prefix="/invoices", tags=["invoices"])
def get_service(session: Session = Depends(get_db)) -> InvoiceService:
return InvoiceService(session)
@router.get("/")
def index(page: int = 1, per_page: int = 20, svc: InvoiceService = Depends(get_service)):
items, meta = svc.paginate(page=page, per_page=per_page)
return paginated_response(items=[i.model_dump() for i in items], pagination=meta)
@router.get("/{id}")
def show(id: int, svc: InvoiceService = Depends(get_service)):
return success_response(data=svc.find_or_fail(id).model_dump())
@router.post("/", status_code=201)
def store(data: InvoiceCreate, svc: InvoiceService = Depends(get_service)):
return success_response(data=svc.create(data).model_dump())
@router.put("/{id}")
def update(id: int, data: InvoiceUpdate, svc: InvoiceService = Depends(get_service)):
return success_response(data=svc.update(id, data).model_dump())
@router.delete("/{id}", status_code=204)
def destroy(id: int, svc: InvoiceService = Depends(get_service)):
svc.delete(id)
Step 3 — Define Your Fields
Edit the two files that need your domain-specific content. Everything else is ready:
models.py — add SQLAlchemy columns
# modules/invoices/models.py
from fastkit_core.database import BaseWithTimestamps, IntIdMixin, SoftDeleteMixin
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import String, Float, Integer, ForeignKey
class Invoice(BaseWithTimestamps, IntIdMixin, SoftDeleteMixin):
__tablename__ = "invoices"
client_id: Mapped[int] = mapped_column(Integer, ForeignKey("clients.id"))
number: Mapped[str] = mapped_column(String(50), unique=True)
total: Mapped[float] = mapped_column(Float, default=0.0)
status: Mapped[str] = mapped_column(String(20), default="draft")
schemas.py — add Pydantic fields
# modules/invoices/schemas.py
from fastkit_core.validation import BaseSchema
from datetime import datetime
class InvoiceCreate(BaseSchema):
client_id: int
number: str
total: float = 0.0
status: str = "draft"
class InvoiceUpdate(BaseSchema):
number: str | None = None
total: float | None = None
status: str | None = None
class InvoiceResponse(BaseSchema):
id: int
client_id: int
number: str
total: float
status: str
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
Step 4 — Run Migrations
Generate a migration from your model definition, then apply it:
# Generate migration from model changes
fastkit migrate make -m "create_invoices"
# Apply all pending migrations
fastkit migrate run
fastkit migrate make is a wrapper for alembic revision --autogenerate. fastkit migrate run is a wrapper for alembic upgrade head. See the migrate docs for rollback and status commands.
Step 5 — Register the Router
Include the generated router in your main FastAPI application:
# main.py
from fastapi import FastAPI
from fastkit_core.config import ConfigManager
from fastkit_core.database import init_database
from modules.invoices.router import router as invoices_router
app = FastAPI(title="My API")
config = ConfigManager(modules=['database'], auto_load=True)
init_database(config)
# Include the generated router
app.include_router(invoices_router, prefix="/api/v1")
This registers these endpoints under /api/v1:
| Method | Full path | Description |
|---|---|---|
| GET | /api/v1/invoices | Paginated list |
| GET | /api/v1/invoices/{id} | Single invoice |
| POST | /api/v1/invoices | Create invoice |
| PUT | /api/v1/invoices/{id} | Update invoice |
| DELETE | /api/v1/invoices/{id} | Delete invoice |
Step 6 — Start the Server
fastkit server
Your Invoice API is live. Visit http://localhost:8000/docs for the interactive Swagger UI — all five endpoints are there, fully documented, ready to test.
Async Variant
Add --async to generate async-ready files using AsyncSession and get_async_db throughout:
fastkit make module Invoice --async
The async variant generates the same six files but with async patterns throughout. A snippet from the generated service.py:
# modules/invoices/service.py (async)
from fastkit_core.services import AsyncBaseCrudService
from fastkit_core.database import AsyncRepository
from sqlalchemy.ext.asyncio import AsyncSession
class InvoiceService(AsyncBaseCrudService[Invoice, InvoiceCreate, InvoiceUpdate, InvoiceResponse]):
def __init__(self, session: AsyncSession):
super().__init__(
AsyncRepository(Invoice, session),
response_schema=InvoiceResponse
)
And the generated router.py uses get_async_db and await on every service call:
# modules/invoices/router.py (async)
from fastkit_core.database import get_async_db
from sqlalchemy.ext.asyncio import AsyncSession
def get_service(session: AsyncSession = Depends(get_async_db)) -> InvoiceService:
return InvoiceService(session)
@router.get("/")
async def index(page: int = 1, per_page: int = 20, svc = Depends(get_service)):
items, meta = await svc.paginate(page=page, per_page=per_page)
return paginated_response(items=[i.model_dump() for i in items], pagination=meta)
@router.post("/", status_code=201)
async def store(data: InvoiceCreate, svc = Depends(get_service)):
return success_response(data=(await svc.create(data)).model_dump())
Compound Names
Multi-word model names are handled automatically in every format:
# PascalCase — preferred
fastkit make module InvoiceItem
# snake_case — also works
fastkit make module invoice_item
Both produce identical output — the CLI converts to all required formats:
InvoiceItem
invoice_items
modules/invoice_items/
/invoice_items
InvoiceItemService
Generating Individual Files
If you need just one file — for example you already have a model and just need a router — generate individual components with sub-commands:
# Generate only the model
fastkit make model Invoice
# Generate only the schemas
fastkit make schema Invoice
# Generate only the repository
fastkit make repository Invoice
# Generate only the service
fastkit make service Invoice --async
# Generate only the router
fastkit make router Invoice --async
All sub-commands support --path to specify the target directory and --force to overwrite existing files. See the make command docs for the full options reference.