76 lines
3.2 KiB
Markdown
76 lines
3.2 KiB
Markdown
# PocketBase Data Normalization Pitfalls
|
|
|
|
## JSON fields come back as strings
|
|
|
|
PocketBase's `json` field type does NOT guarantee parsed JSON arrays on read. The SDK may return JSON fields as **serialized strings** instead of parsed arrays/objects. Always normalize after fetching:
|
|
|
|
```typescript
|
|
const records = await pb.collection('repairOrders').getList(1, 200, { ... });
|
|
|
|
const normalized = records.items.map((item: any) => {
|
|
let services = item.services || [];
|
|
if (typeof services === 'string' && services.trim()) {
|
|
try { services = JSON.parse(services); } catch { services = []; }
|
|
}
|
|
if (!Array.isArray(services)) services = [];
|
|
return { ...item, services };
|
|
});
|
|
```
|
|
|
|
**Symptom:** `e.reduce is not a function` or `e.map is not a function` on fields you expected to be arrays.
|
|
|
|
**Also affects:** edit modals that pre-populate from fetched records — apply the same normalization before `setState()`.
|
|
|
|
## Empty/malformed JSON strings
|
|
|
|
Even when a JSON field has a value, it might be an empty string `""` or partially corrupted. JSON.parse on these throws `"Unexpected end of JSON input"`. Always wrap in try/catch:
|
|
|
|
```typescript
|
|
try { parsed = JSON.parse(raw); } catch { parsed = []; }
|
|
```
|
|
|
|
## Missing system fields (created, updated)
|
|
|
|
Some PocketBase collections lack the standard `created`/`updated` auto-fields. This happens when collections were created via raw SQL or imported. **Symptom:** queries with `sort: '-created'` or `fields: '...,created'` return 400 errors.
|
|
|
|
**Fix:** Use `sort: '-id'` (PocketBase IDs are time-sortable) and avoid requesting `created`/`updated` in the `fields` parameter.
|
|
|
|
**Detection:** Test with `curl` first:
|
|
```bash
|
|
curl -s "http://127.0.0.1:8091/api/collections/NAME/records?sort=-created&perPage=1" \
|
|
-H "Authorization: $TOKEN"
|
|
```
|
|
If it returns 400, the collection lacks `created`.
|
|
|
|
## Collection naming conventions
|
|
|
|
PocketBase collection names are case-sensitive. `repairOrders` and `repair_orders` are different collections. When porting from one naming convention to another, test each collection name directly against the API.
|
|
|
|
**Detection:**
|
|
```python
|
|
for name in ['repairOrders', 'repair_orders', 'repairorders']:
|
|
r = fetch(f'http://127.0.0.1:8091/api/collections/{name}/records?perPage=1')
|
|
print(f"{name}: {'EXISTS' if r.status == 200 else 'MISSING'}")
|
|
```
|
|
|
|
## PocketBase SDK error structure
|
|
|
|
The `ClientResponseError` thrown by the PocketBase JS SDK has this shape:
|
|
```
|
|
error.message → top-level message ("Failed to create record.")
|
|
error.status → HTTP status (400)
|
|
error.response → full API response body: {
|
|
data: { email: { message: "Value must be unique." } },
|
|
message: "Failed to create record.",
|
|
status: 400
|
|
}
|
|
```
|
|
|
|
For field-level validation errors, access `error.response.data`. Do NOT assume `error.data` is the field errors — in some SDK versions `error.data` is an alias for `error.response` (the full response body), so field errors are at `error.response.data` or `error.data.data`.
|
|
|
|
Simple reliable pattern:
|
|
```typescript
|
|
const message = err instanceof Error ? err.message : 'Failed';
|
|
```
|
|
The PocketBase SDK's `.message` already includes the user-facing error text.
|