Collections - Dart SDK Documentation
Overview
Collections represent your application data. Under the hood they are backed by plain SQLite tables that are generated automatically with the collection name and fields (columns).
A single entry of a collection is called a record (a single row in the SQL table).
Collection Types
Base Collection
Default collection type for storing any application data.
import 'package:bosbase/bosbase.dart';
final pb = Bosbase('http://localhost:8090');
await pb.admins.authWithPassword('admin@example.com', 'password');
final collection = await pb.collections.createBase('articles', overrides: {
'fields': [
{'name': 'title', 'type': 'text', 'required': true},
{'name': 'description', 'type': 'text'}
]
});
View Collection
Read-only collection populated from a SQL SELECT statement.
final view = await pb.collections.createView(
'post_stats',
viewQuery: '''
SELECT posts.id, posts.name, count(comments.id) as totalComments
FROM posts LEFT JOIN comments on comments.postId = posts.id
GROUP BY posts.id
''',
);
Auth Collection
Base collection with authentication fields (email, password, etc.).
final users = await pb.collections.createAuth('users', overrides: {
'fields': [
{'name': 'name', 'type': 'text', 'required': true}
]
});
Collections API
List Collections
final result = await pb.collections.getList(page: 1, perPage: 50);
final all = await pb.collections.getFullList();
Get Collection
final collection = await pb.collections.getOne('articles');
Create Collection
// Using scaffolds
final base = await pb.collections.createBase('articles');
final auth = await pb.collections.createAuth('users');
final view = await pb.collections.createView('stats', viewQuery: 'SELECT * FROM posts');
// Manual
final collection = await pb.collections.create(CollectionModel(
type: 'base',
name: 'articles',
fields: [
CollectionField(name: 'title', type: 'text', required: true),
// Note: created and updated fields must be explicitly added if you want to use them
CollectionField(
name: 'created',
type: 'autodate',
required: false,
options: {
'onCreate': true,
'onUpdate': false,
},
),
CollectionField(
name: 'updated',
type: 'autodate',
required: false,
options: {
'onCreate': true,
'onUpdate': true,
},
),
],
));
Update Collection
// Update collection rules
final updated = await pb.collections.update('articles', body: {
'listRule': 'published = true'
});
// Update collection name
final updated = await pb.collections.update('articles', body: {
'name': 'posts'
});
Add Fields to Collection
To add a new field to an existing collection, fetch the collection, add the field to the fields array, and update:
// Get existing collection
var collection = await pb.collections.getOne('articles');
// Add new field to existing fields
final newFields = List<Map<String, dynamic>>.from(collection.fields);
newFields.add({
'name': 'views',
'type': 'number',
'min': 0,
'onlyInt': true,
});
// Update collection with new field
await pb.collections.update('articles', body: {
'fields': newFields,
});
// Or add multiple fields at once
newFields.addAll([
{
'name': 'excerpt',
'type': 'text',
'max': 500,
},
{
'name': 'cover',
'type': 'file',
'maxSelect': 1,
'mimeTypes': ['image/jpeg', 'image/png'],
},
]);
await pb.collections.update('articles', body: {
'fields': newFields,
});
Delete Fields from Collection
To delete a field, fetch the collection, remove the field from the fields array, and update:
// Get existing collection
var collection = await pb.collections.getOne('articles');
// Remove field by filtering it out
final updatedFields = (collection.fields as List)
.where((field) => field['name'] != 'oldFieldName')
.toList();
// Update collection without the deleted field
await pb.collections.update('articles', body: {
'fields': updatedFields,
});
// Or remove multiple fields
final fieldsToKeep = ['title', 'content', 'author', 'status'];
final filteredFields = (collection.fields as List)
.where((field) =>
fieldsToKeep.contains(field['name']) || field['system'] == true)
.toList();
await pb.collections.update('articles', body: {
'fields': filteredFields,
});
Modify Fields in Collection
To modify an existing field (e.g., change its type, add options, etc.), fetch the collection, update the field object, and save:
// Get existing collection
var collection = await pb.collections.getOne('articles');
// Convert fields to mutable list
final fields = List<Map<String, dynamic>>.from(collection.fields);
// Find and modify a field
final titleIndex = fields.indexWhere((f) => f['name'] == 'title');
if (titleIndex != -1) {
fields[titleIndex] = {
...fields[titleIndex],
'max': 200, // Change max length
'required': true, // Make required
};
}
// Update the field type
final statusIndex = fields.indexWhere((f) => f['name'] == 'status');
if (statusIndex != -1) {
// Note: Changing field types may require data migration
fields[statusIndex] = {
...fields[statusIndex],
'type': 'select',
'options': {
'values': ['draft', 'published', 'archived'],
},
'maxSelect': 1,
};
}
// Save changes
await pb.collections.update('articles', body: {
'fields': fields,
});
Complete Example: Managing Collection Fields
import 'package:bosbase/bosbase.dart';
final pb = Bosbase('http://localhost:8090');
await pb.admins.authWithPassword('admin@example.com', 'password');
// Get existing collection
var collection = await pb.collections.getOne('articles');
// Convert to mutable list
final fields = List<Map<String, dynamic>>.from(collection.fields);
// Add new fields
fields.addAll([
{
'name': 'tags',
'type': 'select',
'options': {
'values': ['tech', 'design', 'business'],
},
'maxSelect': 5,
},
{
'name': 'published_at',
'type': 'date',
},
]);
// Remove an old field
fields.removeWhere((f) => f['name'] == 'oldField');
// Modify existing field
final viewsIndex = fields.indexWhere((f) => f['name'] == 'views');
if (viewsIndex != -1) {
fields[viewsIndex] = {
...fields[viewsIndex],
'max': 1000000, // Increase max value
};
}
// Save all changes at once
await pb.collections.update('articles', body: {
'fields': fields,
});
Delete Collection
await pb.collections.delete('articles');
Records API
List Records
final result = await pb.collection('articles').getList(
page: 1,
perPage: 20,
filter: 'published = true',
sort: '-created',
expand: 'author',
);
print(result.items); // List of records
print(result.page); // Current page
print(result.perPage); // Items per page
print(result.totalItems); // Total count
print(result.totalPages); // Total pages
Get Record
final record = await pb.collection('articles').getOne(
'RECORD_ID',
expand: 'author,category',
);
Create Record
final record = await pb.collection('articles').create(body: {
'title': 'My Article',
'views+': 1, // Field modifier
});
Update Record
await pb.collection('articles').update('RECORD_ID', body: {
'title': 'Updated',
'views+': 1,
'tags+': 'new-tag',
});
Delete Record
await pb.collection('articles').delete('RECORD_ID');
Field Types
BoolField
CollectionField(name: 'published', type: 'bool', required: true)
await pb.collection('articles').create(body: {'published': true});
NumberField
CollectionField(name: 'views', type: 'number', min: 0)
await pb.collection('articles').update('ID', body: {'views+': 1});
TextField
CollectionField(name: 'title', type: 'text', required: true, min: 6, max: 100)
await pb.collection('articles').create(body: {'slug:autogenerate': 'article-'});
EmailField
CollectionField(name: 'email', type: 'email', required: true)
URLField
CollectionField(name: 'website', type: 'url')
EditorField
CollectionField(name: 'content', type: 'editor', required: true)
await pb.collection('articles').create(body: {'content': '<p>HTML content</p>'});
DateField
CollectionField(name: 'published_at', type: 'date')
await pb.collection('articles').create(body: {
'published_at': '2024-11-10 18:45:27.123Z'
});
AutodateField
Important Note: Bosbase does not initialize created and updated fields by default. To use these fields, you must explicitly add them when initializing the collection with the proper options:
// Create field with proper options
CollectionField(
name: 'created',
type: 'autodate',
required: false,
options: {
'onCreate': true, // Set on record creation
'onUpdate': false // Don't update on record update
},
)
// For updated field
CollectionField(
name: 'updated',
type: 'autodate',
required: false,
options: {
'onCreate': true, // Set on record creation
'onUpdate': true // Update on record update
},
)
// The value is automatically set by the backend based on the options
SelectField
// Single select
CollectionField(
name: 'status',
type: 'select',
options: {
'values': ['draft', 'published']
},
maxSelect: 1,
)
await pb.collection('articles').create(body: {'status': 'published'});
// Multiple select
CollectionField(
name: 'tags',
type: 'select',
options: {
'values': ['tech', 'design']
},
maxSelect: 5,
)
await pb.collection('articles').update('ID', body: {'tags+': 'marketing'});
FileField
// Single file
CollectionField(
name: 'cover',
type: 'file',
maxSelect: 1,
mimeTypes: ['image/jpeg'],
)
final file = http.MultipartFile.fromPath('cover', '/path/to/image.jpg');
await pb.collection('articles').create(
body: {'title': 'My Article'},
files: [file],
);
RelationField
CollectionField(
name: 'author',
type: 'relation',
options: {'collectionId': 'users'},
maxSelect: 1,
)
await pb.collection('articles').create(body: {'author': 'USER_ID'});
final record = await pb.collection('articles').getOne('ID', expand: 'author');
JSONField
CollectionField(name: 'metadata', type: 'json')
await pb.collection('articles').create(body: {
'metadata': {
'seo': {'title': 'SEO Title'}
}
});
GeoPointField
CollectionField(name: 'location', type: 'geoPoint')
await pb.collection('places').create(body: {
'location': {'lon': 139.6917, 'lat': 35.6586}
});
Complete Example
import 'package:bosbase/bosbase.dart';
final pb = Bosbase('http://localhost:8090');
await pb.admins.authWithPassword('admin@example.com', 'password');
// Create collections
final users = await pb.collections.createAuth('users');
final articles = await pb.collections.createBase('articles', overrides: {
'fields': [
{'name': 'title', 'type': 'text', 'required': true},
{
'name': 'author',
'type': 'relation',
'options': {'collectionId': users.id},
'maxSelect': 1,
}
]
});
// Create and authenticate user
final user = await pb.collection('users').create(body: {
'email': 'user@example.com',
'password': 'password123',
'passwordConfirm': 'password123',
});
await pb.collection('users').authWithPassword('user@example.com', 'password123');
// Create article
final article = await pb.collection('articles').create(body: {
'title': 'My Article',
'author': user.id,
});
// Subscribe to changes
await pb.collection('articles').subscribe('*', (e) {
print('${e.action}: ${e.record}');
});
Authentication Example
// Create auth collection
final customers = await pb.collections.createAuth('customers', overrides: {
'fields': [
{'name': 'name', 'type': 'text', 'required': true},
{'name': 'phone', 'type': 'text'},
]
});
// Register customer
final customer = await pb.collection('customers').create(body: {
'email': 'customer@example.com',
'emailVisibility': true,
'password': 'password123',
'passwordConfirm': 'password123',
'name': 'Jane Doe',
'phone': '+1234567890',
});
// Authenticate
final auth = await pb.collection('customers').authWithPassword(
'customer@example.com',
'password123',
);
print(auth.token);
print(auth.record);
// Check authentication
if (pb.authStore.isValid) {
print('Current user: ${pb.authStore.record}');
}
// Logout
pb.authStore.clear();
Realtime Subscriptions
// Subscribe to all changes
final unsubscribeAll = await pb.collection('articles').subscribe('*', (e) {
print('Action: ${e.action}');
print('Record: ${e.record}');
});
// Subscribe to specific record
final unsubscribeRecord = await pb.collection('articles').subscribe(
'RECORD_ID',
(e) {
print('Record updated: ${e.record}');
},
);
// Unsubscribe
await pb.collection('articles').unsubscribe('RECORD_ID');
await pb.collection('articles').unsubscribe('*');
await pb.collection('articles').unsubscribe(); // All