> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/pocketbase/pocketbase/llms.txt
> Use this file to discover all available pages before exploring further.

# App interface

> Core PocketBase App interface and methods

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:

```go theme={null}
app := pocketbase.New()
```

### NewWithConfig()

Create a PocketBase instance with custom configuration:

```go theme={null}
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):

```go theme={null}
if err := app.Bootstrap(); err != nil {
    log.Fatal(err)
}
```

<Note>
  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.
</Note>

### Start()

Start the application and register default system commands:

```go theme={null}
if err := app.Start(); err != nil {
    log.Fatal(err)
}
```

### ResetBootstrapState()

Release initialized resources (close database connections, stop cron ticker):

```go theme={null}
if err := app.ResetBootstrapState(); err != nil {
    log.Fatal(err)
}
```

## App properties

### DataDir()

Get the app data directory path:

```go theme={null}
dataDir := app.DataDir() // "./pb_data"
```

### IsDev()

Check if the app is in development mode:

```go theme={null}
if app.IsDev() {
    log.Println("Running in development mode")
}
```

### IsBootstrapped()

Check if the application was initialized:

```go theme={null}
if app.IsBootstrapped() {
    // App is ready
}
```

### Logger()

Get the default app logger:

```go theme={null}
app.Logger().Info("Application started", "version", pocketbase.Version)
```

### Settings()

Get the loaded app settings:

```go theme={null}
settings := app.Settings()
log.Println("App name:", settings.Meta.AppName)
```

### Store()

Get the app runtime store (for temporary data):

```go theme={null}
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):

```go theme={null}
var count int64
err := app.DB().Select("count(*)").From("_collections").Row(&count)
```

### ConcurrentDB()

Get the concurrent database instance (for read operations):

```go theme={null}
db := app.ConcurrentDB()
```

### NonconcurrentDB()

Get the nonconcurrent database instance (for write operations):

```go theme={null}
db := app.NonconcurrentDB()
```

## Transactions

### RunInTransaction()

Execute a function within a database transaction:

```go theme={null}
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
})
```

<Warning>
  Always use the `txApp` instance passed to your callback function, not the original `app` instance, to ensure operations are part of the transaction.
</Warning>

## Filesystem

### NewFilesystem()

Create a new filesystem instance for managing files:

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
ctx := context.Background()
err := app.CreateBackup(ctx, "backup-2024.zip")
if err != nil {
    log.Fatal(err)
}
```

### RestoreBackup()

Restore a backup and restart the application:

```go theme={null}
ctx := context.Background()
err := app.RestoreBackup(ctx, "backup-2024.zip")
if err != nil {
    log.Fatal(err)
}
```

<Warning>
  This feature is experimental and currently expected to work only on UNIX-based systems. The application process will be replaced using `execve`.
</Warning>

## Other utilities

### Cron()

Get the app cron instance for scheduling tasks:

```go theme={null}
app.Cron().Add("daily_cleanup", "0 0 * * *", func() {
    // Cleanup logic
})
```

### SubscriptionsBroker()

Get the realtime subscriptions broker:

```go theme={null}
broker := app.SubscriptionsBroker()
```

### ReloadSettings()

Reinitialize and reload app settings:

```go theme={null}
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):

```go theme={null}
var count int64
err := app.AuxDB().Select("count(*)").From("_logs").Row(&count)
```

### AuxConcurrentDB()

Get the concurrent auxiliary database instance (for read operations):

```go theme={null}
db := app.AuxConcurrentDB()
```

### AuxNonconcurrentDB()

Get the nonconcurrent auxiliary database instance (for write operations):

```go theme={null}
db := app.AuxNonconcurrentDB()
```

### AuxHasTable()

Check if a table or view exists in the auxiliary database:

```go theme={null}
if app.AuxHasTable("_logs") {
    log.Println("Logs table exists")
}
```

### AuxVacuum()

Reclaim unused disk space in the auxiliary database:

```go theme={null}
if err := app.AuxVacuum(); err != nil {
    log.Fatal(err)
}
```

### AuxModelQuery()

Create a preconfigured SELECT query for the auxiliary database:

```go theme={null}
log := &core.Log{}
query := app.AuxModelQuery(log)
err := query.One(log)
```

### AuxSave()

Validate and save a model to the auxiliary database:

```go theme={null}
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:

```go theme={null}
if err := app.AuxSaveNoValidate(log); err != nil {
    log.Fatal(err)
}
```

### AuxDelete()

Delete a model from the auxiliary database:

```go theme={null}
if err := app.AuxDelete(log); err != nil {
    log.Fatal(err)
}
```

### AuxRunInTransaction()

Execute a function within an auxiliary database transaction:

```go theme={null}
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
})
```

<Note>
  It's safe to nest `AuxRunInTransaction` calls as long as you use the callback's `txApp` instance.
</Note>

## Table operations

### HasTable()

Check if a table or view exists in the main database:

```go theme={null}
if app.HasTable("users") {
    log.Println("Users table exists")
}
```

### TableColumns()

Get all column names of a table:

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
if err := app.DeleteTable("old_table"); err != nil {
    log.Fatal(err)
}
```

<Warning>
  This method is vulnerable to SQL injection. The table name must come only from trusted input.
</Warning>

### DeleteView()

Drop a view from the database:

```go theme={null}
if err := app.DeleteView("users_view"); err != nil {
    log.Fatal(err)
}
```

<Warning>
  This method is vulnerable to SQL injection. The view name must come only from trusted input.
</Warning>

### SaveView()

Create or update a persistent SQL view:

```go theme={null}
viewQuery := `
    SELECT id, username, email, created
    FROM users
    WHERE verified = TRUE
`

if err := app.SaveView("verified_users", viewQuery); err != nil {
    log.Fatal(err)
}
```

<Warning>
  This method is vulnerable to SQL injection. Arguments must come only from trusted input.
</Warning>

### CreateViewFields()

Create a FieldsList from a SELECT query for use in view collections:

```go theme={null}
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,
}
```

<Note>
  The SELECT query must have an "id" column and cannot use wildcard (\*) columns.
</Note>

## Transaction helpers

### TxInfo()

Get the transaction info associated with the current app instance:

```go theme={null}
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
    })
}
```

<Note>
  This is useful when you need to execute logic after a transaction completes, such as sending notifications or cleaning up resources.
</Note>

### IsTransactional()

Check if the current app instance is part of a transaction:

```go theme={null}
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:

```go theme={null}
unsafeApp := app.UnsafeWithoutHooks()
// Use with caution - no validation or normalization hooks will run
```

<Warning>
  Using the unsafe app instance may cause data integrity errors since record validations and normalizations rely on hooks.
</Warning>

## Additional model methods

### SaveNoValidate()

Save a model to the database without running validations:

```go theme={null}
record := core.NewRecord(collection)
record.Set("name", "John")

// Skip validation hooks
if err := app.SaveNoValidate(record); err != nil {
    log.Fatal(err)
}
```

<Note>
  Use `Save()` instead if you want to run validations before persisting.
</Note>

### SaveWithContext()

Save a model with a context to limit execution time:

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
type PocketBase struct {
    core.App
    RootCmd *cobra.Command
}
```

The `core.App` interface is implemented by `core.BaseApp`:

```go theme={null}
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
}
```
