247 lines
9.3 KiB
Markdown
247 lines
9.3 KiB
Markdown
# 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. 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
|
|
Use the `q` property in your search body for a general keyword search across indexed columns.
|
|
|
|
```json
|
|
// POST /v3/crud/journal/search
|
|
{
|
|
"q": "Annual Meeting",
|
|
"and": [
|
|
{ "field": "enable", "op": "eq", "value": true }
|
|
]
|
|
}
|
|
```
|
|
|
|
### D. Supported Search Operators
|
|
The `op` property in a `SearchFilter` supports the following values:
|
|
|
|
| Operator | SQL equivalent | Description |
|
|
| --- | --- | --- |
|
|
| `eq` | `=` | Equal to |
|
|
| `ne` | `!=` | Not equal to |
|
|
| `gt` | `>` | Greater than |
|
|
| `gte` | `>=` | Greater than or equal to |
|
|
| `lt` | `<` | Less than |
|
|
| `lte` | `<=` | Less than or equal to |
|
|
| `in` | `IN (...)` | Matches any value in a provided list |
|
|
| `is_null` | `IS NULL` | Field is null (ignores `value`) |
|
|
| `is_not_null` | `IS NOT NULL` | Field is not null (ignores `value`) |
|
|
| `like` | `LIKE` | Standard SQL LIKE (requires manual `%` in `value`) |
|
|
| `contains` | `LIKE %val%` | Wraps value in `%` automatically |
|
|
| `startswith` | `LIKE val%` | Appends `%` to value automatically |
|
|
| `endswith` | `LIKE %val` | Prepends `%` to value automatically |
|
|
|
|
---
|
|
|
|
## 3. 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}/
|
|
// POST /v3/crud/journal/
|
|
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}/
|
|
// POST /v3/crud/journal/EIAC-40-76-82/journal_entry/
|
|
// 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 });
|
|
}
|
|
|
|
// PATCH /v3/crud/{parent_type}/{parent_id}/{child_type}/{child_id}
|
|
// Verification: The backend ensures the child actually belongs to the parent before updating.
|
|
export async function update_nested_obj_v3({ api_cfg, parent_type, parent_id, child_type, child_id, data }) {
|
|
const endpoint = `/v3/crud/${parent_type}/${parent_id}/${child_type}/${child_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 });
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Specialized & Context Endpoints
|
|
|
|
### A. Initial Context Resolution (FQDN)
|
|
**Path**: `GET /crud/site/domain/{fqdn}?use_alt_table=true&use_alt_base=true`
|
|
|
|
This is a **critical public endpoint** used by the Svelte frontend during initial load.
|
|
- **Requirement**: Does NOT require `X-Account-ID` header (it resolves it).
|
|
- **Purpose**: Returns the `account_id` and site configuration associated with the current URL.
|
|
- **Backend**: Currently routed via legacy V1/V2 `api_crud.py` for maximum compatibility.
|
|
|
|
```ts
|
|
// Example: resolve context from current location
|
|
const fqdn = window.location.host;
|
|
const endpoint = `/crud/site/domain/${fqdn}`;
|
|
const context = await get_object({
|
|
api_cfg,
|
|
endpoint,
|
|
params: { use_alt_table: true, use_alt_base: true }
|
|
});
|
|
// result includes account_id and account_id_random
|
|
```
|
|
|
|
### 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. Returns:
|
|
- Database column names and SQL types.
|
|
- Pydantic model field names, aliases, and TypeScript-compatible types.
|
|
|
|
```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 });
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Advanced Search Logic
|
|
|
|
### A. 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": "%",
|
|
"and": [{ "field": "enable", "op": "eq", "value": true }]
|
|
}
|
|
```
|
|
|
|
## 6. Authentication and Security in V3 (Mandatory)
|
|
|
|
Implemented in January 2026, the V3 architecture enforces strict **Multi-Tenant Isolation** and requires valid authentication for nearly all requests.
|
|
|
|
### A. Authentication Requirement
|
|
Most API calls now require a standard Bearer token in the `Authorization` header.
|
|
|
|
* **Mandatory:** V3 CRUD endpoints require a valid JWT or an administrative bypass header.
|
|
* **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 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. Role-Based Access
|
|
The API performs background checks on the user's role stored in the system:
|
|
* **Managers/Admins:** Can see and edit most data within their account.
|
|
* **Super Users:** Can bypass account isolation (reserved for system maintenance).
|
|
|
|
### C. 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}`;
|
|
```
|
|
|
|
### D. Administrative Bypass (Utility/Dev only)
|
|
For development utilities and automated scripts, the `X-No-Account-ID: true` header grants full administrative access. **Never use this in a public-facing application.**
|
|
|
|
---
|
|
|
|
## 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` if defined in the backend.
|
|
2. **Hybrid Search**: Use query parameters for simple toggles (enabled/hidden) and the POST body for complex logic.
|
|
3. **Global Search**: Always prefer the `q` property for text search instead of manually targeting `default_qry_str`.
|
|
4. **Singular Nouns**: Always use singular names for `obj_type` (e.g., `journal`). |