Base collections are the standard collection type in PocketBase. They provide a flexible way to store any kind of structured data with custom fields and validation rules.
When to use base collections
Use base collections when you need to:
Store general application data (posts, products, comments, etc.)
Create custom data structures without authentication features
Build relationships between different data types
Store file uploads alongside other data
Creating a base collection
Create a base collection using the factory function:
collection := core . NewBaseCollection ( "posts" )
This initializes a collection with:
Type set to "base"
A default id field (15-character lowercase alphanumeric)
Empty fields list ready for your custom fields
With a custom ID
You can optionally specify a custom collection ID:
collection := core . NewBaseCollection ( "posts" , "pbc_custom_id" )
Collection IDs are automatically generated based on the type and name if not specified. The ID format is pbc_ followed by a CRC32 checksum.
Default fields
Every base collection automatically includes an id field:
& TextField {
Name : "id" ,
System : true ,
PrimaryKey : true ,
Required : true ,
Min : 15 ,
Max : 15 ,
Pattern : "^[a-z0-9]+$" ,
AutogeneratePattern : "[a-z0-9]{15}" ,
}
This field:
Is automatically generated for new records
Cannot be changed after creation
Serves as the primary key for the collection
Is always required and system-managed
Complete example
Here’s a complete example of creating a blog posts collection:
package main
import (
" log "
" github.com/pocketbase/pocketbase "
" github.com/pocketbase/pocketbase/core "
)
func main () {
app := pocketbase . New ()
app . OnBootstrap (). BindFunc ( func ( e * core . BootstrapEvent ) error {
// Create the posts collection
posts := core . NewBaseCollection ( "posts" )
// Add custom fields
posts . Fields . Add (
& core . TextField {
Name : "title" ,
Required : true ,
Min : 3 ,
Max : 200 ,
},
& core . TextField {
Name : "slug" ,
Required : true ,
Pattern : "^[a-z0-9-]+$" ,
Max : 200 ,
},
& core . EditorField {
Name : "content" ,
Required : true ,
},
& core . BoolField {
Name : "published" ,
},
& core . RelationField {
Name : "author" ,
Required : true ,
CollectionId : "users_collection_id" ,
MaxSelect : 1 ,
},
& core . RelationField {
Name : "categories" ,
CollectionId : "categories_collection_id" ,
MaxSelect : 5 ,
},
& core . FileField {
Name : "featured_image" ,
MaxSelect : 1 ,
MaxSize : 5242880 , // 5MB
MimeTypes : [] string { "image/jpeg" , "image/png" , "image/webp" },
},
)
// Add indexes for better performance
posts . AddIndex ( "idx_slug" , true , "`slug`" , "" )
posts . AddIndex ( "idx_published" , false , "`published`" , "" )
// Set API rules
posts . ListRule = core . String ( "" )
posts . ViewRule = core . String ( "" )
posts . CreateRule = core . String ( "@request.auth.id != ''" )
posts . UpdateRule = core . String ( "@request.auth.id = author" )
posts . DeleteRule = core . String ( "@request.auth.id = author" )
// Save the collection
if err := e . App . Save ( posts ); err != nil {
return err
}
return e . Next ()
})
if err := app . Start (); err != nil {
log . Fatal ( err )
}
}
Field configuration
Base collections support all available field types. Here are some common patterns:
Text fields with validation
& core . TextField {
Name : "username" ,
Required : true ,
Min : 3 ,
Max : 30 ,
Pattern : "^[a-zA-Z0-9_]+$" ,
}
Numeric fields with constraints
& core . NumberField {
Name : "price" ,
Required : true ,
Min : core . Float64 ( 0 ),
Max : core . Float64 ( 999999.99 ),
}
Relations to other collections
// Single relation
& core . RelationField {
Name : "category" ,
Required : true ,
CollectionId : categoryCollectionId ,
MaxSelect : 1 ,
}
// Multiple relations
& core . RelationField {
Name : "tags" ,
CollectionId : tagCollectionId ,
MaxSelect : 10 ,
}
File uploads
& core . FileField {
Name : "attachments" ,
MaxSelect : 5 ,
MaxSize : 10485760 , // 10MB
MimeTypes : [] string {
"application/pdf" ,
"application/msword" ,
"text/plain" ,
},
}
Setting API rules
Control access to your collection with API rules:
Allow public read access
collection . ListRule = core . String ( "" )
collection . ViewRule = core . String ( "" )
Require authentication for writes
collection . CreateRule = core . String ( "@request.auth.id != ''" )
Owner-only updates and deletes
collection . UpdateRule = core . String ( "@request.auth.id = owner" )
collection . DeleteRule = core . String ( "@request.auth.id = owner" )
Common rule patterns
// Public read, authenticated write
collection . ListRule = core . String ( "" )
collection . ViewRule = core . String ( "" )
collection . CreateRule = core . String ( "@request.auth.id != ''" )
collection . UpdateRule = nil // Deny all
collection . DeleteRule = nil // Deny all
// Owner-based access
collection . ListRule = core . String ( "" )
collection . ViewRule = core . String ( "" )
collection . CreateRule = core . String ( "@request.auth.id != ''" )
collection . UpdateRule = core . String ( "@request.auth.id = userId" )
collection . DeleteRule = core . String ( "@request.auth.id = userId" )
// Role-based access
collection . CreateRule = core . String ( "@request.auth.role = 'admin'" )
collection . UpdateRule = core . String ( "@request.auth.role = 'admin' || @request.auth.role = 'editor'" )
Setting a rule to nil denies all access. Setting it to "" allows all access. Use filter expressions for conditional access.
Adding indexes
Indexes improve query performance for frequently searched fields:
// Unique index on email
collection . AddIndex ( "idx_unique_email" , true , "`email`" , "" )
// Composite index on multiple fields
collection . AddIndex ( "idx_user_status" , false , "`userId`, `status`" , "" )
// Conditional index
collection . AddIndex (
"idx_active_users" ,
false ,
"`createdAt`" ,
"`status` = 'active'" ,
)
Index parameters
Name : Unique identifier for the index
Unique : Whether the index enforces uniqueness
Columns : SQL expression for indexed columns (use backticks)
Where : Optional WHERE clause for conditional indexes
Updating collections
You can modify existing collections by finding them first:
collection , err := app . FindCollectionByNameOrId ( "posts" )
if err != nil {
return err
}
// Add a new field
collection . Fields . Add (
& core . DateField {
Name : "publishedAt" ,
},
)
// Save the changes
if err := app . Save ( collection ); err != nil {
return err
}
PocketBase handles schema migrations automatically when you save collection changes. Existing data is preserved when adding new fields.
Best practices
Naming
Validation
Security
Performance
Use plural names for collections (posts, users, categories)
Use snake_case or camelCase for field names
Keep names descriptive but concise
Set appropriate min/max constraints on fields
Use Required only when truly necessary
Add indexes to fields used in queries
Start with restrictive rules and loosen as needed
Test API rules thoroughly before deploying
Use @request.auth to implement ownership checks
Add indexes to frequently queried fields
Limit relation depth in queries
Use view collections for complex read-only queries
Next steps
Auth collections Add user authentication to your app
Fields reference Explore all available field types
View collections Create read-only SQL-based collections
API rules Learn about the rule syntax