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

# Use as Go framework

> Learn how to use PocketBase as a Go framework for maximum control and performance

PocketBase can be used as a Go framework, giving you complete control over the application lifecycle, custom commands, and direct access to all PocketBase internals.

## Installation

First, create a new Go module and install PocketBase:

```bash theme={null}
mkdir myapp
cd myapp
go mod init myapp
go get github.com/pocketbase/pocketbase
```

## Basic example

Here's a minimal example from `examples/base/main.go`:

```go main.go theme={null}
package main

import (
    "log"
    "github.com/pocketbase/pocketbase"
)

func main() {
    app := pocketbase.New()

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

This creates a PocketBase instance with default settings and starts the server.

## Configuration

You can customize PocketBase's configuration using `NewWithConfig`:

```go theme={null}
package main

import (
    "log"
    "time"
    "github.com/pocketbase/pocketbase"
)

func main() {
    app := pocketbase.NewWithConfig(pocketbase.Config{
        DefaultDataDir:      "./pb_data",
        DefaultDev:          true,
        DefaultQueryTimeout: 30 * time.Second,
        HideStartBanner:     false,
        DataMaxOpenConns:    20,
        DataMaxIdleConns:    5,
    })

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

<Note>
  The application is bootstrapped automatically when you call `app.Start()`. Database connections, migrations, and settings are initialized at this point.
</Note>

## Application lifecycle

The PocketBase app follows this lifecycle:

<Steps>
  <Step title="Creation">
    Create a new PocketBase instance with `New()` or `NewWithConfig()`.

    ```go theme={null}
    app := pocketbase.New()
    ```
  </Step>

  <Step title="Configuration">
    Register plugins, hooks, and custom commands before starting.

    ```go theme={null}
    app.OnServe().BindFunc(func(e *core.ServeEvent) error {
        // Add custom routes
        return e.Next()
    })
    ```
  </Step>

  <Step title="Bootstrap">
    The app bootstraps when `Start()` or `Execute()` is called. This initializes:

    * Database connections
    * Migrations
    * Application settings
    * Event hooks
  </Step>

  <Step title="Execution">
    The app executes the command (default is `serve`) and runs until terminated.
  </Step>

  <Step title="Termination">
    Graceful shutdown with cleanup via `OnTerminate()` hooks.
  </Step>
</Steps>

## Custom commands

You can add custom CLI commands to your PocketBase app:

```go theme={null}
package main

import (
    "log"
    "github.com/pocketbase/pocketbase"
    "github.com/spf13/cobra"
)

func main() {
    app := pocketbase.New()

    // Add a custom command
    app.RootCmd.AddCommand(&cobra.Command{
        Use:   "hello",
        Short: "Prints a greeting message",
        Run: func(cmd *cobra.Command, args []string) {
            log.Println("Hello from custom command!")
        },
    })

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

Run it with:

```bash theme={null}
go run main.go hello
```

## Working with the database

<Tabs>
  <Tab title="Query records">
    ```go theme={null}
    app.OnServe().BindFunc(func(e *core.ServeEvent) error {
        // Find a single record
        record, err := app.FindRecordById("posts", "RECORD_ID")
        if err != nil {
            return err
        }

        // Query multiple records
        records, err := app.FindRecordsByFilter(
            "posts",
            "status = 'published'",
            "-created",
            20,
            0,
        )
        if err != nil {
            return err
        }

        return e.Next()
    })
    ```
  </Tab>

  <Tab title="Create records">
    ```go theme={null}
    app.OnServe().BindFunc(func(e *core.ServeEvent) error {
        collection, err := app.FindCollectionByNameOrId("posts")
        if err != nil {
            return err
        }

        record := core.NewRecord(collection)
        record.Set("title", "My new post")
        record.Set("status", "draft")

        if err := app.Save(record); err != nil {
            return err
        }

        return e.Next()
    })
    ```
  </Tab>

  <Tab title="Update records">
    ```go theme={null}
    app.OnServe().BindFunc(func(e *core.ServeEvent) error {
        record, err := app.FindRecordById("posts", "RECORD_ID")
        if err != nil {
            return err
        }

        record.Set("status", "published")
        record.Set("publishedAt", time.Now())

        if err := app.Save(record); err != nil {
            return err
        }

        return e.Next()
    })
    ```
  </Tab>

  <Tab title="Delete records">
    ```go theme={null}
    app.OnServe().BindFunc(func(e *core.ServeEvent) error {
        record, err := app.FindRecordById("posts", "RECORD_ID")
        if err != nil {
            return err
        }

        if err := app.Delete(record); err != nil {
            return err
        }

        return e.Next()
    })
    ```
  </Tab>

  <Tab title="Raw SQL">
    ```go theme={null}
    app.OnServe().BindFunc(func(e *core.ServeEvent) error {
        type Result struct {
            Id    string `db:"id"`
            Total int    `db:"total"`
        }

        results := []Result{}
        err := app.DB().Select("id", "COUNT(*) as total").From("posts").All(&results)
        if err != nil {
            return err
        }

        return e.Next()
    })
    ```
  </Tab>
</Tabs>

## Accessing app settings

You can read and modify application settings:

```go theme={null}
app.OnServe().BindFunc(func(e *core.ServeEvent) error {
    // Read settings
    settings := app.Settings()
    appName := settings.Meta.AppName
    
    // Modify settings
    settings.Meta.AppName = "My Custom App"
    
    // Save settings
    if err := app.Save(settings); err != nil {
        return err
    }
    
    return e.Next()
})
```

## Register plugins

PocketBase supports a plugin system for modular extensions:

```go theme={null}
package main

import (
    "log"
    "github.com/pocketbase/pocketbase"
    "github.com/pocketbase/pocketbase/plugins/jsvm"
    "github.com/pocketbase/pocketbase/plugins/migratecmd"
)

func main() {
    app := pocketbase.New()

    // Register JavaScript VM plugin for pb_hooks
    jsvm.MustRegister(app, jsvm.Config{
        HooksDir:      "./pb_hooks",
        HooksWatch:    true,
        HooksPoolSize: 15,
    })

    // Register migration command plugin
    migratecmd.MustRegister(app, app.RootCmd, migratecmd.Config{
        TemplateLang: migratecmd.TemplateLangJS,
        Automigrate:  true,
    })

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

## Build and deployment

<Steps>
  <Step title="Build the binary">
    ```bash theme={null}
    CGO_ENABLED=0 go build -o myapp
    ```
  </Step>

  <Step title="Run the application">
    ```bash theme={null}
    ./myapp serve --http=0.0.0.0:8080
    ```
  </Step>

  <Step title="Optional: Cross-compile">
    ```bash theme={null}
    # For Linux
    GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o myapp-linux

    # For macOS
    GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -o myapp-mac

    # For Windows
    GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -o myapp.exe
    ```
  </Step>
</Steps>

<Warning>
  Always use `CGO_ENABLED=0` when building PocketBase to ensure the binary is statically linked and portable.
</Warning>

## Complete example

Here's a complete example with multiple features:

```go main.go theme={null}
package main

import (
    "log"
    "net/http"
    "os"
    
    "github.com/pocketbase/pocketbase"
    "github.com/pocketbase/pocketbase/apis"
    "github.com/pocketbase/pocketbase/core"
    "github.com/pocketbase/pocketbase/plugins/jsvm"
)

func main() {
    app := pocketbase.New()

    // Register JSVM plugin
    var hooksDir string
    app.RootCmd.PersistentFlags().StringVar(
        &hooksDir,
        "hooksDir",
        "./pb_hooks",
        "the directory with the JS hooks",
    )
    
    app.RootCmd.ParseFlags(os.Args[1:])
    
    jsvm.MustRegister(app, jsvm.Config{
        HooksDir:      hooksDir,
        HooksWatch:    true,
        HooksPoolSize: 15,
    })

    // Add custom route
    app.OnServe().BindFunc(func(e *core.ServeEvent) error {
        e.Router.GET("/api/hello", func(re *core.RequestEvent) error {
            return re.JSON(http.StatusOK, map[string]string{
                "message": "Hello, World!",
            })
        })
        
        return e.Next()
    })

    // Add record creation hook
    app.OnRecordCreate("posts").BindFunc(func(e *core.RecordEvent) error {
        log.Printf("New post created: %s\n", e.Record.GetString("title"))
        return e.Next()
    })

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

## Next steps

<CardGroup cols={2}>
  <Card title="Event hooks" icon="webhook" href="/extending/event-hooks">
    Learn about all available event hooks
  </Card>

  <Card title="Custom routes" icon="route" href="/extending/custom-routes">
    Add custom API endpoints
  </Card>
</CardGroup>
