> ## 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 Python API: All Public Framework Exports

> Complete reference for veloiq_framework public exports — app factory, base models, relationships, access control decorators, and utilities.

The `veloiq_framework` package exposes every symbol you need to build a full-stack VeloIQ application: the app factory, SQLModel base classes, a relationship wrapper with cardinality metadata, automatic CRUD router generation, role-based and row-level access control decorators, and FastAPI dependencies for auth and database sessions.

## Installation

```bash theme={null}
pip install veloiq-framework
```

***

## App factory

### `create_veloiq_app`

```python theme={null}
from veloiq_framework import create_veloiq_app, VeloIQConfig

app = create_veloiq_app(config: VeloIQConfig | None = None, **kwargs) -> FastAPI
```

Creates and returns a fully configured FastAPI application. When `config` is omitted, `VeloIQConfig` is constructed from environment variables and any `**kwargs` you pass. The returned `app` is a standard FastAPI instance — you can add routes, middleware, or dependencies on top.

<ParamField path="config" type="VeloIQConfig | None">
  A `VeloIQConfig` instance. If `None`, one is built from environment variables. See [Configuration](/reference/configuration).
</ParamField>

<ParamField path="**kwargs" type="Any">
  Forwarded to `VeloIQConfig` when `config` is not provided. Accepts the same fields as `VeloIQConfig`.
</ParamField>

<CodeGroup>
  ```python Minimal (env vars only) theme={null}
  from veloiq_framework import create_veloiq_app

  app = create_veloiq_app()   # DATABASE_URL read from environment
  ```

  ```python Explicit config theme={null}
  from veloiq_framework import create_veloiq_app, VeloIQConfig

  app = create_veloiq_app(VeloIQConfig(
      title="Acme Admin",
      database_url="postgresql://user:pass@localhost/acme",
      modules_dir="app/modules",
  ))
  ```
</CodeGroup>

<Note>
  `create_veloiq_app` raises `ValueError` if no `DATABASE_URL` can be determined from the config or the environment.
</Note>

***

## Base models

All base classes extend `SQLModel`. Declare your tables by inheriting from one of these classes with `table=True`.

### `FrameworkModel`

Minimal base model providing a standard auto-increment integer primary key named `id`. Use this when you do not need timestamp columns.

```python theme={null}
from veloiq_framework import FrameworkModel

class Category(FrameworkModel, table=True):
    __tablename__ = "category"
    name: str
    slug: str
```

| Field | Type            | Description                 |
| ----- | --------------- | --------------------------- |
| `id`  | `Optional[int]` | Auto-increment primary key. |

### `TimestampedModel`

Extends `FrameworkModel` with automatic `created_at` and `updated_at` columns. The schema generator appends these two fields after all fields you declare, so they appear last in every list, form, and detail view.

```python theme={null}
from veloiq_framework import TimestampedModel

class Order(TimestampedModel, table=True):
    __tablename__ = "order"
    reference: str
    total: float
    status: str = "pending"
```

| Field        | Type                 | Description                                         |
| ------------ | -------------------- | --------------------------------------------------- |
| `id`         | `Optional[int]`      | Inherited from `FrameworkModel`. Auto-increment PK. |
| `created_at` | `Optional[datetime]` | Set on insert. Never overwritten by CRUD updates.   |
| `updated_at` | `Optional[datetime]` | Updated automatically on every write.               |

### `StandardModel`

CubicWeb-compatible base model for applications that require `eid` as the primary key and `cw_` column naming conventions. The `eid` Python attribute maps to the `cw_eid` physical column. Use this only when migrating from CubicWeb or maintaining an existing CubicWeb schema; new applications should use `FrameworkModel` or `TimestampedModel`.

```python theme={null}
from veloiq_framework import StandardModel
from sqlmodel import Field
from sqlalchemy import Column, String

class LegacyItem(StandardModel, table=True):
    __tablename__ = "legacy_item"
    cw_name: str = Field(sa_column=Column("cw_name", String))
```

| Field               | Type                 | Description                                 |
| ------------------- | -------------------- | ------------------------------------------- |
| `eid`               | `Optional[int]`      | Maps to `cw_eid` column. Auto-increment PK. |
| `creation_date`     | `Optional[datetime]` | Set on insert.                              |
| `modification_date` | `Optional[datetime]` | Updated automatically on every write.       |

***

## Relationships

### `jm_relationship`

```python theme={null}
jm_relationship(
    *,
    min_items: int = 0,
    max_items: int | None = None,
    required: bool = False,
    **kwargs: Any,
) -> Any
```

SQLModel `Relationship` wrapper that attaches cardinality metadata. The DynamicResource UI component reads this metadata to render required/optional indicators and pagination hints. All `**kwargs` are passed through to SQLModel's `Relationship`.

<ParamField path="min_items" type="int" default="0">
  Minimum number of related items (used for UI validation hints).
</ParamField>

<ParamField path="max_items" type="int | None" default="None">
  Maximum number of related items. `None` means unbounded.
</ParamField>

<ParamField path="required" type="bool" default="False">
  Whether the relation is required.
</ParamField>

<ParamField path="**kwargs" type="Any">
  Passed directly to SQLModel's `Relationship` (e.g. `back_populates`, `link_model`).
</ParamField>

```python theme={null}
from typing import List, Optional
from sqlmodel import Field
from veloiq_framework import TimestampedModel, jm_relationship

class Customer(TimestampedModel, table=True):
    __tablename__ = "customer"
    name: str
    orders: List["Order"] = jm_relationship(back_populates="customer")

class Order(TimestampedModel, table=True):
    __tablename__ = "order"
    reference: str
    customer_id: Optional[int] = Field(default=None, foreign_key="customer.id")
    customer: Optional["Customer"] = jm_relationship(back_populates="orders")
```

### `RelationCardinality`

Dataclass stored in the SQLAlchemy relationship `info` dict by `jm_relationship`. You do not normally construct this directly.

| Field       | Type          | Default | Description                               |
| ----------- | ------------- | ------- | ----------------------------------------- |
| `min_items` | `int`         | `0`     | Minimum cardinality.                      |
| `max_items` | `int \| None` | `None`  | Maximum cardinality (`None` = unbounded). |
| `required`  | `bool`        | `False` | Whether at least one item is required.    |

### `get_pk_field_name`

```python theme={null}
get_pk_field_name(model_cls: type) -> str
```

Returns the Python attribute name for the primary key of a mapped SQLModel class. Falls back to `"id"` if inspection fails.

***

## CRUD router

### `create_crud_router`

```python theme={null}
create_crud_router(
    model_class: Type[T],
    *,
    prefix: str | None = None,
    tags: list[str] | None = None,
    pk_type: type = int,
) -> APIRouter
```

Generates a FastAPI `APIRouter` with standard list, get, create, update, and delete endpoints for `model_class`. See [CRUD Router](/reference/crud-router) for the full endpoint reference.

<ParamField path="model_class" type="Type[T]" required>
  A SQLModel table class.
</ParamField>

<ParamField path="prefix" type="str | None">
  URL prefix for all routes. Defaults to `/<tablename>`.
</ParamField>

<ParamField path="tags" type="list[str] | None">
  OpenAPI tags. Defaults to `[<tablename>]`.
</ParamField>

<ParamField path="pk_type" type="type" default="int">
  Python type of the primary key.
</ParamField>

```python theme={null}
from veloiq_framework.crud import create_crud_router
from .models import Task

router = create_crud_router(Task)
```

***

## Access control

VeloIQ provides three layers of declarative access control. All are opt-in and can be combined freely.

### Method set constants

| Constant        | Value                                                          | Description                |
| --------------- | -------------------------------------------------------------- | -------------------------- |
| `ALL_METHODS`   | `{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"}` | All HTTP methods.          |
| `WRITE_METHODS` | `{"GET", "POST", "PUT", "PATCH", "OPTIONS", "HEAD"}`           | All methods except DELETE. |
| `READ_METHODS`  | `{"GET", "OPTIONS", "HEAD"}`                                   | Read-only methods.         |

### `RoleDef`

```python theme={null}
@dataclass
class RoleDef:
    name: str
    methods: set[str]
    description: str = ""
    is_preset: bool = True
```

Defines a developer-declared role. Pass a list of `RoleDef` objects to `VeloIQConfig.roles`; they are upserted to the database on startup.

### `DEFAULT_ROLES`

Built-in role definitions used when `VeloIQConfig.roles` is not overridden.

| Name      | Methods         | Description                         |
| --------- | --------------- | ----------------------------------- |
| `Admin`   | `ALL_METHODS`   | Full administrative access.         |
| `Manager` | `WRITE_METHODS` | Create, edit, and view — no delete. |
| `Viewer`  | `READ_METHODS`  | Read-only access.                   |

### `model_access`

```python theme={null}
@model_access(Manager=["list", "show"], Viewer=["list", "show"])
class Invoice(TimestampedModel, table=True):
    ...
```

Class decorator that restricts which Refine actions a role may perform on this specific model. Roles not mentioned inherit their global permissions unchanged. Exceptions are restrictive only — they can narrow access, never grant beyond a role's global permissions.

### `veloiq_field`

```python theme={null}
veloiq_field(
    *,
    read_roles: list[str] | None = None,
    write_roles: list[str] | None = None,
    **kwargs: Any,
) -> Any
```

SQLModel/Pydantic field with optional per-role read and write restrictions. Wraps `pydantic.Field` and stores role metadata in `json_schema_extra` so the schema generator emits `readRoles`/`writeRoles` into TypeScript schemas. The CRUD router enforces these restrictions at runtime.

<ParamField path="read_roles" type="list[str] | None">
  Roles allowed to read this field. Absent means all roles can read it.
</ParamField>

<ParamField path="write_roles" type="list[str] | None">
  Roles allowed to write this field. Absent means all roles can write it.
</ParamField>

<ParamField path="**kwargs" type="Any">
  Passed through to `pydantic.Field` (e.g. `default`, `description`).
</ParamField>

```python theme={null}
from veloiq_framework import veloiq_field, TimestampedModel

class Employee(TimestampedModel, table=True):
    __tablename__ = "employee"
    name: str
    department: str
    salary: float = veloiq_field(
        default=0.0,
        read_roles=["Admin"],
        write_roles=["Admin"],
    )
    notes: str = veloiq_field(
        default="",
        write_roles=["Admin", "Manager"],
    )
```

### `rebac`

```python theme={null}
@rebac(
    *,
    filter=None,
    owner_field: str | None = None,
    tenant_field: str | None = None,
)
```

Class decorator for row-level access control. At least one of `filter`, `owner_field`, or `tenant_field` must be supplied. Multiple options are OR-combined — a row is visible if any rule allows it.

<ParamField path="filter" type="callable | None">
  A lambda `(user, cls, session) -> SQLAlchemy clause | True | False | None`. Return a WHERE clause for permitted rows, `True` for no restriction, or `False` to deny all rows.
</ParamField>

<ParamField path="owner_field" type="str | None">
  Name of a column pointing to `veloiq_user.id`. Shorthand for a filter that matches `cls.<field> == user["eid"]`.
</ParamField>

<ParamField path="tenant_field" type="str | None">
  Name of a column pointing to `veloiq_tenant.id`. Grants access when the tenant is one the authenticated user belongs to.
</ParamField>

```python theme={null}
from veloiq_framework import rebac, TimestampedModel
from sqlmodel import Field

@rebac(owner_field="created_by")
class Note(TimestampedModel, table=True):
    created_by: int = Field(foreign_key="veloiq_user.id")

@rebac(tenant_field="tenant_id")
class Contract(TimestampedModel, table=True):
    tenant_id: int = Field(foreign_key="veloiq_tenant.id")
```

<Note>
  `@rebac` applies to all roles, including Admin. To exempt Admins, return `True` from the filter when the user has the Admin role. Inaccessible rows return 404, not 403, to avoid leaking which IDs exist.
</Note>

### `rebac_subquery`

```python theme={null}
rebac_subquery(model_class: type, user: dict, session) -> Subquery
```

Returns a SQLAlchemy subquery of primary keys of `model_class` rows that `user` may access. Designed to be called inside a `@rebac(filter=…)` lambda to express relationship-based access. The target `model_class` must itself carry a `@rebac` decorator. Raises `ValueError` on circular dependencies.

```python theme={null}
from veloiq_framework import rebac, rebac_subquery, TimestampedModel

@rebac(filter=lambda user, cls, session:
           cls.folder_id.in_(rebac_subquery(Folder, user, session)))
class Document(TimestampedModel, table=True):
    folder_id: int
```

***

## Auth utilities

### `get_current_user`

FastAPI dependency that returns the authenticated user payload decoded from the JWT Bearer token. The payload is a dict containing at minimum `sub` (user ID as string) and `roles` (list of role names).

```python theme={null}
from fastapi import Depends
from veloiq_framework import get_current_user

@router.get("/me")
def me(user=Depends(get_current_user)):
    return user
```

### `require_role`

FastAPI dependency that raises HTTP 403 if the authenticated user does not hold at least one of the specified roles.

```python theme={null}
from fastapi import Depends
from veloiq_framework import require_role

@router.delete("/{id}")
def delete_item(id: int, _=Depends(require_role("Admin"))):
    ...
```

### `get_session`

FastAPI dependency that yields a `sqlmodel.Session` bound to the configured database engine. Use it in custom endpoints that need direct database access.

```python theme={null}
from fastapi import Depends
from sqlmodel import Session, select
from veloiq_framework import get_session
from .models import Order

@router.get("/pending")
def list_pending(session: Session = Depends(get_session)):
    return session.exec(select(Order).where(Order.status == "pending")).all()
```

***

<CardGroup cols={2}>
  <Card title="Configuration" icon="gear" href="/reference/configuration">
    Full VeloIQConfig field reference and environment variables.
  </Card>

  <Card title="CRUD Router" icon="route" href="/reference/crud-router">
    Auto-generated endpoint reference and query parameter details.
  </Card>
</CardGroup>
