> ## 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 Auto-Generated CRUD REST API Reference

> VeloIQ's create_crud_router generates paginated list, get, create, update, and delete endpoints automatically from your SQLModel model.

`create_crud_router` generates five standard REST endpoints for any SQLModel table class and returns them as a FastAPI `APIRouter`. The generated router is the entire API layer for the common case — a database table with list, get, create, update, and delete operations. Each generated module's `api.py` uses this function, and the module auto-loader registers the router with the FastAPI application automatically.

## Generated endpoints

The URL prefix defaults to `/<tablename>` based on the model's `__tablename__` attribute.

| Method   | Path            | Description                                                                                |
| -------- | --------------- | ------------------------------------------------------------------------------------------ |
| `GET`    | `/{model}`      | Paginated list. Accepts `_start`, `_end`, `_sort`, `_order`, and column filter parameters. |
| `GET`    | `/{model}/{id}` | Fetch a single record by primary key.                                                      |
| `POST`   | `/{model}`      | Create a new record. Returns the created record with status 201.                           |
| `PUT`    | `/{model}/{id}` | Full update — replaces all writable fields.                                                |
| `PATCH`  | `/{model}/{id}` | Partial update — sets only the fields present in the payload.                              |
| `DELETE` | `/{model}/{id}` | Delete a record. Returns status 204.                                                       |

***

## List endpoint

### Query parameters

| Parameter | Type  | Default | Description                                                                        |
| --------- | ----- | ------- | ---------------------------------------------------------------------------------- |
| `_start`  | `int` | `0`     | Zero-based offset of the first record to return.                                   |
| `_end`    | `int` | `25`    | Zero-based offset of the last record (exclusive). Returns `_end - _start` records. |

### Column filters

Any query parameter whose name matches a column name on the model is applied as an exact equality filter:

```
GET /order?status=pending
```

Append `__ilike` to a column name for a case-insensitive contains match:

```
GET /order?reference__ilike=INV-2024
```

Parameters whose names begin with `_` are reserved for pagination and are not interpreted as column filters.

### Response headers

List responses include two headers required by the Refine data provider pagination convention:

| Header          | Format                        | Example           |
| --------------- | ----------------------------- | ----------------- |
| `x-total-count` | Integer string                | `"42"`            |
| `content-range` | `items <start>-<end>/<total>` | `"items 0-25/42"` |

These headers are exposed via CORS (`expose_headers` in the middleware configuration) so the frontend can read them cross-origin.

### Example list response

```http theme={null}
GET /task?_start=0&_end=10&status=open HTTP/1.1
Authorization: Bearer <token>

HTTP/1.1 200 OK
x-total-count: 3
content-range: items 0-3/3
Content-Type: application/json

[
  { "id": 1, "title": "Design wireframes", "status": "open", "_label": "Design wireframes" },
  { "id": 2, "title": "Write tests", "status": "open", "_label": "Write tests" },
  { "id": 3, "title": "Deploy staging", "status": "open", "_label": "Deploy staging" }
]
```

<Note>
  Every record in list and get responses includes a `_label` field — a human-readable string computed by the model's `build_model_str_label` method. The frontend uses this for relation selectors and breadcrumbs.
</Note>

***

## Authentication

When `auth_enabled` is `True` (the default), all generated endpoints require a valid JWT Bearer token in the `Authorization` header. Unauthenticated requests receive a `401` response. Requests with a valid token but insufficient role permissions receive `403`.

```http theme={null}
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```

Disable auth enforcement for development by setting `VELOIQ_AUTH_DISABLED=1`.

***

## Access control integration

The CRUD router integrates with all three access control layers automatically:

* **Layer 1 (global roles):** The RBAC middleware checks whether the user's roles permit the HTTP method before the endpoint handler runs. Unauthorized methods return `403`.
* **Layer 2 (model-level):** `@model_access` exceptions are enforced per-model. A Viewer that has read-only global permissions and is further restricted on a specific model via `@model_access` will receive `403` on forbidden actions.
* **Layer 3 (field-level):** `veloiq_field(read_roles=…, write_roles=…)` restrictions are applied at runtime. Read-restricted fields are silently omitted from responses; write-restricted fields are silently dropped from create/update payloads.
* **ReBAC (row-level):** If the model carries a `@rebac` decorator, the list query and single-record fetch both apply the row-level filter. Inaccessible rows return `404`.

***

## Usage

```python theme={null}
# modules/orders/api.py — AUTO-GENERATED, do not edit
from veloiq_framework.crud import create_crud_router
from .models import Order

router = create_crud_router(Order)
```

You can add custom endpoints to the generated router without editing `api.py`. Import `router` from `api.py` in your `custom_api.py`:

```python theme={null}
# modules/orders/custom_api.py
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()
```

The framework loader imports both `api.py` and `custom_api.py` automatically. See [Custom Endpoints](/guides/custom-endpoints) for the full guide.

***

<CardGroup cols={2}>
  <Card title="Python API" icon="python" href="/reference/python-api">
    `create_crud_router` signature, base models, and access control decorators.
  </Card>

  <Card title="Configuration" icon="gear" href="/reference/configuration">
    Configure auth, CORS, and database connection.
  </Card>
</CardGroup>
