Files
OSIT-AE-API-FastAPI/documentation/V3_FRONTEND_API_GUIDE.md

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 Forbidden if missing, invalid, or expired.

B. The "Visa" (Account Context)

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.
    • Header: x-account-id: <account_id_random>
  2. Administrative Bypass: For authorized scripts needing global access, use the bypass header.
    • 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.

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 like enable).
  • Fallback: If the table lacks a default_qry_str column, the API automatically performs a LIKE search 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.