PocketBase Best Practices
PocketBase is a single-binary backend (SQLite + REST + realtime + auth) that is easy to start with and easy to misuse at scale. This skill gives AI agents the guardrails: 64 rules across 9 categories, each with an incorrect vs. correct code example, covering the mistakes agents actually make — insecure API rules, unindexed queries, broken auth flows, N+1 expands, leaked server-side handles.
Updated for PocketBase v0.40 and JS SDK v0.28 (August 2026). Works with v0.36+.
How rules are prioritized
Every rule carries an impact level: Critical = security holes or broken data models, High = performance and correctness problems, Medium/Low = operational polish. When rules conflict, higher impact wins.
What's covered
| Category | Impact | What it protects you from |
|---|
| Collection Design | Critical | Wrong field types, missing indexes, manual ID strings instead of relations |
| API Rules & Security | Critical | Data leaks from empty/incorrect access rules, filter injection |
| Authentication | Critical | Broken password/OAuth2/OTP/MFA flows, token mishandling |
| SDK Usage | High | Bad client setup, swallowed errors, cancelled-request bugs |
| Query Performance | High | N+1 queries, over-fetching, missing pagination |
| Realtime | Medium | Dropped subscriptions, unauthenticated realtime access |
| File Handling | Medium | Unvalidated uploads, wrong file URLs, missing thumbnails |
| Production & Deployment | Medium | No backups, no rate limits, exposed superuser access |
| Server-Side Extending | High | Go/JSVM hook mistakes, transaction misuse, unsafe filter binding |
Quick Reference
Collection Design (CRITICAL)
- coll-field-types: Use appropriate field types (json for objects, select for enums)
- coll-auth-vs-base: Extend auth collection for users, base for non-auth data
- coll-relations: Use relation fields, not manual ID strings
- coll-indexes: Create indexes on frequently filtered/sorted fields
- coll-view-collections: Use views for complex aggregations
- coll-geopoint: Store coordinates as json field with lat/lng
API Rules (CRITICAL)
- rules-basics: Always set API rules; empty = public access
- rules-filter-syntax: Use @request.auth, @collection, @now in rules
- rules-request-context: Access request data via @request.body, @request.query;
@request.context values: default/oauth2/otp/password/realtime/protectedFile
- rules-cross-collection: Use @collection.name.field for cross-collection checks
- rules-locked-vs-open: Start locked, open selectively
- rules-strftime: Use
strftime('%Y-%m-%d', created) for date arithmetic (v0.36+)
Authentication (CRITICAL)
- auth-password: Use authWithPassword for email/password login
- auth-oauth2: Configure OAuth2 providers via Admin UI; run ≥v0.38.2 (pre-linking hijack fix); provider logos come inline from
listAuthMethods() (v0.37+)
- auth-otp: Two-step
requestOTP → authWithOTP; rate-limit requestOTP and never leak email existence
- auth-token-management: Store tokens securely, refresh before expiry
- auth-mfa: Enable MFA for sensitive applications
- auth-impersonation: Use impersonation for admin actions on behalf of users
SDK Usage (HIGH)
- sdk-initialization: Initialize client once, reuse instance
- sdk-auth-store: Use AsyncAuthStore for React Native/SSR
- sdk-error-handling: Catch ClientResponseError, check status codes
- sdk-auto-cancellation: Disable auto-cancel for concurrent requests
- sdk-filter-binding: Use filter binding to prevent injection
- query-expand: Expand relations to avoid N+1 queries
- query-field-selection: Select only needed fields
- query-pagination: Use cursor pagination for large datasets
- query-batch-operations: Batch creates/updates when possible
Realtime (MEDIUM)
- realtime-subscribe: Subscribe to specific records or collections
- realtime-events: Handle create, update, delete events separately
- realtime-auth: Realtime respects API rules automatically; auth unsets on password change; ~30min absolute connection cap (v0.38+)
- realtime-reconnection: Implement reconnection logic — periodic reconnects are by design, re-sync state instead of fighting them
File Handling (MEDIUM)
- file-upload: Use FormData for uploads, set proper content types
- file-serving: Use pb.files.getURL() for file URLs
- file-validation: Validate file types and sizes server-side
Deployment (MEDIUM)
- deploy-backup: Schedule regular backups of pb_data
- deploy-configuration: Use environment variables for config; mind v0.40 log caps (
Log.Data ~16KB) and treat the SQL console (v0.39+) as debug-only
- deploy-reverse-proxy: Put behind nginx/caddy in production
- deploy-sqlite-considerations: Optimize SQLite for production workloads
- deploy-rate-limiting: Enable the built-in rate limiter (fixed-window as of v0.36.7); front with Nginx/Caddy for defense in depth
- deploy-scaling: Raise
ulimit -n for realtime, set GOMEMLIMIT, enable settings encryption
- deploy-superuser-ips: Whitelist superuser access by IP/CIDR in production (v0.38+); recovery via the
superuser ips console command
Server-Side Extending (HIGH)
- ext-go-setup: Use
app.OnServe() to register routes; use e.App inside hooks, not the parent-scope app; v0.40 needs Go 1.27 (encoding/json/v2 — test before shipping)
- ext-js-setup: Drop
*.pb.js in pb_hooks/; add /// <reference path="../pb_data/types.d.ts" />
- ext-hooks-chain: Always call
e.Next()/e.next(); use Bind with an Id for later Unbind
- ext-hooks-record-vs-request: Use
OnRecordEnrich to shape responses (incl. realtime); OnRecordRequest for HTTP-only
- ext-routing-custom: Namespace routes under
/api/{yourapp}/; attach RequireAuth() middleware
- ext-transactions: Use the scoped
txApp inside RunInTransaction; never capture the outer app
- ext-filter-binding-server: Bind user input with
{:name} + dbx.Params in FindFirstRecordByFilter / FindRecordsByFilter
- ext-filesystem:
defer fs.Close() on every NewFilesystem() / NewBackupsFilesystem() handle
- ext-cron-jobs: Register with
app.Cron().MustAdd(id, expr, fn) / cronAdd(); stable ids, no __pb*__ prefix
- ext-go-migrations: Versioned
.go files under migrations/; Automigrate: osutils.IsProbablyGoRun()
- ext-js-migrations:
pb_migrations/<unix>_*.js with migrate(upFn, downFn); auto-discovered by filename
- ext-mailer: Resolve sender from
app.Settings().Meta at send-time; never ship no-reply@example.com; create the mail client per send
- ext-settings: Read via
app.Settings() at call time; set PB_ENCRYPTION (32 chars) to encrypt _params at rest
- ext-testing:
tests.NewTestApp(testDataDir) + tests.ApiScenario; defer app.Cleanup(), assert ExpectedEvents
- ext-compose-request-flow: Composite walkthrough showing which app instance is active at each layer (route → tx → hook → enrich)
- ext-go-custom-sqlite: Only use
DBConnect when you need FTS5/ICU; DBConnect is called twice (data.db + auxiliary.db)
- ext-jsvm-scope: Variables outside handlers are undefined at runtime — load shared config via
require() inside the handler
- ext-jsvm-modules: Only CJS (
require()) works in goja; bundle ESM first; avoid mutable module state
Example Prompts
Try these with your AI agent to see the skill in action:
Building a new feature:
- "Design a PocketBase schema for an e-commerce app with products, orders, and reviews"
- "Implement OAuth2 login with Google and GitHub for my app"
- "Build a real-time notification system with PocketBase subscriptions"
- "Create a file upload form with image validation and thumbnail previews"
Fixing issues:
- "My list query is slow on 100k records -- optimize it"
- "I'm getting 403 errors on my batch operations"
- "Fix the N+1 query problem in my posts list that loads author data in a loop"
- "My realtime subscriptions stop working after a few minutes"
Security review:
- "Review my API rules -- users should only access their own data"
- "Set up proper access control: admins manage all content, users edit only their own"
- "Are my authentication cookies configured securely for SSR?"
- "Audit my collection rules for IDOR vulnerabilities"
Going to production:
- "Configure Nginx with HTTPS, rate limiting, and security headers for PocketBase"
- "Set up automated backups for my PocketBase database"
- "Optimize SQLite settings for a production workload with ~500 concurrent users"
- "Deploy PocketBase with Docker Compose and Caddy"
Extending PocketBase:
- "Add a custom Go route that sends a Slack notification after a record is created"
- "Write a pb_hooks script that validates an email domain before user signup"
- "Set up FTS5 full-text search with a custom SQLite driver in my Go app"
- "Share a config object across multiple pb_hooks files without race conditions"
Detailed Rules
Load the relevant category for complete rule documentation with code examples:
- Collection Design - Schema patterns, field types, relations, indexes
- API Rules & Security - Access control, filter expressions, security patterns
- Authentication - Password auth, OAuth2, MFA, token management
- SDK Usage - Client initialization, auth stores, error handling, hooks
- Query Performance - Pagination, expansion, batch operations, N+1 prevention
- Realtime - SSE subscriptions, event handling, reconnection
- File Handling - Uploads, serving, validation
- Production & Deployment - Backup, configuration, reverse proxy, SQLite optimization
- Server-Side Extending - Go/JSVM setup, event hooks, custom routes, modules, custom SQLite