PocketBase Development Guide
PocketBase is an open-source, lightweight backend packaged as a single executable or embeddable Go package. It bundles an embedded SQLite database, user authentication, file storage, a real-time event engine, an administrative dashboard UI, and RESTful APIs with official client SDKs.
PocketBase supports two primary operating modes:
1. Standalone Backend with Client SDKs: Run PocketBase as an independent service and interact via client SDKs (JavaScript/TypeScript, Dart/Flutter) or HTTP REST calls.
2. Embedded Go Framework: Import github.com/pocketbase/pocketbase into a Go project to extend server logic with custom endpoints, lifecycle event hooks, background crons, and custom CLI tools.
Quick Navigation
- Getting Started & Setup: Standalone Quickstart | Embedded Go Setup | CLI Commands
- Data Modeling & Access Control: Collections & Schemas | API Security Rules | Schema Templates
- Client Integration (SDKs & REST): JavaScript / TypeScript SDK | Dart / Flutter SDK | REST API Reference | Realtime
- Go Extension Framework: Lifecycle & Event Hooks | Routing & Middleware | Database & Transactions | Migrations | CLI Commands
- Production & Maintenance: Going to Production | Docker Setup | Caddy HTTPS | Data Migration
1. Getting Started & Setup
Standalone Quickstart
-
Download Binary or Run via Docker:
bash # Option A: Run via Docker docker run -d \ -v pb_data:/pb_data \ -p 8090:8090 \ --name pocketbase \ ghcr.io/pocketbase/pocketbase:latest serve --http=0.0.0.0:8090 -
Access the Admin Dashboard:
- Openhttp://127.0.0.1:8090/_/in your browser.
- Create the initial superuser account. -
Detailed reference: Getting Started and CLI Commands.
Embedded Go Setup
When building a customized backend, PocketBase can be embedded directly into your Go application:
-
Initialize Module & Install Dependencies:
bash go mod init myapp go get github.com/pocketbase/pocketbase@latest -
Minimal
main.go:
```go
package main
import (
"log"
"net/http"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/core"
)
func main() {
app := pocketbase.New()
// Register custom route during serve
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
se.Router.GET("/api/healthz", func(e *core.RequestEvent) error {
return e.JSON(http.StatusOK, map[string]string{"status": "ok"})
})
return se.Next()
})
if err := app.Start(); err != nil {
log.Fatal(err)
}
}
```
- Start the Application:
bash go run main.go serve --http=0.0.0.0:8090
For in-depth extension guides, see Go Overview.
2. Data Modeling & Collections
PocketBase supports three collection types:
- Base Collection: Standard data tables for posts, comments, orders, etc.
- Auth Collection: Stores credentials, handles logins, OAuth2, and session tokens.
- View Collection: Read-only SQL views for reports, aggregations, or denormalized data.
System Fields
Every record contains the following built-in fields:
- id (15-character alphanumeric unique identifier)
- created (ISO 8601 UTC timestamp string)
- updated (ISO 8601 UTC timestamp string)
Auth collections additionally contain email, emailVisibility, verified, tokenKey, and password management fields.
Common Relations Pattern
When linking collections, use the relation field type:
- Single Relation: Set maxSelect = 1 (e.g., post.author -> single user id).
- Multiple Relation: Set maxSelect > 1 or empty (e.g., post.tags -> array of tag ids).
- When querying, use expand (e.g., author,comments(post).user).
- Reference guide: Working with Relations and Collections Guide.
3. Security & API Rules
API rules determine who can read, create, update, or delete records. Each collection has 5 rule slots: List, View, Create, Update, and Delete.
- Empty / Blank rule (
""): Anyone (including unauthenticated public users) can perform the action. null: The action is locked from the public API; only superusers or Go hooks can access it.- Rule Expression: Dynamic evaluation based on
@requestand collection fields.
Common Rule Patterns
# Only authenticated users
@request.auth.id != ""
# Record owner only
@request.auth.id = author.id
# User can update only their own profile
@request.auth.id = id
# Role-based access
@request.auth.role = "admin"
# Relational check: User belongs to the same organization
@request.auth.organization = organization.id
# Field immutability: Ensure author field cannot be changed on update
@request.data.author:isset = false || @request.data.author = author
Detailed guides: API Rules & Filters and Security Rules Patterns.
4. Client SDKs & API Integration
JavaScript / TypeScript SDK
Install via npm:
npm install pocketbase
Initialization & Authentication
import PocketBase from 'pocketbase';
const pb = new PocketBase('http://127.0.0.1:8090');
// Login with email and password
const authData = await pb.collection('users').authWithPassword('user@example.com', 'password123');
console.log('Logged in user ID:', pb.authStore.record?.id);
// Check if currently authenticated
if (pb.authStore.isValid) {
// auth headers automatically attached to requests
}
// Logout
pb.authStore.clear();
CRUD Operations
// List / Paginate records with filter and sort
const resultList = await pb.collection('posts').getList(1, 20, {
filter: 'status = "published" && created >= "2026-01-01"',
sort: '-created',
expand: 'author,categories',
});
// Get single record
const record = await pb.collection('posts').getOne('RECORD_ID', {
expand: 'author',
});
// Create record
const newPost = await pb.collection('posts').create({
title: 'Hello World',
content: 'PocketBase is awesome',
status: 'published',
author: pb.authStore.record?.id,
});
// Update record
await pb.collection('posts').update('RECORD_ID', {
title: 'Updated Title',
});
// Delete record
await pb.collection('posts').delete('RECORD_ID');
Realtime Subscriptions
// Subscribe to all changes in a collection
await pb.collection('posts').subscribe('*', (e) => {
console.log('Action:', e.action); // 'create', 'update', or 'delete'
console.log('Record:', e.record);
});
// Subscribe to a specific record
await pb.collection('posts').subscribe('RECORD_ID', (e) => {
console.log('Post updated:', e.record);
});
// Unsubscribe
await pb.collection('posts').unsubscribe('*');
File Uploads & URLs
// Uploading files via FormData
const formData = new FormData();
formData.append('title', 'My Document');
formData.append('file', fileInput.files[0]);
const record = await pb.collection('documents').create(formData);
// Generating file access URL
const fileUrl = pb.files.getURL(record, record.file, { thumb: '100x100' });
Full SDK documentation: JavaScript SDK, Dart SDK, API Records, and API Realtime.
5. Extending PocketBase with Go
PocketBase can be embedded and customized with Go hooks, router handlers, and database utilities.
Event Hooks in Go
Hooks intercept PocketBase operations before or after execution. Always call e.Next() to continue the chain.
// Intercept record creation to validate or enrich data
app.OnRecordCreateRequest("posts").BindFunc(func(e *core.RecordRequestEvent) error {
if e.Auth != nil {
e.Record.Set("author", e.Auth.Id)
}
// Custom validation
if len(e.Record.GetString("title")) < 3 {
return e.BadRequestError("Title is too short", nil)
}
return e.Next()
})
// Trigger post-creation side effects (e.g., logging, async notification)
app.OnRecordCreate("posts").BindFunc(func(e *core.RecordCreateEvent) error {
e.App.Logger().Info("Post created", "id", e.Record.Id, "title", e.Record.GetString("title"))
return e.Next()
})
// Intercept authentication
app.OnRecordAuth().BindFunc(func(e *core.RecordAuthEvent) error {
e.App.Logger().Info("User authenticated", "email", e.Record.GetString("email"))
return e.Next()
})
Reference: Go Event Hooks.
Custom Routes & Middleware in Go
Register custom HTTP endpoints inside the OnServe event:
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
// Add custom GET route
se.Router.GET("/api/stats", func(e *core.RequestEvent) error {
count, err := e.App.CountRecords("posts")
if err != nil {
return e.InternalServerError("failed to count posts", err)
}
return e.JSON(http.StatusOK, map[string]any{
"total_posts": count,
"user": e.Auth != nil,
})
})
// Custom POST route with body parsing
se.Router.POST("/api/feedback", func(e *core.RequestEvent) error {
var data struct {
Message string `json:"message"`
}
if err := e.BindBody(&data); err != nil {
return e.BadRequestError("invalid request body", err)
}
e.App.Logger().Info("Feedback received", "message", data.Message)
return e.NoContent(http.StatusOK)
})
return se.Next()
})
Reference: Go Routing.
Database Operations with dbx
Use PocketBase's built-in DB engine and query builder:
import "github.com/pocketbase/dbx"
// 1. Fetch single record
post, err := app.FindRecordById("posts", "RECORD_ID")
// 2. Fetch multiple records with filter and pagination
records, err := app.FindRecordsByFilter(
"posts",
"status = {:status}",
"-created",
20, // limit
0, // offset
dbx.Params{"status": "published"},
)
// 3. Create a record programmatically
postsCol, err := app.FindCollectionByNameOrId("posts")
if err != nil {
return err
}
newRecord := core.NewRecord(postsCol)
newRecord.Set("title", "Server Generated")
newRecord.Set("status", "published")
if err := app.Save(newRecord); err != nil {
return err
}
// 4. Run inside an ACID transaction
err = app.RunInTransaction(func(txApp core.App) error {
record, err := txApp.FindRecordById("posts", "RECORD_ID")
if err != nil {
return err
}
record.Set("views", record.GetInt("views")+1)
return txApp.Save(record)
})
Reference: Go Database Operations.
Cron Jobs & Console Commands
// Add a recurring background job
app.Cron().MustAdd("cleanup-task", "@every 30m", func() {
app.Logger().Info("Running background cleanup...")
})
// Register custom CLI commands via Cobra
import "github.com/spf13/cobra"
app.RootCmd.AddCommand(&cobra.Command{
Use: "seed",
Short: "Seed initial test data",
RunE: func(cmd *cobra.Command, args []string) error {
// seed logic
return nil
},
})
Reference: Go Console Commands and Go Overview.
6. Production Deployment
Production Checklist
- Reverse Proxy & TLS: Use Caddy or Nginx in front of PocketBase for automatic SSL termination and rate limiting.
- Persistent Storage: Ensure
pb_datadirectory is on persistent SSD storage. - Admin Security: Change default admin credentials and enable two-factor authentication or restrict dashboard access by IP.
- Automated Backups: Utilize PocketBase built-in backup settings (Dashboard -> Settings -> Backups) or schedule periodic snapshots.
Docker Compose Example
Use the pre-configured docker-compose.yml and Caddyfile:
version: '3.8'
services:
pocketbase:
image: ghcr.io/pocketbase/pocketbase:latest
restart: unless-stopped
command: serve --http=0.0.0.0:8090
volumes:
- ./pb_data:/pb_data
ports:
- "127.0.0.1:8090:8090"
Deployment guide: Going to Production.
7. Complete Reference Index
| Category | Reference Document | Description |
|---|---|---|
| Core | Getting Started | Setup options, initialization, and basic concepts |
| CLI Commands | serve, migrate, superuser, and CLI flags |
|
| Collections | Schema design, collection types, indexes | |
| Authentication | Password, OAuth2, OTP, and token handling | |
| API Rules & Filters | Filter syntax, security expressions, @request |
|
| Files Handling | File fields, upload handling, thumbnails, S3 | |
| Working with Relations | One-to-many, many-to-many, expand queries | |
| Going to Production | Security hardening, reverse proxies, scaling | |
| Data Migration | Migration strategies, workflows, and tools | |
| REST APIs | API Reference | Complete HTTP API documentation |
| Records API | CRUD operations, filters, sorting, expand | |
| Realtime API | SSE / WebSocket live event subscriptions | |
| Files API | File uploads, tokens, and asset URLs | |
| Collections API | Schema inspection and management via HTTP | |
| Settings API | Mail, S3, OAuth2 configuration endpoints | |
| Logs API | Request and auth activity logging | |
| Crons API | Scheduled jobs management | |
| Backups API | Backup creation and restoration | |
| Health API | Health check and readiness endpoints | |
| Client SDKs | JavaScript SDK | Browser, Node.js, React, Vue, Svelte client integration |
| Dart SDK | Flutter and Dart client library guide | |
| Go Extensions | Go Overview | Extending PocketBase as an embedded Go library (architecture, testing, cron, logger) |
| Event Hooks | Intercepting record and auth lifecycle events | |
| Routing | Custom API routes, middleware, request handling | |
| Database Operations | dbx query builder, record queries, transactions |
|
| Console Commands | Extending Cobra CLI commands | |
| Migrations | Go-based schema migrations | |
| Templates & Assets | Schema Templates | Pre-built models for Blog, E-commerce, Tasks |
| Security Rules | Comprehensive security patterns and recipes | |
| Docker Compose | Production Docker deployment configuration | |
| Caddyfile | Automatic HTTPS reverse proxy config | |
| Frontend Template | Standalone HTML/JS integration demo | |
| Import Script | Go data import utility | |
| Export Script | Go data export utility | |
| Setup Script | Go Docker bootstrap script |