Quick Start
From pip install to running API in 60 seconds. Add ZCore pieces only where they help.
Every step below is optional. ZCore is designed to be an incremental, opt-in companion for FastAPI. This guide demonstrates the fastest integration path. Feel free to skip any component that does not suit your project's architectural requirements.
1. Install
pip install fastapi-zcore-framework[all]ZCore extends FastAPI by providing modular helper patterns — it does not hide or replace standard FastAPI structures.
2. Scaffold a Project
Use the built-in interactive CLI tool to quickly bootstrap a clean, modular starting structure:
# Interactive setup (prompts for DB driver, uv/pip, and virtual environment)
zc init my_api && cd my_api
# Or non-interactive default (SQLite driver)
# zc init my_api --db sqlite -y && cd my_apiThis command automatically generates a production-ready starting layout, sets up an isolated .venv, and creates a fresh cryptographic SECRET_KEY:
main.py is a standard FastAPI entrypoint. ZCore does not introduce hidden magic; you retain absolute control over the FastAPI application instance.
3. Create an App Module (Domain)
ZCore projects are organized as decoupled, modular domains. Let's scaffold a tasks domain:
zc startapp tasks --templateThis command generates a structured, independent directory layout for your domain directly in the project directory:
4. Define a Model & Schemas
Open the auto-generated tasks/models.py file and customize your standard SQLAlchemy 2.0 model:
# tasks/models.py
import uuid
from sqlalchemy.orm import Mapped, mapped_column
from zcore import Base
class Task(Base):
__tablename__ = "tasks"
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
title: Mapped[str]
is_completed: Mapped[bool] = mapped_column(default=False)Let's also define the basic schemas in tasks/schemas.py:
# tasks/schemas.py
import uuid
from pydantic import ConfigDict
from zcore import Zchema
class TaskBase(Zchema):
__model__ = "tasks"
title: str
is_completed: bool = False
class TaskCreate(TaskBase):
pass
class TaskUpdate(TaskBase):
title: str | None = None
is_completed: bool | None = None
class TaskResponse(TaskBase):
id: uuid.UUID
model_config = ConfigDict(from_attributes=True)At this stage, you have a normal declarative model. You can stop right here and write raw SQLAlchemy queries using AsyncSession.
Or read on to see how ZCore can optionally reduce your boilerplate.
Tip: Don't forget to include your domain's router inside the plugin's setup method:
# tasks/plugin.py
from fastapi import FastAPI
from zcore import Plugin
from .routers import router_instance
class TaskPlugin(Plugin):
name = "tasks"
version = "0.1.0"
dependencies = []
def setup(self, app: FastAPI) -> None:
app.include_router(router_instance.router)5. Register the Plugin
For the Kernel to recognize your domain and attach its routes to the FastAPI application, register the generated TaskPlugin inside your main.py file.
Open main.py and register the plugin before kernel.setup(app):
# main.py
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from fastapi import FastAPI
from zcore import (
Kernel,
db_manager,
register_db_event_dispatcher,
register_exception_handlers,
settings,
)
from zcore.logging import setup_logging
from zcore.web import RequestLogMiddleware, ScopedDependencyMiddleware
# Import your domain plugin
from tasks.plugin import TaskPlugin # <--- ADD THIS
setup_logging()
# Initialize Database Manager using structured settings
db_manager.init_app(config=settings.DATABASE)
kernel = Kernel()
register_db_event_dispatcher(kernel.dispatcher)
# Register the domain plugin
kernel.add_plugin(TaskPlugin()) # <--- ADD THIS
app = FastAPI(
title=settings.PROJECT_NAME,
lifespan=kernel.lifespan
)
kernel.setup(app)
app.add_middleware(RequestLogMiddleware)
app.add_middleware(ScopedDependencyMiddleware)
register_exception_handlers(app)
@app.get("/")
async def root():
return {
"status": "healthy",
"framework": "ZCore",
"version": "0.1.0-rc.2",
"debug": settings.DEBUG
}6. Run the Server
Start the development server with live reload and automatic environment resolution:
zc runYour API is live at http://127.0.0.1:8000.
If you navigate to http://127.0.0.1:8000/docs, you will see the standard FastAPI Swagger UI. Let's incrementally adopt ZCore features — only the ones you actually need.
Now Add ZCore (Optional)
Each layer is decoupled. You can pick any single component without being forced to adopt the rest.
Add Router — Auto-generated, secure endpoints
Orchestrated. Requires a Service and Repository configuration to auto-scaffold endpoints.
Open tasks/routers.py and define your router configuration:
# tasks/routers.py
from zcore import BaseRouter
from .models import Task
from .schemas import TaskCreate, TaskResponse, TaskUpdate
from .services import TaskService
class TaskRouter(BaseRouter[TaskCreate, TaskUpdate]):
model = Task
create_schema = TaskCreate
update_schema = TaskUpdate
schema_out = TaskResponse
service = TaskService
prefix = "/tasks"
tags = ["Tasks"]
router_instance = TaskRouter()Now, ensure the router is included in tasks/plugin.py:
# tasks/plugin.py
from fastapi import FastAPI
from zcore import Plugin
from .routers import router_instance
class TaskPlugin(Plugin):
name = "tasks"
version = "0.1.0"
dependencies = []
def setup(self, app: FastAPI) -> None:
app.include_router(router_instance.router)
async def before_startup(self) -> None:
pass
async def on_startup(self) -> None:
pass
async def after_startup(self) -> None:
pass
async def on_shutdown(self) -> None:
passThis single class scaffolds 8 secure endpoints out-of-the-box: POST /, GET /{id}, GET / (with pagination), POST /search, POST /lookup, PUT /{id}, PATCH /{id}, and DELETE /{id} (with optional ?force=true).
Or don't. Write standard @router.get and @router.post handlers inside your plugin.py setup method. You get the exact same raw FastAPI performance either way.
Architecture Summary: When to reach for ZCore
| When you want to... | Use this ZCore component | Or write this natively |
|---|---|---|
| Stop writing repetitive CRUD queries | BaseRepository | Raw SQLAlchemy query executions |
| Execute validation and hooks before/after DB writes | BaseService | Code inside your route functions |
| Dynamically hide fields on a schema based on user roles | Zchema | Multiple separate Pydantic schemas |
| Group database writes and isolate domain events | UnitOfWork | Manual session.commit() blocks |
| Scaffold standard CRUD API endpoints with zero boilerplate | BaseRouter | Explicit @router path functions |
| Auto-wire service and repository dependencies | Inject[T] | Hand-written Depends() parameters |
| Run isolated background jobs with IoC injection | background_task | Manual session & scope handling |
| Normalize API errors into a unified envelope | register_exception_handlers | Manual @app.exception_handler definitions |
Every native pattern in the right-hand column remains 100% valid. ZCore simply streamlines your architecture when you want to write less boilerplate.