> ## Documentation Index
> Fetch the complete documentation index at: https://docs.veloiq.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# VeloIQ Modules: Auto-Loaded Domain Packages

> VeloIQ modules are self-contained Python packages that the framework discovers and registers automatically — no manual wiring in main.py.

A VeloIQ module is a Python package that lives under `backend/app/modules/`. Each module owns one domain concept — its models, generated API, custom endpoints, and admin views. On startup, the framework scans `app/modules/*/` and registers every module it finds automatically. You never add import statements to `main.py`.

## Module directory layout

```
modules/orders/
├── __init__.py
├── models.py           # SQLModel table classes — you write this
├── api.py              # AUTO-GENERATED CRUD — do not edit
├── custom_api.py       # custom endpoints — you write this
└── admin/
    └── admin_views.py  # SQLAdmin view — you write this
```

## Creating a module

Use the CLI to scaffold a new module:

```bash theme={null}
veloiq add-module orders
```

Add optional flags to include extra files in the scaffold:

| Flag                | Effect                                                    |
| ------------------- | --------------------------------------------------------- |
| `--with-custom-api` | Creates `custom_api.py` with a starter router             |
| `--with-admin`      | Creates `admin/admin_views.py` with a starter `ModelView` |

```bash theme={null}
veloiq add-module orders --with-custom-api --with-admin
```

## models.py

Define your entities by inheriting from one of the framework base classes (`FrameworkModel`, `TimestampedModel`, or `StandardModel`). See [Models](/concepts/models) for the full reference on base classes, field types, and relationships.

## api.py — generated CRUD endpoints

After running `veloiq generate`, each module has an `api.py` that creates a complete CRUD router for your model:

```python theme={null}
# AUTO-GENERATED — do not edit. Run `veloiq generate` to update.
from veloiq_framework.crud import create_crud_router
from .models import Order

router = create_crud_router(Order)
```

`create_crud_router` generates these endpoints automatically:

| Method | Path          | Action                               |
| ------ | ------------- | ------------------------------------ |
| GET    | `/order`      | Paginated list (`?_start=0&_end=25`) |
| GET    | `/order/{id}` | Single record                        |
| POST   | `/order`      | Create                               |
| PUT    | `/order/{id}` | Full update                          |
| DELETE | `/order/{id}` | Delete                               |

List responses include `x-total-count` and `content-range` headers to support the Refine data provider pagination convention.

<Warning>Do not edit `api.py`. It is overwritten every time you run `veloiq generate`. Put all custom logic in `custom_api.py` instead.</Warning>

## custom\_api.py — custom endpoints

For business-specific endpoints, create `custom_api.py` in the module directory and import `router` from the generated `api.py`. Add routes to that router and the framework loader picks them up automatically.

```python theme={null}
from fastapi import Depends, HTTPException
from sqlmodel import Session, select

from veloiq_framework import get_session
from .api import router
from .models import Order


@router.post("/{order_id}/confirm")
def confirm_order(order_id: int, session: Session = Depends(get_session)):
    order = session.get(Order, order_id)
    if order is None:
        raise HTTPException(404, f"Order {order_id} not found")
    order.status = "confirmed"
    session.add(order)
    session.commit()
    session.refresh(order)
    return order.model_dump()


@router.get("/pending")
def list_pending(session: Session = Depends(get_session)):
    rows = session.exec(select(Order).where(Order.status == "pending")).all()
    return [r.model_dump() for r in rows]
```

The framework imports both `api.py` and `custom_api.py` on startup — you don't need to register the router anywhere else.

## admin/admin\_views.py — back-office views

Add an SQLAdmin `ModelView` to expose your model in the admin panel at `/admin/`.

```python theme={null}
from sqladmin import ModelView
from app.modules.orders.models import Order

class OrderAdmin(ModelView, model=Order):
    column_list = [Order.id, Order.reference, Order.status, Order.total]
    column_searchable_list = [Order.reference]
    column_sortable_list = [Order.total, Order.created_at]
    icon = "fa-solid fa-receipt"
```

## Frontend schema files

`veloiq generate` creates a TypeScript schema file for each module that the `DynamicResource` UI component reads to render list, detail, create, and edit views.

| File                       | Description                                    |
| -------------------------- | ---------------------------------------------- |
| `{module}Schema.gen.ts`    | Auto-generated field definitions — do not edit |
| `{module}Schema.manual.ts` | Your overrides and extensions — you write this |

To customise or extend the generated schema, create `{module}Schema.manual.ts` in the same directory and merge it into `{module}Schema.ts`. Refer to the `@veloiq/ui` documentation for the `ModelDef` and `FieldDef` type reference.

## Module auto-loading

On every startup, `create_veloiq_app()` scans `app/modules/*/` and, for each subdirectory that contains an `__init__.py`, imports `models.py`, `api.py`, and `custom_api.py` (if present) and registers any `ModelView` classes found in `admin/admin_views.py`.

Because of this automatic discovery, `main.py` never grows beyond a single line:

```python theme={null}
app = create_veloiq_app()
```

<Note>Pass a `VeloIQConfig` instance to `create_veloiq_app()` when you need to configure roles, the database URL, or other settings. The defaults work for local development with SQLite.</Note>

<CardGroup cols={2}>
  <Card title="Models" href="/concepts/models">
    Define fields, base classes, and relationships
  </Card>

  <Card title="Access Control" href="/concepts/access-control">
    Apply RBAC and ReBAC to your modules and fields
  </Card>
</CardGroup>
