Menu
Public repository / caebd6669c276c9c4dd88ff423984ed8df65d490
AkurAI-Framework / site/content/docs/auto-api.md
Latest change caebd6669c276c9c4dd88ff423984ed8df65d490 - feat(cli): auto-generated REST API from backend/collections.toml by Ólafur Búi Ólafsson
# Auto-generated REST API
Declare your data model once in `backend/collections.toml` and the framework
mounts a full REST API for it at startup — list, create, read, update, delete,
and `?search` — with no handwritten endpoint code. The schema is the single
source of truth; validation, storage, and search all derive from it.
If `backend/collections.toml` is absent, nothing changes: the server serves your
site exactly as before, with zero collections.
## Declaring collections
```toml
# backend/collections.toml
[[collection]]
name = "posts"
[[collection.field]]
name = "title"
type = "text"
required = true
[[collection.field]]
name = "body"
type = "text"
embed = true # auto-embedded for semantic search (?search=...)
```
Field types: `text`, `int`, `float`, `bool` (case-insensitive).
- `required = true` — the field must be present (and non-null) on create.
- `embed = true` — (text only) the field is indexed for semantic `?search` when
an embedding endpoint is configured. Otherwise it is ignored.
The engine owns two reserved keys on every record: `id` (auto-increment) and
`created` (unix seconds). Your input may not set them.
A malformed `collections.toml` (TOML syntax error, or an unknown field type)
fails `akurai serve` loudly at startup with the file and line — it never
silently drops your routes.
## Routes
For a collection named `posts`:
| Method | Path | Purpose |
| ------ | ---- | ------- |
| `GET` | `/api/collections` | Manifest of every collection and its endpoints |
| `GET` | `/api/collections/posts/records` | List records, newest first |
| `GET` | `/api/collections/posts/records?search=<q>&limit=<n>` | Search |
| `POST` | `/api/collections/posts/records` | Create from a JSON body |
| `GET` | `/api/collections/posts/records/<id>` | Fetch one record |
| `PATCH` | `/api/collections/posts/records/<id>` | Partial update |
| `DELETE` | `/api/collections/posts/records/<id>` | Delete |
The collection manifest is also merged into `/api/_meta` under `collections`.
## Examples
Create a record:
```
curl -X POST http://localhost:8090/api/collections/posts/records \
-H 'Content-Type: application/json' \
-d '{"title":"Hello","body":"a wonderful world"}'
```
```json
{ "id": 1, "created": 1750000000, "title": "Hello", "body": "a wonderful world" }
```
List records (newest first):
```
curl http://localhost:8090/api/collections/posts/records
```
```json
[ { "id": 2, "created": 1750000100, "title": "Other", "body": "..." },
{ "id": 1, "created": 1750000000, "title": "Hello", "body": "..." } ]
```
Fetch, update, delete:
```
curl http://localhost:8090/api/collections/posts/records/1
curl -X PATCH http://localhost:8090/api/collections/posts/records/1 \
-H 'Content-Type: application/json' -d '{"title":"Hello again"}'
curl -X DELETE http://localhost:8090/api/collections/posts/records/1
```
### Status codes
- `201` on create, `200` on list/get/update/search, `204` on delete.
- `400` for a bad JSON body, a malformed id, or a validation failure
(the body is `{ "error": "..." }`).
- `404` for an unknown collection name or a missing record id.
## Search: semantic vs substring
`?search=<query>` works in two modes, transparently:
- **Substring (default).** With no embedding endpoint configured, `?search`
matches the query as a case-sensitive substring over the collection's `text`
fields, newest first. No setup required.
- **Semantic.** When `AKURAI_EMBED_URL` is set, the query is embedded and ranked
by cosine similarity against the stored vectors of each record's `embed` text
fields. Results come back in ranked (most-similar-first) order.
If the embedding endpoint is unreachable or returns an error for a given query,
search falls back to substring matching for that request — it never errors out.
The response is always a plain JSON array of records in either mode.
## Embedding configuration
Two environment variables, both optional:
| Variable | Meaning | Default |
| -------- | ------- | ------- |
| `AKURAI_EMBED_URL` | OpenAI-compatible `/v1/embeddings` endpoint | _unset_ → substring search |
| `AKURAI_EMBED_MODEL` | Embedding model name | `embeddinggemma` |
```
AKURAI_EMBED_URL=http://192.168.8.14:8081/v1/embeddings \
AKURAI_EMBED_MODEL=embeddinggemma \
akurai serve
```
Embeddings live in `data/embeddings.db` (separate from the record store at
`data/collections.db`). They are written best-effort after each successful
create/update and removed on delete — **a failed embedding never fails the
write**; the record is always saved, and a warning is printed to stderr.
### Plain HTTP only (no TLS)
The built-in embedding client speaks **plain HTTP** — it has no TLS stack, to
keep the framework dependency-free. An `https://` endpoint is rejected. Point
`AKURAI_EMBED_URL` at a plain-HTTP embeddings server (e.g. one running on your
LAN or behind a local reverse proxy that terminates TLS).