pocketbase/pocketbase
Open Source realtime backend in 1 file
11 hidden assumptions · 6-stage pipeline · 10 components
Like any codebase, this backend api makes assumptions it never checks — most are routine. The ones worth your attention are below.
Provides backend-as-a-service with SQL database, REST API, file storage, and admin dashboard
HTTP requests enter through the Router which applies authentication and validation middleware before reaching collection-specific handlers. These handlers interact with the RecordService to perform database operations while enforcing access rules. Changes are persisted to SQLite and broadcast to WebSocket subscribers. File uploads are handled by the Filesystem abstraction and can be stored locally or in S3.
Under the hood, the system uses 3 feedback loops, 4 data pools, 4 control points to manage its runtime behavior.
A 10-component backend api. 649 files analyzed. Data flows through 6 distinct pipeline stages.
Hidden Assumptions
Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".
Large legitimate file uploads fail with cryptic errors, forcing users to split files or find workarounds, while the limit may be too permissive for memory-constrained environments
Backup listing fails with timeout errors on slower storage systems or when many backup files exist, making backups appear unavailable even when they exist
API returns success status but subsequent download attempts fail because backup file isn't yet accessible on eventually-consistent storage backends like S3
Show everything (8 more)
JavaScript runtime pool of 15 pre-warmed instances is sufficient for all concurrent hook executions under typical load, with no mechanism to adapt to actual concurrency patterns
If this fails: Under high load, requests queue waiting for available JS runtime instances, causing response time spikes and potential timeouts for legitimate requests
examples/base/main.go:hooksPool flag
Relation expansion is hard-coded to 6 levels maximum depth with no configuration override, assuming this depth is sufficient for all data modeling scenarios
If this fails: Complex relational data structures requiring deeper expansion silently truncate at level 6, returning incomplete data that may break client applications expecting full object graphs
ui/src/apiPreview/expandInfo.js:6-levels depth limit
The active backup flag in the store is properly cleaned up when backup operations complete or fail, with no timeout mechanism for stuck backup processes
If this fails: If a backup process crashes or hangs without clearing the flag, all subsequent backup attempts permanently fail with 'another backup process started' error until manual intervention
apis/backup_create.go:StoreKeyActiveBackup check
The default public directory exists and is readable by the process, with no fallback strategy if the directory is missing or has wrong permissions
If this fails: Static file serving fails completely if the public directory is misconfigured, potentially breaking admin UI and custom frontend applications with no clear error message
examples/base/main.go:defaultPublicDir() and file serving
Backup file uploads can complete within the HTTP request timeout regardless of file size, available bandwidth, or filesystem performance
If this fails: Large backup uploads timeout and fail, leaving partial files in storage and no way for clients to resume uploads
apis/backup_upload.go:fsys.UploadFile operation
All backup files in the filesystem have valid modification times and size metadata that can be read without errors
If this fails: Backup listing fails if any backup file has corrupted metadata or is in an inconsistent state during upload, making all backups appear unavailable
apis/backup.go:backupFileInfo struct
Filter expressions have reasonable complexity limits that prevent expensive operations, but no explicit validation of expression depth or computation cost is visible
If this fails: Complex nested filter expressions could cause performance degradation or resource exhaustion during SQL query generation and execution
ui/src/apiPreview/filterSyntax.js:filter expression parsing
The excerpt modifier always operates on text fields and handles edge cases like null values, binary data, or extremely long strings gracefully
If this fails: Field excerpting may fail or produce unexpected output when applied to non-text data types, potentially exposing sensitive data or causing client rendering issues
ui/src/apiPreview/fieldsInfo.js:excerpt modifier
Open the standalone hidden-assumptions report for pocketbase →
How Data Flows Through the System
HTTP requests enter through the Router which applies authentication and validation middleware before reaching collection-specific handlers. These handlers interact with the RecordService to perform database operations while enforcing access rules. Changes are persisted to SQLite and broadcast to WebSocket subscribers. File uploads are handled by the Filesystem abstraction and can be stored locally or in S3.
- Route HTTP request — Router in tools/router/router.go matches incoming HTTP request to a handler, applies middleware chain including authentication (loadAuthToken), rate limiting, and body parsing
- Authenticate request — loadAuthToken middleware extracts JWT from Authorization header or cookie, validates it against database, and populates RequestEvent.Auth with current user record [RequestEvent → RequestEvent]
- Validate collection access — Collection handlers in apis/records.go check ListRule/ViewRule/CreateRule/UpdateRule against current user context to determine if operation is allowed [RequestEvent]
- Process record operation — RecordService validates field data against Field definitions, handles file uploads via Filesystem, executes database query via Database wrapper, and triggers event hooks [Record → Record]
- Broadcast real-time updates — RealtimeService receives record change events and sends JSON messages over WebSocket connections to subscribed clients matching collection and filter criteria [Record]
- Return JSON response — Handler methods call RequestEvent.JSON() to serialize Record data, apply field visibility rules, and send HTTP response with appropriate status code [Record]
Data Models
The data structures that flow between stages — the contracts that hold the system together.
core/collection.gostruct with Id: string, Name: string, Type: string ('base'|'auth'|'view'), Fields: []*Field, Indexes: []*Index, ListRule: *string, ViewRule: *string, CreateRule: *string, UpdateRule: *string, DeleteRule: *string
Defined in admin UI or code, validated against database constraints, used to generate REST endpoints and enforce access rules
core/record.gostruct with Id: string, Collection: *Collection, Data: map[string]any, plus computed fields like Created: types.DateTime, Updated: types.DateTime
Created from HTTP requests or database queries, validated against collection rules, stored in SQLite, broadcast to subscribers
core/request_event.gostruct with Request: *http.Request, Response: http.ResponseWriter, App: App, Auth: *Record (current user), plus helper methods for JSON/validation
Created per HTTP request, populated with auth context, passed through middleware chain and handlers, used for response generation
core/field.gointerface with subtypes like TextField, NumberField, FileField, RelationField — each has Name: string, Type: string, Required: bool, plus type-specific options
Defined in collection schema, used to generate database columns, validate record data, and construct API responses
System Behavior
How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.
Data Pools
Primary data store holding collections metadata, user records, and all application data with ACID transactions
Stores uploaded files either locally or in S3, with metadata tracked in database records
Active client subscriptions for real-time updates, indexed by collection and filter criteria
JWT tokens and refresh tokens with expiration times for authenticated sessions
Feedback Loops
- Real-time subscription loop (polling, reinforcing) — Trigger: Record changes in database. Action: RealtimeService broadcasts JSON message to matching WebSocket clients. Exit: Client disconnects or unsubscribes.
- File cleanup loop (polling, balancing) — Trigger: Cron job schedule. Action: Scans for orphaned files not referenced by any records and deletes them. Exit: All orphaned files processed.
- Auth token refresh (retry, reinforcing) — Trigger: JWT token near expiration. Action: Client requests new token using refresh token. Exit: New token issued or refresh token expires.
Delays
- Database transactions (async-processing, ~milliseconds) — All record operations wait for SQLite transaction commit before returning response
- File upload processing (async-processing, ~seconds) — Large file uploads block request until stored in filesystem and metadata saved to database
- Real-time message delivery (eventual-consistency, ~milliseconds) — WebSocket clients receive record changes slightly after database commit completes
Control Points
- Collection access rules (runtime-toggle) — Controls: Which users can read, create, update, delete records in each collection. Default: SQL-like expressions
- File upload limits (threshold) — Controls: Maximum request body size for file uploads. Default: DefaultMaxBodySize
- Real-time subscription filters (runtime-toggle) — Controls: Which record changes are sent to each client connection. Default: Client-specified filter expressions
- JavaScript hooks enabled (feature-flag) — Controls: Whether custom JavaScript hooks can be executed. Default: true in default build
Technology Stack
Embedded database providing ACID transactions, full-text search, and JSON support for all data persistence
JavaScript runtime for executing user-defined hooks and custom business logic within Go application
CLI framework for command-line interface supporting serve, migrate, and admin commands
Token-based authentication system for stateless user sessions and API access
Real-time bidirectional communication for live record updates and subscriptions
Third-party authentication integration supporting Google, GitHub, and other providers
Form and data validation framework enforcing field constraints and business rules
Key Components
- App (orchestrator) — Central coordinator that manages database connections, HTTP server, event hooks, plugin system, and application lifecycle — the main dependency injection container
core/app.go - Router (gateway) — HTTP request router that applies middleware chains, matches URL patterns, and dispatches requests to handlers — converts HTTP requests to RequestEvent objects
tools/router/router.go - Database (store) — SQLite database wrapper that provides query building, transaction management, and schema operations — handles all data persistence and retrieval
core/db.go - RecordService (processor) — Handles CRUD operations on records with validation, access control, file uploads, and real-time event broadcasting — the primary business logic layer
core/record_service.go - Auth (validator) — Manages user authentication, JWT tokens, OAuth providers, and access control — determines what users can access and provides auth context
tools/auth/ - Filesystem (adapter) — Abstracts file storage operations supporting local disk and S3-compatible storage — handles uploads, downloads, and file management
tools/filesystem/ - Validator (validator) — Validates form data against collection field definitions and custom rules — ensures data integrity before database operations
forms/ - EventHooks (dispatcher) — Event system that triggers hooks at various application lifecycle points — enables plugins and custom logic to respond to database and API events
tools/hook/ - RealtimeService (dispatcher) — WebSocket-based real-time subscription system that broadcasts record changes to connected clients — maintains client connections and delivers live updates
core/realtime.go - JSVMPlugin (executor) — JavaScript runtime for executing user-defined hooks and custom logic using Goja — allows extending PocketBase behavior without recompiling
plugins/jsvm/
Explore the interactive analysis
See the full architecture map, data flow, and code patterns visualization.
Analyze on CodeSeaRelated Backend Api Repositories
Frequently Asked Questions
What is pocketbase used for?
Provides backend-as-a-service with SQL database, REST API, file storage, and admin dashboard pocketbase/pocketbase is a 10-component backend api written in Go. Data flows through 6 distinct pipeline stages. The codebase contains 649 files.
How is pocketbase architected?
pocketbase is organized into 5 architecture layers: API Layer, Core Engine, Tools & Utilities, Admin UI, and 1 more. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.
How does data flow through pocketbase?
Data moves through 6 stages: Route HTTP request → Authenticate request → Validate collection access → Process record operation → Broadcast real-time updates → .... HTTP requests enter through the Router which applies authentication and validation middleware before reaching collection-specific handlers. These handlers interact with the RecordService to perform database operations while enforcing access rules. Changes are persisted to SQLite and broadcast to WebSocket subscribers. File uploads are handled by the Filesystem abstraction and can be stored locally or in S3. This pipeline design reflects a complex multi-stage processing system.
What technologies does pocketbase use?
The core stack includes SQLite (Embedded database providing ACID transactions, full-text search, and JSON support for all data persistence), Goja (JavaScript runtime for executing user-defined hooks and custom business logic within Go application), Cobra (CLI framework for command-line interface supporting serve, migrate, and admin commands), JWT (Token-based authentication system for stateless user sessions and API access), WebSockets (Real-time bidirectional communication for live record updates and subscriptions), OAuth2 (Third-party authentication integration supporting Google, GitHub, and other providers), and 1 more. A focused set of dependencies that keeps the build manageable.
What system dynamics does pocketbase have?
pocketbase exhibits 4 data pools (SQLite Database, File Storage), 3 feedback loops, 4 control points, 3 delays. The feedback loops handle polling and polling. These runtime behaviors shape how the system responds to load, failures, and configuration changes.
What design patterns does pocketbase use?
4 design patterns detected: Event-driven architecture, Code generation from schema, Middleware chain, Plugin architecture.
Analyzed on April 20, 2026 by CodeSea. Written by Karolina Sarna.