Docs: Final formatting and cleanup of V3 Frontend Guide

This commit is contained in:
Scott Idem
2026-01-19 17:08:48 -05:00
parent ede4cfabf0
commit 579772977b

View File

@@ -6,23 +6,23 @@ This guide explains how to update or create frontend functions to interact with
## 1. Key Differences (V2 vs V3) ## 1. Key Differences (V2 vs V3)
| Feature | CRUD V2 (Legacy) | CRUD V3 (Modern) | | Feature | CRUD V2 (Legacy) | CRUD V3 (Modern) |
| --- | --- | --- | | ------------------ | --------------------- | ---------------------------------------------------- |
| **Base Prefix** | `/v2/crud` | `/v3/crud` | | **Base Prefix** | `/v2/crud` | `/v3/crud` |
| **List Suffix** | Uses `/list` | **No suffix** (e.g., `/v3/crud/journal/`) | | **List Suffix** | Uses `/list` | **No suffix** (e.g., `/v3/crud/journal/`) |
| **Nested Path** | Not supported in URL | **Supported** (e.g., `/v3/crud/journal/{id}/journal_entry/`) | | **Nested Path** | Not supported in URL | **Supported** (e.g., `/v3/crud/journal/{id}/entry/`) |
| **View Selection**| `tbl_alt`, `mdl_alt` | **`view` parameter** (e.g., `?view=enriched`) | | **View Selection** | `tbl_alt`, `mdl_alt` | **`view` parameter** (e.g., `?view=enriched`) |
| **Complex Search**| Limited to GET `jp` | **POST `/search`** (Unlimited size + Hybrid params) | | **Complex Search** | Limited to GET `jp` | **POST `/search`** (Unlimited size + Hybrid params) |
| **Full-Text Search**| Manual column names | **Reserved `q` property** in SearchQuery | | **Full-Text** | Manual column names | **Reserved `q` property** in SearchQuery |
--- ---
## 2. Authentication and Security (Mandatory in V3) ## 2. Authentication and Security (Mandatory)
As of January 2026, the V3 architecture enforces strict **Multi-Tenant Isolation** and **Machine Authorization**. Most requests now require two levels of validation. As of January 2026, the V3 architecture enforces strict **Multi-Tenant Isolation** and **Machine Authorization**. Most requests require two levels of validation.
### A. The "Entry Ticket" (API Key) ### A. The "Entry Ticket" (API Key)
**Mandatory for all requests.** To prevent unauthorized clients from using the API, every request must provide a valid `x-aether-api-key` in the header. This identifies the application or script (e.g., the Svelte frontend, the Flask legacy app, or an AI agent). **Mandatory for all requests.** Every request must provide a valid `x-aether-api-key` in the header. This identifies the application or script.
* **Header:** `x-aether-api-key: <your_app_key>` * **Header:** `x-aether-api-key: <your_app_key>`
* **Status Code:** `403 Forbidden` if missing, invalid, or expired. * **Status Code:** `403 Forbidden` if missing, invalid, or expired.
@@ -30,27 +30,20 @@ As of January 2026, the V3 architecture enforces strict **Multi-Tenant Isolation
### B. The "Visa" (Account Context) ### B. The "Visa" (Account Context)
Once the API Key is validated, you must specify the context of your request. Once the API Key is validated, you must specify the context of your request.
1. **Standard User Access**: Provide the `x-account-id` (the random string ID). This restricts all results to that specific tenant. 1. **Standard User Access**: Provide the `x-account-id` (the random string ID).
* **Header:** `x-account-id: <account_id_random>` * **Header:** `x-account-id: <account_id_random>`
2. **Administrative Bypass**: For authorized scripts needing global access, use the bypass header. 2. **Administrative Bypass**: For authorized scripts needing global access.
* **Header:** `x-no-account-id: bypass` * **Header:** `x-no-account-id: bypass`
3. **Authentication Requirement**: Most V3 CRUD endpoints also require a standard Bearer token in the `Authorization` header for user-level actions. 3. **Authentication Requirement**: Standard `Authorization: Bearer <jwt>` is still required for user-level actions.
### C. The "Bootstrap Paradox" Exception (`site_domain`) ### C. The "Bootstrap Paradox" Exception (`site_domain`)
There is one critical exception to strict authentication: **`site_domain` search**. The **`site_domain` search** endpoint allows unauthenticated (**guest**) access so the frontend can resolve site configuration *before* login.
Because the frontend needs to lookup the site configuration (to know which account it's on) *before* it has an Account ID or User token, the following endpoint allows unauthenticated (**guest**) access: * **Endpoint:** `POST /v3/crud/site_domain/search`
* **Note:** Still requires a valid API Key.
**Endpoint:** `POST /v3/crud/site_domain/search`
This is the only V3 search allowed without an Account ID or JWT. However, it **still requires a valid API Key**.
### D. Fail-Fast on Auth Failures (401/403) ### D. Fail-Fast on Auth Failures (401/403)
To prevent unnecessary server load and looping, the frontend implements a **Fail-Fast** policy for authentication and authorization errors. The frontend implements a **Fail-Fast** policy for 401/403 errors. If the API returns these codes, the frontend **must not retry** automatically. It should stop and redirect the user or display an error.
* **401 Unauthorized**: Means the token is missing, invalid, or expired.
* **403 Forbidden**: Means the token is valid, but you do not have permission to access the specific resource (or you attempted a cross-tenant request without bypass).
* **Protocol**: If the API returns a 401 or 403, the frontend **must not retry** the request automatically. It should stop immediately and, if applicable, redirect the user to the login page or display a "Permission Denied" message.
--- ---
@@ -69,7 +62,7 @@ export async function get_ae_obj_v3({ api_cfg, obj_type, obj_id, view = 'default
``` ```
### B. Advanced & Hybrid Search (POST) ### B. Advanced & Hybrid Search (POST)
The `/search` endpoint combines the power of complex logical bodies with the simplicity of query parameters. The `/search` endpoint combines complex logical bodies with simple query parameters.
```ts ```ts
export async function search_ae_obj_v3({ export async function search_ae_obj_v3({
@@ -77,30 +70,19 @@ export async function search_ae_obj_v3({
obj_type, obj_type,
search_query, // { q: "search term", and: [...] } search_query, // { q: "search term", and: [...] }
enabled = 'enabled', enabled = 'enabled',
view = 'default', view = 'default'
for_obj_type,
for_obj_id
}) { }) {
const endpoint = `/v3/crud/${obj_type}/search`; const endpoint = `/v3/crud/${obj_type}/search`;
const params = { enabled, view };
// Standard filters can be passed as query params
const params: any = { enabled, view };
if (for_obj_type) params.for_obj_type = for_obj_type;
if (for_obj_id) params.for_obj_id = for_obj_id;
return await post_object({ return await post_object({ api_cfg, endpoint, params, data: search_query });
api_cfg,
endpoint,
params,
data: search_query
});
} }
``` ```
### C. Standardized Global Search (`q`) ### C. Standardized Global Search (`q`)
Use the `q` property in your search body for a keyword search. Use the `q` property in your search body for a keyword search.
- **Wildcard Support**: Setting `q: "%"` will bypass all text filtering and return all records (respecting only logical filters like `enable`). - **Wildcard**: `q: "%"` returns all records (respecting logical filters).
- **Fallback**: If the table lacks a `default_qry_str` column, the API automatically performs a `LIKE` search across all searchable fields. - **Fallback**: Performs a `LIKE` search across all searchable fields if no index exists.
```json ```json
{ {
@@ -111,12 +93,10 @@ Use the `q` property in your search body for a keyword search.
--- ---
## 4. Create, Update, & Delete (POST, PATCH, DELETE) ## 4. Create, Update, & Delete
V3 supports both top-level operations and nested parent/child operations.
### A. Create (POST) ### A. Create (POST)
When creating objects, V3 strictly validates the incoming JSON against the `mdl_in` Pydantic model. V3 validates incoming JSON against the `mdl_in` Pydantic model.
```ts ```ts
// POST /v3/crud/{obj_type}/ // POST /v3/crud/{obj_type}/
@@ -124,17 +104,10 @@ export async function create_ae_obj_v3({ api_cfg, obj_type, data }) {
const endpoint = `/v3/crud/${obj_type}/`; const endpoint = `/v3/crud/${obj_type}/`;
return await post_object({ api_cfg, endpoint, data }); return await post_object({ api_cfg, endpoint, data });
} }
// POST /v3/crud/{parent_obj_type}/{parent_obj_id}/{child_obj_type}/
// Note: Parent ID is automatically injected into the child record.
export async function create_nested_obj_v3({ api_cfg, parent_type, parent_id, child_type, data }) {
const endpoint = `/v3/crud/${parent_type}/${parent_id}/${child_type}/`;
return await post_object({ api_cfg, endpoint, data });
}
``` ```
### B. Update (PATCH) ### B. Update (PATCH)
V3 uses `PATCH` for partial updates. Only the fields provided in the body will be modified in the database. V3 uses `PATCH` for partial updates. Only provided fields are modified.
```ts ```ts
// PATCH /v3/crud/{obj_type}/{obj_id} // PATCH /v3/crud/{obj_type}/{obj_id}
@@ -145,202 +118,79 @@ export async function update_ae_obj_v3({ api_cfg, obj_type, obj_id, data }) {
``` ```
### C. Delete (DELETE) ### C. Delete (DELETE)
The `DELETE` method is used for removal. The backend may implement soft-delete (hide/disable) depending on the configuration. Supports soft-delete (`method=hide`) or hard-delete (`method=delete`).
```ts ```ts
// DELETE /v3/crud/{obj_type}/{obj_id} // DELETE /v3/crud/{obj_type}/{obj_id}?method=hide
export async function delete_ae_obj_v3({ api_cfg, obj_type, obj_id }) { export async function delete_ae_obj_v3({ api_cfg, obj_type, obj_id, method = 'delete' }) {
const endpoint = `/v3/crud/${obj_type}/${obj_id}`; const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
return await delete_object({ api_cfg, endpoint }); return await delete_object({ api_cfg, endpoint, params: { method } });
} }
``` ```
--- ---
## 5. Specialized & Context Endpoints ## 5. Specialized Endpoints
### A. Context Resolution (FQDN) ### A. Schema Discovery
Used during initial load to find the `account_id` associated with the domain.
**Legacy Method:** `GET /crud/site/domain/{fqdn}?use_alt_table=true&use_alt_base=true`
**Modern V3 Method (Preferred):** `POST /v3/crud/site_domain/search` with `{ "q": "your-domain.com" }`
### B. Schema Discovery
**Path**: `GET /v3/crud/{obj_type}/schema` **Path**: `GET /v3/crud/{obj_type}/schema`
Returns DB column definitions and Pydantic field rules for dynamic UI builders.
Used for developer tools or dynamic UI builders to understand the structure of an AE object. ### B. Secure File Downloads (JWT in URL)
For browsers downloading files, you can pass the JWT directly in the query string.
```ts - `GET /v3/crud/hosted_file/{id}/?jwt={token}`
// GET /v3/crud/account/schema
export async function get_obj_schema_v3({ api_cfg, obj_type }) {
const endpoint = `/v3/crud/${obj_type}/schema`;
return await get_object({ api_cfg, endpoint });
}
```
--- ---
## 6. Secure File Downloads (URL Parameter) ## 6. The "ID Vision" Standard (2026)
For `hosted_file` and `event_file`, browsers often need to download files without complex header modifications. In these cases, you can pass the JWT directly in the URL.
```ts
// Example: Creating a secure download link for a browser
// GET /v3/crud/hosted_file/{id}/?jwt={token}
const downloadUrl = `${BASE_URL}/v3/crud/hosted_file/${fileId}/?jwt=${jwtToken}`;
```
---
## 7. The "ID Vision" Standard (2026 Refactor)
V3 has moved to a **String-Only ID Vision**. In this paradigm, the frontend (SvelteKit) and external agents NEVER handle or store internal database integers.
V3 uses a **String-Only ID Vision**. The frontend and external agents NEVER handle or store internal database integers.
### A. Automatic ID Mapping (Output) ### A. Automatic ID Mapping (Output)
Internal `id_random` fields are renamed to clean, short names in the JSON response.
All V3 endpoints now automatically rename internal `id_random` fields to clean, short names in the JSON response. - **`id`**: Unique random identifier for the record.
- **`account_id`**: Unique random identifier for the parent account.
- **`id`**: The unique random identifier for the primary record.
- **`account_id`**: The unique random identifier for the parent account.
- **`site_id`, `person_id`, etc**: All related IDs follow this clean naming.
**Example Response:**
```json
{
"id": "OGQK-02-04-94",
"name": "Scott's Journal",
"account_id": "nqOzejLCDXM"
}
```
### B. Intelligent Mapping (Input/Search) ### B. Intelligent Mapping (Input/Search)
You can send string IDs back to the API using the same clean names.
The backend is now "Vision Aware." You can send string IDs back to the API using the same clean names. - **Search**: Filter by `account_id` using the random string value.
- **Save**: Send `account_id: "nqOzejLCDXM"` in a payload; the API resolves it to an integer.
- **Search Filters**: You can filter by `account_id` using the random string value. The API handles the mapping to the DB integer automatically.
- **Create/Update**: You can send `account_id: "nqOzejLCDXM"` in a payload. The `sanitize_payload` logic resolves this to the correct integer before saving.
**Preferred Filter Pattern:**
```json
{
"and": [{ "field": "account_id", "op": "eq", "value": "nqOzejLCDXM" }]
}
```
--- ---
## 7. Standard Patterns & Pitfalls
## 8. Standard Patterns & Common Pitfalls (2026 Update)
### A. Permissive Update Mode (Available) ### A. Permissive Update Mode (Available)
To simplify state management, V3 supports a **Permissive Update Mode** which ignores unknown extra fields in the payload.
**Usage:** High-performance state syncing.
To simplify frontend state management, V3 supports a **Permissive Update Mode**. When enabled, the backend will **ignore** extra fields in your `PATCH` or `POST` payload (like `_processed_at`, `created_on`, or UI-specific keys) instead of returning a `400 Bad Request`.
- **Header:** `x-ae-ignore-extra-fields: true` - **Header:** `x-ae-ignore-extra-fields: true`
- **Use when:** Sending back objects that contain read-only technical metadata.
**When to use:** ### B. Structured Error Handling (Rich Bubbling)
- When sending back an object fetched via `GET` that contains read-only technical metadata. V3 returns machine-readable error objects in `meta.details` for failures (400/500).
- When the frontend adds temporary "state" keys to objects that shouldn't be saved to the DB.
---
### B. Handle 'NULL' vs '0' for Booleans
**Pitfall:** Fields like `hide` or `priority` can be `NULL` in the database.
**Standard:** Ensure your synchronization logic in components handles `null`, `undefined`, and `0` identically for boolean checks to prevent "ghost" changes being detected by Svelte 5.
---
### C. Structured Error Handling (Rich Bubbling)
V3 provides machine-readable error objects in the `meta.details` field for most failures (400/500). Instead of parsing strings, your error handler should check the `category` and `code`.
**Example Error Response:** **Example Error Response:**
```json ```json
{ {
"meta": { "meta": {
"success": false, "success": false,
"status_code": 400, "status_code": 400,
"status_message": "Failed to update object.",
"details": { "details": {
"category": "database_schema", "category": "database_schema",
"code": 1054, "code": 1054,
"message": "Unknown column 'notes' in 'SET'",
"message": "Unknown column 'unauthorized_field' in 'SET'",
"recoverable": false "recoverable": false
} }
} }
} }
``` ```
**Common Categories:** **Common Categories:**
- **`database_duplicate`**: Non-unique value (Code 1062).
- **`database_constraint`**: Foreign key violation (Codes 1451, 1452).
- **`database_schema`**: Invalid column name (Codes 1054, 1146).
- **`database_connection`**: Temporary DB issues (`recoverable: true`).
- **`validation`**: Pydantic validation failed.
- **`database_duplicate`**: Attempted to create a record with a non-unique value (Code 1062). ### C. Booleans: 'NULL' vs '0'
**Pitfall:** Fields like `hide` can be `NULL` in the DB.
- **`database_constraint`**: Foreign key or integrity violation (Codes 1451, 1452). **Standard:** Handle `null`, `undefined`, and `0` identically for boolean checks.
- **`database_schema`**: Invalid column name or table configuration (Codes 1054, 1146).
- **`database_connection`**: Temporary DB unavailability (`recoverable: true`).
- **`validation`**: Pydantic validation failed (Check `details` for field-specific errors).