This tutorial walks you through building a working task-management application from scratch: a REST API, a SQLAdmin back-office, and a React CRUD frontend — with almost no code to write. You will create three modules (Team, Projects, and Tasks), add business logic through custom endpoints, lock down access with RBAC and ReBAC, and explore the tree view and built-in UI features that VeloIQ generates automatically. The finished app lives inDocumentation Index
Fetch the complete documentation index at: https://docs.veloiq.dev/llms.txt
Use this file to discover all available pages before exploring further.
samples/task-manager/ if you want to compare at any point.
What you’ll build
Three modules, four models:- Team — team members with a name, email, and role
- Projects — projects with a status and an owner (a team member)
- Tasks — tasks with priority, due date, project, and assignee; plus a self-referential parent/sub-task relationship that renders as an interactive Miller columns tree view
- A REST API at
http://localhost:8000with auto-generated CRUD for all three entities - Interactive API docs at
http://localhost:8000/docs - A SQLAdmin back-office at
http://localhost:8000/admin/ - A React CRUD frontend at
http://localhost:5173
Overview
| Section | Goal | Time | Required? |
|---|---|---|---|
| Section 1 — Core app | Have a working full-stack app in your browser | ~15 min | Yes |
| Section 2 — Custom endpoints | Add business logic beyond standard CRUD | ~5 min | Optional |
| Section 3 — Global search | Wire up the header search bar | ~5 min | Optional |
| Section 4 — RBAC | Restrict what roles can do globally, per-model, and per-field | ~10 min | Optional |
| Section 5 — ReBAC | Filter which rows each user can see | ~10 min | Optional |
| Section 6 — Tree views | Navigate task hierarchies with Miller columns | ~5 min | Optional |
| Section 7 — Built-in UI features | Explore the analysis charts, column config, dark mode | ~5 min | Optional |
Section 1 — Core app
This section gets a working full-stack CRUD app with authentication running in your browser in about 15 minutes. Prerequisites: Python 3.10+, Node.js 18+Create the project
Run the following commands to clone the repository, set up a virtual environment, install the framework, and scaffold the project:You get this layout:
backend/app/main.py is already complete — one line that creates the whole app:Configure the database
Copy the example environment file:Choose your database:
- SQLite (default)
- PostgreSQL
No changes needed. The SQLite creates
.env.example file already points to a local SQLite file:app.db automatically on the first startup.Scaffold the three modules
Run these commands from Each command creates the module skeleton under
task-manager/ to create the module skeletons:backend/app/modules/:Write the Team module model
Replace
backend/app/modules/team/models.py with:TimestampedModel adds created_at and updated_at automatically, always placed last in every list, form, and detail view. Use FrameworkModel instead if you do not need audit timestamps.Write the Tasks module model
Replace The
backend/app/modules/tasks/models.py with:parent_task_id foreign key and the subtasks / parent_task relationships give every task an optional parent. The code generator detects the self-referential relationship and automatically sets showViewType: "tree-details" in the TypeScript schema — telling the React UI to render sub-tasks as a Miller columns tree browser instead of a flat table.Generate API and frontend schemas
From Alternatively, you can run
task-manager/backend/, run:veloiq generate as a shorthand. This writes two files per module:backend/app/modules/{module}/api.py— standard CRUD endpointsfrontend/src/pages/{module}/{module}Schema.gen.ts— TypeScript field definitions
Start the backend
Install dependencies and start the server:The framework creates all database tables automatically on first start — no migration step needed for a fresh project.Open
After changing your models, run
veloiq db upgrade to apply schema changes via Alembic without restarting the app.http://localhost:8000/docs to see a fully documented REST API for all three entities. Open http://localhost:8000/admin/ for the SQLAdmin back-office.Start the frontend and log in
In a second terminal, install frontend dependencies and start the development server:Open
Log in with
http://localhost:5173. You will be redirected to /login.Every VeloIQ application ships with authentication enabled. The first startup seeds a default admin user:| Username | Password | Role | Access |
|---|---|---|---|
admin | admin | Admin | Full CRUD on all resources |
admin / admin. You will see the sidebar with Tasks, Projects, Team, and an Access Control group with Users, Roles, and Tenants.What you wrote vs. what the framework generated
| You wrote | The framework generated |
|---|---|
3 × models.py | 3 × api.py (full CRUD REST endpoints) |
Nothing in main.py | TypeScript schemas, all routers, SQLAdmin views |
| A self-referential FK in Task | Miller columns tree view — automatically |
Section 2 — Custom endpoints
The auto-generated CRUD endpoints cover list, get, create, update, and delete. For anything more specific — status transitions, batch actions, domain operations — you add acustom_api.py in the module directory and import the generated router. The framework loads custom_api.py automatically; no registration in main.py is required.
Section 3 — Global search
Pages are found automatically from the navigation menu. Data search requires you to tell the framework which models and fields to match against. Once configured, the backend serves the search configuration fromGET /config/search and the frontend reads it on startup, querying those fields whenever a user types in the search bar.
Register models and fields
From This creates How field matching works: for each model, only fields whose key exactly matches one of the listed names (or ends with
task-manager/backend/, run:config/search.json:_<name>, e.g. task_title matches title) are searched.Section 4 — Role-based access control (RBAC)
VeloIQ ships with a three-layer RBAC system. All layers are purely restrictive — they can narrow access but never grant more than the role’s global permissions allow. You configure global roles inmain.py, restrict individual models with @model_access, and restrict individual fields with veloiq_field.
Layer 1 — Define global roles
Roles are defined in The
backend/app/main.py, seeded to the database on startup, and editable at runtime through Access Control → Roles in the sidebar.Replace the contents of backend/app/main.py with:Auditor role is upserted to the database on the next startup and immediately appears in Access Control → Roles.To test different roles, create users via Access Control → Users and assign roles:| Username | Role | Can do |
|---|---|---|
alice | Manager | Create and edit — no delete |
carol | Viewer | Read-only |
Layer 2 — Add model-level exceptions with @model_access
Restrict which actions a role may perform on one specific model, without changing that role’s permissions on every other resource.Open A Viewer can list and view tasks but can never create, edit, or delete one — even if their global permissions are expanded later. Roles not listed in
backend/app/modules/tasks/models.py and add the decorator:@model_access are unaffected.Layer 3 — Add field-level exceptions with veloiq_field
Control which roles can read or write individual fields:
write_roles— roles that may set this field. Others’ payloads are silently filtered.read_roles— roles that may see this field. Others receive the record without it.
Section 5 — Row-level access control (ReBAC)
Use@rebac when access depends on the data itself rather than a role — for example, a user should only see tasks assigned to them. ReBAC filters which rows each user can access based on data ownership or relationship traversal.
Apply owner-based access
Open With this decorator, every list/get/update/delete endpoint filters rows so a user only sees tasks where
backend/app/modules/tasks/models.py and add the decorator:assignee_id matches their user ID.No automatic Admin bypass —
@rebac applies to all roles including Admin. To exempt Admins, return True from your filter for admin users.- 404 not 403 — accessing an existing but inaccessible row returns 404 to prevent leaking which record IDs exist.
- Multiple patterns (
filter,owner_field,tenant_field) on one decorator are OR-combined: a row is visible if any pattern allows it. - Omitting
@rebacfrom a model means no row filtering — RBAC layers 1–3 still apply.
Section 6 — Tree views and Miller columns
BecauseTask has a self-referential parent_task_id foreign key, the code generator automatically configured the sub-tasks relation as showViewType: "tree-details" in the TypeScript schema. No extra code is needed — the tree view is available as soon as you have hierarchical data.
Create a task hierarchy
Create the following tasks in the UI or via the API:
- Create a top-level task: “Launch website”
- Create sub-tasks: “Write copy”, “Design mockups”, “Set up hosting” — each with Parent Task Id pointing to “Launch website”
- Create a sub-sub-task under “Write copy”: “Draft landing page headline”
Navigate the tree
Open the Show page for “Launch website”. The Subtasks panel renders as a Miller columns browser: each column shows the children of the selected item. Click any sub-task — a new column slides in to the right. The breadcrumb trail at the top of each column lets you navigate back up the tree without leaving the page.Additional navigation options:
- Drag the vertical handle between two columns to resize them.
- Each row has a ↗ (open in new tab) button — click it to open that task’s Show page in a full browser tab.
- The Tasks list page uses the same row-click behaviour in a side-by-side layout: click a row and a detail panel slides in from the right.
Section 7 — Built-in UI features
All of the following features are available out of the box — no code required. They apply to every list and detail view generated by VeloIQ. Analysis charts — open the Show page for a project that has several tasks. The Analyse panel renders distribution charts for the relation’s columns (status, priority, numeric fields). Click the bar-chart icon in the table toolbar to toggle it; your preference is saved. Column configuration — click the settings icon in any list or relation table toolbar to open the column configuration panel. Tick or untick columns to show or hide them, and drag to reorder. Preferences are saved per user and per view. Sorting — click any column header to sort ascending, again to sort descending, a third time to clear the sort. Filtering — hover over a column header to reveal the filter icon. String columns support contains/starts-with; number and date columns support range operators. Multiple filters stack. Dark mode — the toggle in the top-right corner of the header switches between light and dark themes. The preference is stored in the browser. Metadata — the ℹ button in a list toolbar opens a modal with technical details about the resource: field names, types, relation targets, and the API path. Relations explorer — the explore button on any Show page opens an interactive graph visualising all the relations connected to that record. Click any node to navigate directly to that record. Bulk actions — tick the checkboxes on one or more rows in any list. A bulk-action toolbar appears at the bottom with bulk edit, bulk delete, and CSV export.Next steps
Custom endpoints
Learn how to extend generated routers with domain-specific logic and status transitions.
Access control
Deep dive into RBAC and ReBAC concepts, including combining multiple patterns.
Configuration reference
Configure auth, CORS, Alembic migrations, and all other framework settings.
Open-core model
See the full list of free and Pro features available in VeloIQ.