Docs / fastkit-cli / Quick Start

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.

5 minutes
fastkit-cli installed
fastkit-core in project venv

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:

bash
fastkit make module Invoice
Terminal
$ fastkit make module Invoice
Generating module: Invoice
Location : modules/invoices/
Model : Invoice
Table : invoices
Mode : sync
✓  __init__.py
✓  models.py
✓  schemas.py
✓  repository.py
✓  service.py
✓  router.py
✓  Registered model in alembic/env.py
Done! Next steps:
1. Define your fields in  modules/invoices/models.py
2. Add schemas in          modules/invoices/schemas.py
3. Run: fastkit migrate make -m 'create_invoices'

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:

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

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

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

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

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

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

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

bash
# Generate migration from model changes
fastkit migrate make -m "create_invoices"

# Apply all pending migrations
fastkit migrate run
Terminal
$ fastkit migrate make -m "create_invoices"
Generating revision...
✓  alembic/versions/20260101_create_invoices.py
$ fastkit migrate run
Running upgrade head...
✓  create_invoices — done

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:

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

MethodFull pathDescription
GET/api/v1/invoicesPaginated list
GET/api/v1/invoices/{id}Single invoice
POST/api/v1/invoicesCreate invoice
PUT/api/v1/invoices/{id}Update invoice
DELETE/api/v1/invoices/{id}Delete invoice

Step 6 — Start the Server

bash
fastkit server
Terminal
$ fastkit server
INFO: Uvicorn running on http://0.0.0.0:8000
INFO: Started reloader process
INFO: Application startup complete.

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:

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

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

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

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

Model class InvoiceItem
Table name invoice_items
Folder modules/invoice_items/
Router prefix /invoice_items
Service class 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:

bash
# 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.