Skip to main content
The core.App interface is the main entry point for all PocketBase functionality. It provides methods for database operations, file storage, settings management, and event hooks.

Creating an app instance

New()

Create a new PocketBase instance with default configuration:
app := pocketbase.New()

NewWithConfig()

Create a PocketBase instance with custom configuration:
app := pocketbase.NewWithConfig(pocketbase.Config{
    DefaultDev: true,
    DefaultDataDir: "./pb_data",
    HideStartBanner: false,
})

Lifecycle methods

Bootstrap()

Initialize the application (create data directory, open database connections, load settings):
if err := app.Bootstrap(); err != nil {
    log.Fatal(err)
}
The application is automatically bootstrapped when you call Start(). You only need to manually call Bootstrap() if you need to initialize the app before starting.

Start()

Start the application and register default system commands:
if err := app.Start(); err != nil {
    log.Fatal(err)
}

ResetBootstrapState()

Release initialized resources (close database connections, stop cron ticker):
if err := app.ResetBootstrapState(); err != nil {
    log.Fatal(err)
}

App properties

DataDir()

Get the app data directory path:
dataDir := app.DataDir() // "./pb_data"

IsDev()

Check if the app is in development mode:
if app.IsDev() {
    log.Println("Running in development mode")
}

IsBootstrapped()

Check if the application was initialized:
if app.IsBootstrapped() {
    // App is ready
}

Logger()

Get the default app logger:
app.Logger().Info("Application started", "version", pocketbase.Version)

Settings()

Get the loaded app settings:
settings := app.Settings()
log.Println("App name:", settings.Meta.AppName)

Store()

Get the app runtime store (for temporary data):
app.Store().Set("custom_key", "custom_value")
value := app.Store().Get("custom_key")

Database access

DB()

Get the main database builder instance (automatically routes SELECT to concurrent pool):
var count int64
err := app.DB().Select("count(*)").From("_collections").Row(&count)

ConcurrentDB()

Get the concurrent database instance (for read operations):
db := app.ConcurrentDB()

NonconcurrentDB()

Get the nonconcurrent database instance (for write operations):
db := app.NonconcurrentDB()

Transactions

RunInTransaction()

Execute a function within a database transaction:
err := app.RunInTransaction(func(txApp core.App) error {
    record := core.NewRecord(collection)
    record.Set("name", "John")
    
    if err := txApp.Save(record); err != nil {
        return err // Transaction will rollback
    }
    
    return nil // Transaction will commit
})
Always use the txApp instance passed to your callback function, not the original app instance, to ensure operations are part of the transaction.

Filesystem

NewFilesystem()

Create a new filesystem instance for managing files:
fs, err := app.NewFilesystem()
if err != nil {
    log.Fatal(err)
}
defer fs.Close()

// Use the filesystem
exists, _ := fs.Exists("path/to/file.txt")

NewBackupsFilesystem()

Create a new filesystem instance for managing backups:
fs, err := app.NewBackupsFilesystem()
if err != nil {
    log.Fatal(err)
}
defer fs.Close()

Mail client

NewMailClient()

Create a new mail client based on current settings:
mailer := app.NewMailClient()

message := &mailer.Message{
    From: mail.Address{
        Name: "Support",
        Address: "support@example.com",
    },
    To: []mail.Address{
        {Address: "user@example.com"},
    },
    Subject: "Welcome!",
    HTML: "<h1>Welcome to our app</h1>",
}

if err := mailer.Send(message); err != nil {
    log.Fatal(err)
}

Backups

CreateBackup()

Create a backup of the current app data:
ctx := context.Background()
err := app.CreateBackup(ctx, "backup-2024.zip")
if err != nil {
    log.Fatal(err)
}

RestoreBackup()

Restore a backup and restart the application:
ctx := context.Background()
err := app.RestoreBackup(ctx, "backup-2024.zip")
if err != nil {
    log.Fatal(err)
}
This feature is experimental and currently expected to work only on UNIX-based systems. The application process will be replaced using execve.

Other utilities

Cron()

Get the app cron instance for scheduling tasks:
app.Cron().Add("daily_cleanup", "0 0 * * *", func() {
    // Cleanup logic
})

SubscriptionsBroker()

Get the realtime subscriptions broker:
broker := app.SubscriptionsBroker()

ReloadSettings()

Reinitialize and reload app settings:
if err := app.ReloadSettings(); err != nil {
    log.Fatal(err)
}

Auxiliary database methods

PocketBase uses two databases: data.db for main data and auxiliary.db for logs and temporary data.

AuxDB()

Get the auxiliary database builder instance (automatically routes SELECT to concurrent pool):
var count int64
err := app.AuxDB().Select("count(*)").From("_logs").Row(&count)

AuxConcurrentDB()

Get the concurrent auxiliary database instance (for read operations):
db := app.AuxConcurrentDB()

AuxNonconcurrentDB()

Get the nonconcurrent auxiliary database instance (for write operations):
db := app.AuxNonconcurrentDB()

AuxHasTable()

Check if a table or view exists in the auxiliary database:
if app.AuxHasTable("_logs") {
    log.Println("Logs table exists")
}

AuxVacuum()

Reclaim unused disk space in the auxiliary database:
if err := app.AuxVacuum(); err != nil {
    log.Fatal(err)
}

AuxModelQuery()

Create a preconfigured SELECT query for the auxiliary database:
log := &core.Log{}
query := app.AuxModelQuery(log)
err := query.One(log)

AuxSave()

Validate and save a model to the auxiliary database:
log := &core.Log{
    Level:   int(slog.LevelInfo),
    Message: "Application started",
}
if err := app.AuxSave(log); err != nil {
    log.Fatal(err)
}

AuxSaveNoValidate()

Save a model to the auxiliary database without validation:
if err := app.AuxSaveNoValidate(log); err != nil {
    log.Fatal(err)
}

AuxDelete()

Delete a model from the auxiliary database:
if err := app.AuxDelete(log); err != nil {
    log.Fatal(err)
}

AuxRunInTransaction()

Execute a function within an auxiliary database transaction:
err := app.AuxRunInTransaction(func(txApp core.App) error {
    log1 := &core.Log{Message: "First log"}
    log2 := &core.Log{Message: "Second log"}
    
    if err := txApp.AuxSave(log1); err != nil {
        return err
    }
    
    if err := txApp.AuxSave(log2); err != nil {
        return err
    }
    
    return nil // Both logs will be committed together
})
It’s safe to nest AuxRunInTransaction calls as long as you use the callback’s txApp instance.

Table operations

HasTable()

Check if a table or view exists in the main database:
if app.HasTable("users") {
    log.Println("Users table exists")
}

TableColumns()

Get all column names of a table:
columns, err := app.TableColumns("users")
if err != nil {
    log.Fatal(err)
}
log.Println("Columns:", columns) // ["id", "email", "username", "created", ...]

TableInfo()

Get detailed information about table columns:
info, err := app.TableInfo("users")
if err != nil {
    log.Fatal(err)
}

for _, col := range info {
    log.Printf("Column: %s, Type: %s, NotNull: %v\n", 
        col.Name, col.Type, col.NotNull)
}

TableIndexes()

Get all indexes for a table:
indexes, err := app.TableIndexes("users")
if err != nil {
    log.Fatal(err)
}

for name, sql := range indexes {
    log.Printf("Index: %s\nSQL: %s\n", name, sql)
}

DeleteTable()

Drop a table from the database:
if err := app.DeleteTable("old_table"); err != nil {
    log.Fatal(err)
}
This method is vulnerable to SQL injection. The table name must come only from trusted input.

DeleteView()

Drop a view from the database:
if err := app.DeleteView("users_view"); err != nil {
    log.Fatal(err)
}
This method is vulnerable to SQL injection. The view name must come only from trusted input.

SaveView()

Create or update a persistent SQL view:
viewQuery := `
    SELECT id, username, email, created
    FROM users
    WHERE verified = TRUE
`

if err := app.SaveView("verified_users", viewQuery); err != nil {
    log.Fatal(err)
}
This method is vulnerable to SQL injection. Arguments must come only from trusted input.

CreateViewFields()

Create a FieldsList from a SELECT query for use in view collections:
query := `
    SELECT 
        (ROW_NUMBER() OVER()) as id,
        users.username,
        COUNT(posts.id) as post_count
    FROM users
    LEFT JOIN posts ON posts.author = users.id
    GROUP BY users.id
`

fields, err := app.CreateViewFields(query)
if err != nil {
    log.Fatal(err)
}

// Use fields when creating a view collection
collection := &core.Collection{
    Name:   "user_stats",
    Type:   "view",
    Fields: fields,
}
The SELECT query must have an “id” column and cannot use wildcard (*) columns.

Transaction helpers

TxInfo()

Get the transaction info associated with the current app instance:
if app.IsTransactional() {
    info := app.TxInfo()
    
    // Execute a callback after the transaction completes
    info.OnComplete(func(txErr error) error {
        if txErr != nil {
            log.Println("Transaction failed:", txErr)
        } else {
            log.Println("Transaction succeeded")
        }
        return nil
    })
}
This is useful when you need to execute logic after a transaction completes, such as sending notifications or cleaning up resources.

IsTransactional()

Check if the current app instance is part of a transaction:
if app.IsTransactional() {
    log.Println("Running inside a transaction")
} else {
    log.Println("Not in a transaction")
}

UnsafeWithoutHooks()

Get a shallow copy of the app without any registered hooks:
unsafeApp := app.UnsafeWithoutHooks()
// Use with caution - no validation or normalization hooks will run
Using the unsafe app instance may cause data integrity errors since record validations and normalizations rely on hooks.

Additional model methods

SaveNoValidate()

Save a model to the database without running validations:
record := core.NewRecord(collection)
record.Set("name", "John")

// Skip validation hooks
if err := app.SaveNoValidate(record); err != nil {
    log.Fatal(err)
}
Use Save() instead if you want to run validations before persisting.

SaveWithContext()

Save a model with a context to limit execution time:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := app.SaveWithContext(ctx, record); err != nil {
    log.Fatal(err)
}

DeleteWithContext()

Delete a model with a context to limit execution time:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := app.DeleteWithContext(ctx, record); err != nil {
    log.Fatal(err)
}

Validate()

Trigger the OnModelValidate hook for a model without saving:
record := core.NewRecord(collection)
record.Set("email", "invalid-email")

if err := app.Validate(record); err != nil {
    log.Println("Validation failed:", err)
}

ValidateWithContext()

Validate a model with a custom context:
ctx := context.WithValue(context.Background(), "requestId", "12345")

if err := app.ValidateWithContext(ctx, record); err != nil {
    log.Fatal(err)
}

Type signature

The PocketBase struct embeds the core.App interface:
type PocketBase struct {
    core.App
    RootCmd *cobra.Command
}
The core.App interface is implemented by core.BaseApp:
type App interface {
    // Lifecycle methods
    Bootstrap() error
    IsBootstrapped() bool
    ResetBootstrapState() error
    
    // Configuration
    DataDir() string
    IsDev() bool
    Logger() *slog.Logger
    Settings() *Settings
    
    // Database
    DB() dbx.Builder
    RunInTransaction(fn func(txApp App) error) error
    
    // ... and many more methods
}