ZCore LogoZCore

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_api

This command automatically generates a production-ready starting layout, sets up an isolated .venv, and creates a fresh cryptographic SECRET_KEY:

main.py
.env
requirements.txt
.gitignore
zcore_dev.db

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 --template

This command generates a structured, independent directory layout for your domain directly in the project directory:

__init__.py
models.py
schemas.py
repositories.py
services.py
routers.py
plugin.py
test_tasks.py
main.py
.env
requirements.txt
.gitignore

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 run

Your 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:
        pass

This 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 componentOr write this natively
Stop writing repetitive CRUD queriesBaseRepositoryRaw SQLAlchemy query executions
Execute validation and hooks before/after DB writesBaseServiceCode inside your route functions
Dynamically hide fields on a schema based on user rolesZchemaMultiple separate Pydantic schemas
Group database writes and isolate domain eventsUnitOfWorkManual session.commit() blocks
Scaffold standard CRUD API endpoints with zero boilerplateBaseRouterExplicit @router path functions
Auto-wire service and repository dependenciesInject[T]Hand-written Depends() parameters
Run isolated background jobs with IoC injectionbackground_taskManual session & scope handling
Normalize API errors into a unified enveloperegister_exception_handlersManual @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.

On this page