9.3 KiB
Aether API V3 Frontend Integration Guide (Svelte/TypeScript)
This guide explains how to update or create frontend functions to interact with the new Aether API V3 CRUD endpoints. V3 introduces a nested URL structure, a powerful POST-based search, and improved performance.
1. Key Differences (V2 vs V3)
| Feature | CRUD V2 (Legacy) | CRUD V3 (Modern) |
|---|---|---|
| Base Prefix | /v2/crud |
/v3/crud |
| 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/) |
| View Selection | tbl_alt, mdl_alt |
view parameter (e.g., ?view=enriched) |
| Complex Search | Limited to GET jp |
POST /search (Unlimited size + Hybrid params) |
| Full-Text Search | Manual column names | Reserved q property in SearchQuery |
2. Authentication and Security (Mandatory in V3)
As of January 2026, the V3 architecture enforces strict Multi-Tenant Isolation and Machine Authorization. Most requests now require two levels of validation.
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).
- Header:
x-aether-api-key: <your_app_key> - Status Code:
403 Forbiddenif missing, invalid, or expired.
B. The "Visa" (Account Context)
Once the API Key is validated, you must specify the context of your request.
- Standard User Access: Provide the
x-account-id(the random string ID). This restricts all results to that specific tenant.- Header:
x-account-id: <account_id_random>
- Header:
- Administrative Bypass: For authorized scripts needing global access, use the bypass header.
- Header:
x-no-account-id: bypass
- Header:
- Authentication Requirement: Most V3 CRUD endpoints also require a standard Bearer token in the
Authorizationheader for user-level actions.
C. The "Bootstrap Paradox" Exception (site_domain)
There is one critical exception to strict authentication: site_domain search.
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
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)
To prevent unnecessary server load and looping, the frontend implements a Fail-Fast policy for authentication and authorization errors.
- 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.
3. Implementing V3 CRUD Functions
A. List & Single Object (GET)
Support for view selection allows fetching richer data models when needed.
// Example: Get enriched journal details
// GET /v3/crud/journal/{id}?view=enriched
export async function get_ae_obj_v3({ api_cfg, obj_type, obj_id, view = 'default' }) {
const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
return await get_object({ api_cfg, endpoint, params: { view } });
}
B. Advanced & Hybrid Search (POST)
The /search endpoint combines the power of complex logical bodies with the simplicity of query parameters.
export async function search_ae_obj_v3({
api_cfg,
obj_type,
search_query, // { q: "search term", and: [...] }
enabled = 'enabled',
view = 'default',
for_obj_type,
for_obj_id
}) {
const endpoint = `/v3/crud/${obj_type}/search`;
// 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({
api_cfg,
endpoint,
params,
data: search_query
});
}
C. Standardized Global Search (q)
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 likeenable). - Fallback: If the table lacks a
default_qry_strcolumn, the API automatically performs aLIKEsearch across all searchable fields.
{
"q": "Annual Meeting",
"and": [{ "field": "enable", "op": "eq", "value": true }]
}
4. Create, Update, & Delete (POST, PATCH, DELETE)
V3 supports both top-level operations and nested parent/child operations.
A. Create (POST)
When creating objects, V3 strictly validates the incoming JSON against the mdl_in Pydantic model.
// POST /v3/crud/{obj_type}/
export async function create_ae_obj_v3({ api_cfg, obj_type, data }) {
const endpoint = `/v3/crud/${obj_type}/`;
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)
V3 uses PATCH for partial updates. Only the fields provided in the body will be modified in the database.
// PATCH /v3/crud/{obj_type}/{obj_id}
export async function update_ae_obj_v3({ api_cfg, obj_type, obj_id, data }) {
const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
return await patch_object({ api_cfg, endpoint, data });
}
C. Delete (DELETE)
The DELETE method is used for removal. The backend may implement soft-delete (hide/disable) depending on the configuration.
// DELETE /v3/crud/{obj_type}/{obj_id}
export async function delete_ae_obj_v3({ api_cfg, obj_type, obj_id }) {
const endpoint = `/v3/crud/${obj_type}/${obj_id}`;
return await delete_object({ api_cfg, endpoint });
}
5. Specialized & Context Endpoints
A. Context Resolution (FQDN)
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
Used for developer tools or dynamic UI builders to understand the structure of an AE object.
// 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)
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.
// 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. Best Practices for V3
8. Standard Patterns & Common Pitfalls (2026 Update)
To ensure stability across the Aether mesh, all frontend components must adhere to these established patterns.
A. The "Whitelist" Standard for Saves
Problem: Sending the entire object returned by a GET request back to a PATCH endpoint will cause a 400 Bad Request. This happens because the object contains technical metadata (like journal_id, created_on, for_type) that the API cannot "SET".
Standard: When preparing a save payload, explicitly whitelist ONLY the user-editable fields.
// ✅ CORRECT: Whitelist only editable fields
const data_kv = {
name: tmp_obj.name,
content: tmp_obj.content,
tags: tmp_obj.tags,
category_code: tmp_obj.category_code
};
// ❌ WRONG: Passing the whole object or just blacklisting a few
const data_kv = { ...tmp_obj };
delete data_kv.id; // Still contains other computed columns!
B. The Triple-ID Save Pattern
Standard: When sending an ID in a POST/PATCH payload (e.g. creating a child object), use the random string version with the _id_random suffix.
-
Property:
[obj_type]_id_random -
Value: A string (e.g.,
qpgvOh5nOYI)
Warning: Sending a string value under an integer key (e.g. journal_id: "qpgvOh5nOYI") will trigger a Pydantic validation error on the backend.
C. 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.