pocketbase/pocketbase

Open Source realtime backend in 1 file

57,734 stars Go 10 components

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".

Worth your attention first

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

Worth your attention first

Backup listing fails with timeout errors on slower storage systems or when many backup files exist, making backups appear unavailable even when they exist

Worth your attention first

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)
Contract

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
Domain

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
Temporal

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
Environment

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
Resource

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
Contract

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
Scale

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
Domain

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.

  1. 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
  2. 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]
  3. 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]
  4. 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]
  5. 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]
  6. 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.

Collection core/collection.go
struct 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
Record core/record.go
struct 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
RequestEvent core/request_event.go
struct 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
Field core/field.go
interface 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

SQLite Database (database)
Primary data store holding collections metadata, user records, and all application data with ACID transactions
File Storage (file-store)
Stores uploaded files either locally or in S3, with metadata tracked in database records
WebSocket Connections (in-memory)
Active client subscriptions for real-time updates, indexed by collection and filter criteria
Auth Tokens (cache)
JWT tokens and refresh tokens with expiration times for authenticated sessions

Feedback Loops

Delays

Control Points

Technology Stack

SQLite (database)
Embedded database providing ACID transactions, full-text search, and JSON support for all data persistence
Goja (runtime)
JavaScript runtime for executing user-defined hooks and custom business logic within Go application
Cobra (framework)
CLI framework for command-line interface supporting serve, migrate, and admin commands
JWT (library)
Token-based authentication system for stateless user sessions and API access
WebSockets (framework)
Real-time bidirectional communication for live record updates and subscriptions
OAuth2 (library)
Third-party authentication integration supporting Google, GitHub, and other providers
Ozzo Validation (library)
Form and data validation framework enforcing field constraints and business rules

Key Components

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related 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 .