# PocketBase Schema Patch Pattern

Use this when a PocketBase collection is missing fields needed by the frontend. The symptom is "Error saving to [collection]" with no clear error message.

## Pattern

1. Auth as superuser
2. GET the collection schema
3. Append missing fields to the `fields` array
4. PATCH the full `fields` array back — NOT POST to `/fields` (returns 404)

```python
#!/usr/bin/env python3
import json, urllib.request

BASE = "http://127.0.0.1:8091"
COLLECTION = "quotes"  # change as needed
EMAIL = "admin@grajmedia.duckdns.org"
PASSWORD = "RayPB2024!"

# Auth
data = json.dumps({"identity": EMAIL, "password": PASSWORD}).encode()
req = urllib.request.Request(f"{BASE}/api/collections/_superusers/auth-with-password", data=data, headers={"Content-Type": "application/json"})
token = json.loads(urllib.request.urlopen(req).read())["token"]

# Get schema
req = urllib.request.Request(f"{BASE}/api/collections/{COLLECTION}", headers={"Authorization": f"Bearer {token}"})
schema = json.loads(urllib.request.urlopen(req).read())
existing = {f["name"] for f in schema["fields"]}

# Fields to add: [(name, type), ...]
new_fields = [
    ("fieldName", "text"),
    ("otherField", "json"),
]

for name, typ in new_fields:
    if name not in existing:
        schema["fields"].append({"name": name, "type": typ, "required": False})
        print(f"Adding: {name} ({typ})")

# PATCH
req = urllib.request.Request(
    f"{BASE}/api/collections/{COLLECTION}",
    data=json.dumps({"fields": schema["fields"]}).encode(),
    headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
    method="PATCH"
)
result = json.loads(urllib.request.urlopen(req).read())
print(f"Done: {len(result['fields'])} fields total")
```

## Common PocketBase field types

| Type | Use for |
|------|---------|
| `text` | strings |
| `number` | integers/floats |
| `bool` | booleans |
| `json` | arrays, objects |
| `date` | ISO 8601 datetime strings |
| `file` | file uploads |
| `select` | enum values (needs `values` array) |
| `relation` | foreign keys (needs `collectionId`, `cascadeDelete`) |

## Silently Dropped Fields (CRITICAL)

PocketBase silently drops any field that is NOT in its collection schema. There is NO error, NO warning, NO console message. The write succeeds but the field is missing on the next read.

**Symptom on read**: The missing field is `undefined`. Code that falls back to `new Date()` or `false` silently uses the fallback — often producing wrong behavior (e.g., tasks showing as overdue with today's date because `dueDateTime` was dropped and `new Date()` was the fallback).

**When adding new JS-side fields to any collection**, you MUST:
1. Add the field to the PocketBase collection schema
2. Add any date fields to `TIMESTAMP_FIELDS` in `pocketbase.js`
3. Restart the PocketBase container

### Tasks collection (fixed June 2026)

The tasks collection was created with only 11 basic fields. These were added:

```
dueDateTime     text
dueTime         text
completed       bool
notes           text
isRecurring     bool
recurrenceType  text
customInterval  number
parentTaskId    text
instanceNumber  number
lastUpdated     text
```

### SQLite direct patch (when API auth fails)

```python
import sqlite3, json
conn = sqlite3.connect('/home/ray/docker/pocketbase/pb_data/data.db')
rows = conn.execute("SELECT id, fields FROM _collections WHERE name='tasks'").fetchall()
coll_id, fields_json = rows[0]
fields = json.loads(fields_json)
# Append missing fields, then:
conn.execute("UPDATE _collections SET fields = ? WHERE id = ?", (json.dumps(fields), coll_id))
conn.commit()
# Restart container: docker restart pocketbase
```
