Skip to content

5-Minute Quickstart

From zero to an endpoint that responds, without cookiecutter, so you can see what is actually happening.

In a hurry?

Go straight to the cookiecutter scaffold - a complete service with tests, Docker, and CI in 30 seconds.

1. Install

python3.14 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install "mic-core[fastapi]"

2. Write a handler

main.py:

from fastapi import FastAPI
from mic.contracts import StrictSchema, wrap_http_success
from mic.http import HttpResponseSpec, HttpRouteSpec
from mic.fastapi import build_fastapi_router
from mic.model.errors import DomainError


# 1. Strict input schema
class CreateItem(StrictSchema):
    name: str
    quantity: int = 0


# 2. Framework-neutral handler
def _create(*, payload: CreateItem) -> HttpResponseSpec:
    if payload.quantity < 0:
        raise DomainError(
            code="items.invalid_quantity",
            message="quantity must be >= 0",
            details={"received_quantity": payload.quantity},
        )
    return HttpResponseSpec(
        status_code=201,
        body=wrap_http_success(
            {"name": payload.name, "qty": payload.quantity}
        ),
    )


# 3. Wiring
routes = (
    HttpRouteSpec(
        method="POST",
        path="/api/v1/items",
        name="items_v1_create",
        category="items",
        handler=_create,
    ),
)

app = FastAPI()
app.include_router(build_fastapi_router(routes=routes))

3. Run it

pip install uvicorn
uvicorn main:app --reload

4. Call it

# Happy path
curl -X POST http://localhost:8000/api/v1/items \
    -H "Content-Type: application/json" \
    -d '{"name": "widget", "quantity": 3}'
# {"success":true,"data":{"name":"widget","qty":3},"meta":null}

# DomainError
curl -X POST http://localhost:8000/api/v1/items \
    -H "Content-Type: application/json" \
    -d '{"name": "x", "quantity": -1}'
# 400 {"success":false,"code":"items.invalid_quantity","message":"...","details":{"received_quantity":-1}}

# extra=forbid
curl -X POST http://localhost:8000/api/v1/items \
    -H "Content-Type: application/json" \
    -d '{"name": "x", "secret": "boom"}'
# 400 {"success":false,"code":"http.validation_error","message":"...","details":[...]}

5. Switch to Litestar Without Touching the Handler

pip install "mic-core[litestar]"

Change only the final wiring:

# Before (FastAPI)
from fastapi import FastAPI
from mic.fastapi import build_fastapi_router

app = FastAPI()
app.include_router(build_fastapi_router(routes=routes))

# After (Litestar)
from litestar import Litestar
from mic.litestar import build_litestar_router

app = Litestar(route_handlers=[build_litestar_router(routes=routes)])

The _create handler did not change at all. The curl commands above produce the same responses. That is MIC.

What Comes Next?