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:
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:
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:
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 )
}
}
The application is bootstrapped automatically when you call app.Start(). Database connections, migrations, and settings are initialized at this point.
Application lifecycle
The PocketBase app follows this lifecycle:
Creation
Create a new PocketBase instance with New() or NewWithConfig().
Configuration
Register plugins, hooks, and custom commands before starting. app . OnServe (). BindFunc ( func ( e * core . ServeEvent ) error {
// Add custom routes
return e . Next ()
})
Bootstrap
The app bootstraps when Start() or Execute() is called. This initializes:
Database connections
Migrations
Application settings
Event hooks
Execution
The app executes the command (default is serve) and runs until terminated.
Termination
Graceful shutdown with cleanup via OnTerminate() hooks.
Custom commands
You can add custom CLI commands to your PocketBase app:
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:
Working with the database
Query records
Create records
Update records
Delete records
Raw SQL
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 ()
})
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 ()
})
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 ()
})
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 ()
})
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 ()
})
Accessing app settings
You can read and modify application settings:
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:
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
Build the binary
CGO_ENABLED = 0 go build -o myapp
Run the application
./myapp serve --http=0.0.0.0:8080
Optional: Cross-compile
# 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
Always use CGO_ENABLED=0 when building PocketBase to ensure the binary is statically linked and portable.
Complete example
Here’s a complete example with multiple features:
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
Event hooks Learn about all available event hooks
Custom routes Add custom API endpoints