# 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**. Most requests now require valid authentication to prevent data leakage between accounts.

### A. Authentication Requirement
Almost all V3 CRUD endpoints require a standard Bearer token in the `Authorization` header.

*   **Mandatory:** You must provide a valid JWT for nearly all requests.
*   **Account Isolation:** The backend automatically filters all results based on the `account_id` found in your JWT. You cannot access data belonging to another account even if you know the random ID.
*   **Status Codes:**
    *   `401 Unauthorized`: Your JWT is invalid or expired.
    *   `403 Forbidden`: No authentication provided, or you attempted to access an object belonging to a different account.

**Example Request Header:**
```http
Authorization: Bearer <your_jwt_token>
```

### B. 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* a user can log in, the following endpoint allows unauthenticated (guest) access:

**Endpoint:** `POST /v3/crud/site_domain/search`

This is the only V3 search allowed without a JWT. All other object types (journal, account, post, etc.) will return `403 Forbidden` if accessed without a token.

---

## 3. Implementing V3 CRUD Functions

### A. List & Single Object (GET)
Support for view selection allows fetching richer data models when needed.

```ts
// 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.

```ts
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.

```json
{
  "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.

```ts
// 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.

```ts
// 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.

```ts
// 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.

```ts
// 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.

```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. Best Practices for V3

1.  **Use `view` for Rich Data**: Instead of manually joining data in separate calls, use `?view=enriched` or `?view=detail`.
2.  **Singular Nouns**: Always use singular names for `obj_type` (e.g., `journal`).
3.  **Strict Typing**: Ensure your `data` objects match the backend models to avoid `400 Bad Request` validation errors.

---

## 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.
```typescript
// ✅ 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.

---

## 9. Planned API Improvements (2026 Roadmap)

The following refinements have been requested from the Backend Agent to further improve frontend productivity:

1.  **Permissive Update Mode**: A new header (`x-ae-ignore-extra-fields: true`) is planned to allow the API to ignore non-writable columns in `PATCH` requests instead of returning a `400 Bad Request`.
2.  **Automated ID Resolution**: Backend will soon support automatic resolution of `_id_random` strings to integer IDs during `POST/PATCH`, reducing the need for manual lookups in the frontend.
3.  **Schema Evolution Tools**: New orchestration tools are being developed to automate field creation and renaming, including the automatic regeneration of the enriched SQL views.
4.  **Structured Validation Errors**: Move from string-based error details to a machine-readable format for better inline form validation.
