4.2 KiB
Here is a fully rewritten version of the entire Tables + Columns + Floating Mapping section — accurate, simplified, and without over-emphasizing the “floating” term, while still explaining why it matters.
Tables & Columns
Automatic SQL Mapping from UI Components
In Unify, your UI defines your data model.
Every data entity in an application is a table object:
import table from '/unify/table.js';
export default class user extends table {
username = new username(); // column
email = new email(); // column
groups = new groups(); // relation
}
Any child object that extends column is automatically treated as a database column belonging to its nearest table ancestor.
There is no need to write schemas, migrations, or model config files. Unify inspects the structure at runtime and understands:
- Table name → class name (
user) - Column names → field names (
username,email, …) - Relationships → nested collections
- Permissions → defined on the table
No duplication. No ORM setup. Your UI code is your data model.
Columns Can Live Anywhere in the UI Tree
Columns do not need to be direct children of the table.
You can design your UI freely:
export default class userEditPanel extends panel {
accountPanel = new accountPanel();
contactPanel = new contactPanel();
}
class accountPanel extends panel {
username = new username(); // column
}
class contactPanel extends panel {
email = new email(); // column
}
Unify automatically discovers these fields because they appear under a table instance in the object tree.
So you are free to:
- Split a screen into panels
- Reuse complex component hierarchies
- Move UI structure without refactoring SQL code
This enables UI-first design without breaking the data layer.
CRUD Without CRUD Code
Unify provides two primary data operations:
| Action | Developer code | Result |
|---|---|---|
| Save | await user.save() |
INSERT or UPDATE depending on id |
| Load | await user.sync() |
SELECT + populate all child columns |
Example: Editing a profile
async click() {
const form = this.parents('userEdit');
await form.save(); // automatically writes username & email
form.hide();
}
And for loading:
user.id = 42;
await user.sync(); // loads username/email into the UI automatically
No setters. No binding boilerplate. No DTOs. No form libraries.
The table object is both:
- Data source
- UI container
- Binding layer
Why This Matters
| Traditional Full-Stack | With Unify |
|---|---|
| Write database schema | Skip — inferred |
| Write backend model | Skip — inferred |
| Write API controllers | Skip — RPC auto-generation |
| Write validation logic | Built into column objects |
| Build UI → manually connect | UI is the data model |
| Keep everything in sync | .sync() + .save() |
This eliminates the friction between backend + frontend and replaces it with one unified codebase.
Minimal Mental Model
A table object represents a database row. Any column object inside that table’s UI subtree automatically becomes a database column.
sync()loads data into the UI.save()writes UI data back to SQL.
Nothing more is needed to build and ship real applications.
Example Recap
user.id = 7;
await user.sync(); // load existing row → UI updates
user.username.value = "kaj";
await user.save(); // update DB row with new values
One line to fetch, one line to commit.
If you'd like, the next chapter can flow directly into Render Collections & Lists, since they demonstrate the same philosophy at scale:
- UI defines queries (pagination, filtering)
- Changes propagate automatically across the app
Should I continue with:
A) Render Collections + Filtering/Searching B) Permissions (groups, scopes, READ/WRITE/DELETE) C) Routing + Animated Page State D) Server RPC Communication Model (socket commands)
Just tell me which one comes next.