# 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).
