AI Development Guide - GDScript SDK
This guide provides a comprehensive, fast reference for AI systems to quickly develop applications using the BosBase GDScript SDK. All examples are production-ready and follow best practices.
Table of Contents
- Authentication
- Initialize Collections
- Define Collection Fields
- Add Data to Collections
- Modify Collection Data
- Delete Data from Collections
- Query Collection Contents
- Add and Delete Fields from Collections
- Query Collection Field Information
- Upload Files
- Query Logs
- Send Emails
Authentication
Initialize Client
var BosBase = preload("res://gdscript-sdk/src/bosbase.gd")
var pb = BosBase.new("http://localhost:8090")
Password Authentication
# Authenticate with email/username and password
var auth_data = await pb.collection("users").auth_with_password(
"user@example.com",
"password123"
)
if auth_data is ClientResponseError:
push_error("Authentication failed: " + auth_data.to_string())
return
# Auth data is automatically stored
print(pb.auth_store.is_valid) # true
print(pb.auth_store.token) # JWT token
print(pb.auth_store.record) # User record
OAuth2 Authentication
# Get OAuth2 providers
var methods = await pb.collection("users").list_auth_methods()
if methods is ClientResponseError:
push_error("Failed to get auth methods: " + methods.to_string())
return
print(methods.oauth2.providers) # Available providers
# Authenticate with OAuth2 (implementation depends on provider)
# Note: OAuth2 flow in GDScript requires custom implementation
OTP Authentication
# Request OTP
var otp_response = await pb.collection("users").request_verification("user@example.com")
if otp_response is ClientResponseError:
push_error("Failed to request OTP: " + otp_response.to_string())
return
# Authenticate with OTP
var auth_data = await pb.collection("users").auth_with_otp(
otp_response.otpId,
"123456" # OTP code
)
Check Authentication Status
if pb.auth_store.is_valid:
print("Authenticated as: ", pb.auth_store.record.email)
else:
print("Not authenticated")
Logout
pb.auth_store.clear()
Initialize Collections
Create Base Collection
var collection = await pb.collections.create({
"name": "posts",
"type": "base",
"fields": [
{
"name": "title",
"type": "text",
"required": true,
},
],
})
if collection is ClientResponseError:
push_error("Failed to create collection: " + collection.to_string())
return
print("Collection ID: ", collection.id)
Create Auth Collection
var auth_collection = await pb.collections.create({
"name": "users",
"type": "auth",
"fields": [
{
"name": "name",
"type": "text",
"required": false,
},
],
"passwordAuth": {
"enabled": true,
"identityFields": ["email", "username"],
},
})
Create View Collection
var view_collection = await pb.collections.create({
"name": "published_posts",
"type": "view",
"viewQuery": "SELECT * FROM posts WHERE published = true",
})
Get Collection by ID or Name
var collection = await pb.collections.get_one("posts")
# or by ID
var collection = await pb.collections.get_one("_pbc_2287844090")
Define Collection Fields
Add Field to Collection
var updated_collection = await pb.collections.add_field("posts", {
"name": "content",
"type": "editor",
"required": false,
})
Common Field Types
# Text field
{
"name": "title",
"type": "text",
"required": true,
"min": 10,
"max": 255,
}
# Number field
{
"name": "views",
"type": "number",
"required": false,
"min": 0,
}
# Boolean field
{
"name": "published",
"type": "bool",
"required": false,
}
# Date field
{
"name": "published_at",
"type": "date",
"required": false,
}
# File field
{
"name": "avatar",
"type": "file",
"required": false,
"maxSelect": 1,
"maxSize": 2097152, # 2MB
"mimeTypes": ["image/jpeg", "image/png"],
}
# Relation field
{
"name": "author",
"type": "relation",
"required": true,
"options": {
"collectionId": "_pbc_users_auth_",
},
"maxSelect": 1,
}
# Select field
{
"name": "status",
"type": "select",
"required": true,
"options": {
"values": ["draft", "published", "archived"],
},
}
Update Field
var updated_collection = await pb.collections.update_field("posts", "title", {
"max": 500,
"required": true,
})
Remove Field
var updated_collection = await pb.collections.remove_field("posts", "old_field")
Add Data to Collections
Create Single Record
var record = await pb.collection("posts").create({
"title": "My First Post",
"content": "This is the content",
"published": true,
})
if record is ClientResponseError:
push_error("Failed to create record: " + record.to_string())
return
print("Created record ID: ", record.id)
Create Record with File Upload
# Read file
var file_path = "res://image.jpg"
var file = FileAccess.open(file_path, FileAccess.READ)
if file == null:
push_error("Failed to open file")
return
var file_data = file.get_buffer(file.get_length())
file.close()
var files = {
"image": {
"filename": "image.jpg",
"content_type": "image/jpeg",
"data": file_data
}
}
var record = await pb.collection("posts").create({
"title": "Post with Image",
}, {}, files)
Create Record with Relations
var record = await pb.collection("posts").create({
"title": "My Post",
"author": "user_record_id", # Related record ID
"categories": ["cat1_id", "cat2_id"], # Multiple relations
})
Batch Create Records
var records = await pb.batch([
{
"method": "POST",
"url": "/api/collections/posts/records",
"body": {"title": "Post 1"},
},
{
"method": "POST",
"url": "/api/collections/posts/records",
"body": {"title": "Post 2"},
},
])
Modify Collection Data
Update Single Record
var updated = await pb.collection("posts").update("record_id", {
"title": "Updated Title",
"content": "Updated content",
})
Update Record with File
# Read new file
var file = FileAccess.open("res://new_image.jpg", FileAccess.READ)
var file_data = file.get_buffer(file.get_length())
file.close()
var files = {
"image": {
"filename": "new_image.jpg",
"content_type": "image/jpeg",
"data": file_data
}
}
var updated = await pb.collection("posts").update("record_id", {
"title": "Updated Title",
}, {}, files)
Partial Update
# Only update specific fields
var updated = await pb.collection("posts").update("record_id", {
"views": 100, # Only update views
})
Delete Data from Collections
Delete Single Record
var result = await pb.collection("posts").delete("record_id")
if result is ClientResponseError:
push_error("Failed to delete: " + result.to_string())
Delete Multiple Records
# Using batch
await pb.batch([
{
"method": "DELETE",
"url": "/api/collections/posts/records/record_id_1",
},
{
"method": "DELETE",
"url": "/api/collections/posts/records/record_id_2",
},
])
Delete All Records (Truncate)
var result = await pb.collections.truncate("posts")
if result is ClientResponseError:
push_error("Failed to truncate: " + result.to_string())
Query Collection Contents
List Records with Pagination
var result = await pb.collection("posts").get_list(1, 50)
if result is ClientResponseError:
push_error("Failed to get list: " + result.to_string())
return
print(result.page) # 1
print(result.perPage) # 50
print(result.totalItems) # Total count
print(result.items) # Array of records
Filter Records
var result = await pb.collection("posts").get_list(1, 50, {
"filter": "published = true && views > 100",
"sort": "-created",
})
Filter Operators
# Equality
"filter": "status = \"published\""
# Comparison
"filter": "views > 100"
"filter": "created >= \"2023-01-01\""
# Text search
"filter": "title ~ \"javascript\""
# Multiple conditions
"filter": "status = \"published\" && views > 100"
"filter": "status = \"draft\" || status = \"pending\""
# Relation filter
"filter": "author.id = \"user_id\""
Sort Records
# Single field
"sort": "-created" # DESC
"sort": "title" # ASC
# Multiple fields
"sort": "-created,title" # DESC by created, then ASC by title
Expand Relations
var result = await pb.collection("posts").get_list(1, 50, {
"expand": "author,categories",
})
if not result is ClientResponseError:
# Access expanded data
for post in result.items:
var author = post.get("expand", {}).get("author", {})
print(author.get("name", ""))
print(post.get("expand", {}).get("categories", []))
Get Single Record
var record = await pb.collection("posts").get_one("record_id", {
"expand": "author",
})
Get First Matching Record
var record = await pb.collection("posts").get_first_list_item(
"slug = \"my-post-slug\"",
{
"expand": "author",
}
)
Get All Records
var all_records = await pb.collection("posts").get_full_list({
"filter": "published = true",
"sort": "-created",
})
Add and Delete Fields from Collections
Add Field
var collection = await pb.collections.add_field("posts", {
"name": "tags",
"type": "select",
"options": {
"values": ["tech", "science", "art"],
},
})
Update Field
var collection = await pb.collections.update_field("posts", "tags", {
"options": {
"values": ["tech", "science", "art", "music"],
},
})
Remove Field
var collection = await pb.collections.remove_field("posts", "old_field")
Get Field Information
var field = await pb.collections.get_field("posts", "title")
if not field is ClientResponseError:
print(field.type, field.required, field.options)
Query Collection Field Information
Get All Fields for a Collection
var collection = await pb.collections.get_one("posts")
if not collection is ClientResponseError:
for field in collection.fields:
print(field.name, field.type, field.required)
Get Collection Schema (Simplified)
var schema = await pb.collections.get_schema("posts")
if not schema is ClientResponseError:
print(schema.fields) # Array of field info
Get All Collection Schemas
var schemas = await pb.collections.get_all_schemas()
if not schemas is ClientResponseError:
for collection in schemas.collections:
print(collection.name, collection.fields)
Query Field Information for Single Collection
# Method 1: Get full collection
var collection = await pb.collections.get_one("posts")
if not collection is ClientResponseError:
for field in collection.fields:
if field.name == "title":
print("Found title field")
# Method 2: Get specific field
var field = await pb.collections.get_field("posts", "title")
# Method 3: Get schema
var schema = await pb.collections.get_schema("posts")
if not schema is ClientResponseError:
for f in schema.fields:
if f.name == "title":
print("Found title field info")
Upload Files
Upload File with Record Creation
var file = FileAccess.open("res://image.jpg", FileAccess.READ)
var file_data = file.get_buffer(file.get_length())
file.close()
var files = {
"image": {
"filename": "image.jpg",
"content_type": "image/jpeg",
"data": file_data
}
}
var record = await pb.collection("posts").create({
"title": "Post Title",
}, {}, files)
Upload File with Record Update
var file = FileAccess.open("res://new_image.jpg", FileAccess.READ)
var file_data = file.get_buffer(file.get_length())
file.close()
var files = {
"image": {
"filename": "new_image.jpg",
"content_type": "image/jpeg",
"data": file_data
}
}
var updated = await pb.collection("posts").update("record_id", {}, {}, files)
Get File URL
var record = await pb.collection("posts").get_one("record_id")
if not record is ClientResponseError:
var file_url = pb.files.get_url(record, record.get("image", ""))
print("File URL: ", file_url)
Get File URL with Options
var file_url = pb.files.get_url(record, record.get("image", ""), {
"thumb": "100x100", # Thumbnail
"download": true, # Force download
})
Get Private File Token
# For accessing private files
var token = await pb.files.get_token()
if not token is ClientResponseError:
# Use token in file URL query params
print("Token: ", token)
Query Logs
List Logs
var logs = await pb.logs.get_list(1, 50)
if not logs is ClientResponseError:
print(logs.items) # Array of log entries
Filter Logs
var logs = await pb.logs.get_list(1, 50, {
"filter": "level >= 400", # Error level and above
"sort": "-created",
})
Get Single Log
var log = await pb.logs.get_one("log_id")
if not log is ClientResponseError:
print(log.message, log.data)
Get Log Statistics
var stats = await pb.logs.get_stats({
"filter": "level >= 400",
})
if not stats is ClientResponseError:
for stat in stats:
print(stat.date, stat.total)
Log Levels
0- Debug1- Info2- Warning3- Error4- Fatal
Send Emails
Note: Email sending is typically handled server-side via hooks or backend code. The SDK doesn’t provide direct email sending methods, but you can trigger email-related operations.
Trigger Email Verification
# Request verification email
var result = await pb.collection("users").request_verification("user@example.com")
if result is ClientResponseError:
push_error("Failed to request verification: " + result.to_string())
Trigger Password Reset Email
# Request password reset email
var result = await pb.collection("users").request_password_reset("user@example.com")
if result is ClientResponseError:
push_error("Failed to request password reset: " + result.to_string())
Email Change Request
# Request email change
var result = await pb.collection("users").request_email_change("newemail@example.com")
if result is ClientResponseError:
push_error("Failed to request email change: " + result.to_string())
Server-Side Email Sending
Email sending is configured in the backend settings and triggered automatically by:
- User registration (verification email)
- Password reset requests
- Email change requests
- Custom hooks
To send custom emails, you would typically:
- Create a backend hook that uses
app.NewMailClient() - Or use the admin API to configure email templates
- Or trigger email-related record operations that automatically send emails
Complete Example: Full Application Flow
var BosBase = preload("res://gdscript-sdk/src/bosbase.gd")
var pb = BosBase.new("http://localhost:8090")
func setup_application() -> void:
# 1. Authenticate
var auth = await pb.collection("users").auth_with_password("admin@example.com", "password")
if auth is ClientResponseError:
push_error("Authentication failed: " + auth.to_string())
return
# 2. Create collection
var collection = await pb.collections.create({
"name": "posts",
"type": "base",
"fields": [
{"name": "title", "type": "text", "required": true},
{"name": "content", "type": "text"},
{"name": "published", "type": "bool"},
],
})
if collection is ClientResponseError:
push_error("Failed to create collection: " + collection.to_string())
return
# 3. Add more fields
await pb.collections.add_field("posts", {
"name": "views",
"type": "number",
"min": 0,
})
# 4. Create records
var post = await pb.collection("posts").create({
"title": "Hello World",
"content": "My first post",
"published": true,
"views": 0,
})
# 5. Query records
var posts = await pb.collection("posts").get_list(1, 10, {
"filter": "published = true",
"sort": "-created",
})
# 6. Update record
if not post is ClientResponseError:
await pb.collection("posts").update(post.id, {
"views": 100,
})
# 7. Query logs
var logs = await pb.logs.get_list(1, 20, {
"filter": "level >= 400",
})
print("Application setup complete!")
Quick Reference
Common Patterns
# Check if authenticated
if pb.auth_store.is_valid:
# Do authenticated operations
pass
# Get current user
var user = pb.auth_store.record
# Refresh auth token
var refresh = await pb.collection("users").auth_refresh()
# Error handling
var result = await pb.collection("posts").create({"title": "Test"})
if result is ClientResponseError:
if result.status == 400:
push_error("Validation error: ", result.data)
elif result.status == 401:
push_error("Not authenticated")
Field Types Reference
text- Text inputnumber- Numeric valuebool- Booleanemail- Email addressurl- URLdate- Dateselect- Single selectjson- JSON datafile- File uploadrelation- Relation to another collectioneditor- Rich text editor
Best Practices
- Always handle errors: Check for
ClientResponseErrorafter API calls - Check authentication: Verify
pb.auth_store.is_validbefore operations - Use pagination: Don’t fetch all records at once for large collections
- Validate data: Ensure required fields are provided
- Use filters: Filter data on the server, not client-side
- Expand relations wisely: Only expand what you need
- Handle file uploads: Use file dictionaries for file fields
- Refresh tokens: Use
auth_refresh()to maintain sessions
LangChaingo Recipes
Quick Completion
var result = await pb.langchaingo.completions({
"model": {"provider": "openai", "model": "gpt-4"},
"messages": [
{"role": "system", "content": "You are a helpful assistant"},
{"role": "user", "content": "Hello"}
],
"temperature": 0.4
})
if not result is ClientResponseError:
print(result.content)
Retrieval-Augmented Answering
var rag = await pb.langchaingo.rag({
"collection": "knowledge-base",
"question": "Why is the sky blue?",
"topK": 3,
"returnSources": true,
})
if not rag is ClientResponseError:
print(rag.answer)
print(rag.sources)
This guide provides all essential operations for building applications with the BosBase GDScript SDK. For more detailed information, refer to the specific API documentation files.