Files
2026-07-12 10:17:17 -04:00

127 lines
5.8 KiB
Markdown

# PocketBase SDK Path Construction
## The double `/api` trap
When building a reverse proxy for a PocketBase-backed SPA, the PB JS SDK constructs its own API paths. Understanding this avoids the most common proxy bug.
### How the PB SDK builds URLs
The PocketBase JS SDK's `buildURL` method concatenates `baseURL` + `path`:
```js
// If you create: new PocketBase('/pb')
// Then calling pb.collection('users').authWithPassword(email, password)
// SDK builds: GET /pb/api/collections/users/auth-with-password
// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// baseURL SDK-added /api/ path
```
The SDK ALWAYS prepends `/api/` to its paths. Your proxy must strip only the `/pb` prefix and forward the rest as-is — including the `/api/` that the SDK already added.
### The bug
```python
# WRONG — this adds a second /api/, producing /api/api/collections/...
if self.path.startswith('/pb'):
url = PB_BACKEND + '/api' + self.path[3:] # DOUBLE /api!
```
PocketBase returns `404 {"message": "The requested resource wasn't found."}` for the double-path URL, which is the same error message as genuinely missing resources — making this a silent, confusing failure.
### The fix
```python
# CORRECT — preserve the original path, which already includes /api/
if self.path.startswith('/pb'):
url = PB_BACKEND + self.path[3:] # /pb/X → /X on backend
```
## Python `http.server` path behavior
`BaseHTTPRequestHandler.path` includes the query string. When forwarding, the query string is already part of the path. `urllib.request.Request(url)` handles query strings correctly, so no special parsing is needed.
## PocketBase admin auth
PocketBase admin/superuser authentication is version-dependent:
- **v0.22+**: `POST /api/collections/_superusers/auth-with-password`
- **Older**: `POST /api/admins/auth-with-password` (may be a 404)
- The admin dashboard is at `/_/` (web UI only, not an API endpoint)
If neither endpoint works, check the PocketBase version. The admin user must be created via the `./pocketbase superuser` CLI command or through the web UI on first run.
## PocketBase Query Field Errors (400 "Something went wrong")
PocketBase returns `400 {"message": "Something went wrong while processing your request."}` when a query references fields that don't exist in the collection schema. This is NOT a 404 — it's a 400, and the error message is generic, making it easy to misdiagnose as a permissions or setup issue.
### The `created` / `updated` system field trap
Collections created via the PocketBase API (rather than the admin UI) may lack the `created` and `updated` system fields. Any query that sorts or filters on these fields will fail with 400.
**Symptom:** `sort: '-created'` succeeds via curl against a valid collection but returns 400 when tested against the same collection on a different PB instance. The collection EXISTS but the query fails because the field doesn't.
**Detection:** Test the exact query parameters one at a time:
```python
# Isolate which parameter breaks
# Test: ?page=1 (should work)
# Test: ?page=1&sort=-created (fails → field missing)
# Test: ?page=1&fields=created (may fail or be silently ignored)
```
**Workaround:** Replace `sort: '-created'` with `sort: '-id'` — PocketBase record IDs are sortable and roughly chronological. For `fields`, remove any references to `created`/`updated` if the collection lacks them.
**Root cause:** Subagents building against one PB instance may use system fields that work there, but the target deployment's PB instance has collections created via API without auto-system-fields enabled.
### Collection naming mismatches
PocketBase collection names are case-sensitive and can use either camelCase (`repairOrders`) or snake_case (`repair_orders`) depending on how they were created. The API silently returns 404 for the wrong case, and the error message is identical to a genuinely missing collection. Always verify the exact collection name by querying `GET /api/collections` before assuming a collection doesn't exist.
## PocketBase Sign-Up Error Handling
When `pb.collection('users').create()` fails, the PocketBase SDK throws a `ClientResponseError` with a generic top-level `.message` (e.g., `"Failed to create record."`) and field-level details in `.data`:
```json
{
"data": {
"email": {"code": "validation_not_unique", "message": "Value must be unique."}
},
"message": "Failed to create record.",
"status": 400
}
```
**Never use the raw `.message` directly** — it's always `"Failed to create record."` for any validation failure. Instead, unwrap `.data` to show the user which field failed:
```typescript
} catch (err: unknown) {
let message = 'Failed to create account.';
if (err && typeof err === 'object' && 'data' in err) {
const pbErr = err as { data?: Record<string, { message: string }> };
if (pbErr.data) {
const fieldErrors = Object.entries(pbErr.data)
.map(([field, info]) => `${field}: ${info.message}`)
.join('; ');
if (fieldErrors) message = fieldErrors;
}
}
setError(message);
}
```
This produces user-friendly messages like `email: Value must be unique.` instead of the unhelpful `Failed to create record.`
Common PocketBase validation errors on user creation:
- **email not unique** → `email: Value must be unique.`
- **password too short** → `password: Must be at least 8 characters.`
- **missing required field** → `email: Cannot be blank.`
## PocketBase SMTP Configuration
PocketBase requires SMTP to be configured for email verification to work. Check the `_params` table in the PocketBase data directory:
```sql
SELECT value FROM _params WHERE id='settings'
```
If `smtp.enabled` is `false`, verification emails will never send. Configure SMTP via the PocketBase admin UI (`/_/`) or by updating the `_params` row directly with valid SMTP credentials (host, port, username, password, TLS).