API Records - Kotlin SDK Documentation
Overview
The Records API provides comprehensive CRUD (Create, Read, Update, Delete) operations for collection records, along with powerful search, filtering, and authentication capabilities.
Key Features:
- Paginated list and search with filtering and sorting
- Single record retrieval with expand support
- Create, update, and delete operations
- Batch operations for multiple records
- Authentication methods (password, OAuth2, OTP)
- Email verification and password reset
- Relation expansion up to 6 levels deep
- Field selection and excerpt modifiers
Backend Endpoints:
GET /api/collections/{collection}/records- List recordsGET /api/collections/{collection}/records/{id}- View recordPOST /api/collections/{collection}/records- Create recordPATCH /api/collections/{collection}/records/{id}- Update recordDELETE /api/collections/{collection}/records/{id}- Delete recordPOST /api/batch- Batch operations
📖 Reference: For detailed API concepts, see the JavaScript SDK Records documentation.
CRUD Operations
List/Search Records
Returns a paginated records list with support for sorting, filtering, and expansion.
import com.bosbase.sdk.BosBase
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.contentOrNull
val pb = BosBase("http://127.0.0.1:8090")
// Basic list with pagination
val result = pb.collection("posts").getList(page = 1, perPage = 50)
println(result.page) // 1
println(result.perPage) // 50
println(result.totalItems) // 150
println(result.totalPages) // 3
result.items.forEach { post ->
println(post["title"]?.jsonPrimitive?.contentOrNull)
}
Advanced List with Filtering and Sorting
// Filter and sort
val result = pb.collection("posts").getList(
page = 1,
perPage = 50,
filter = "created >= '2022-01-01 00:00:00' && status = 'published'",
sort = "-created,title", // DESC by created, ASC by title
expand = "author,categories"
)
// Using filter helper for safe parameter binding
val searchTerm = "javascript"
val result2 = pb.collection("posts").getList(
page = 1,
perPage = 50,
filter = pb.filter("title ~ {:term} && views > {:minViews}", mapOf(
"term" to searchTerm,
"minViews" to 100
)),
sort = "-views"
)
Get Full List
Fetch all records at once (useful for small collections):
// Get all records
val allPosts = pb.collection("posts").getFullList(
sort = "-created",
filter = "status = 'published'"
)
// With batch size for large collections
val allPosts = pb.collection("posts").getFullList(
batch = 200,
sort = "-created"
)
Get First Matching Record
Get only the first record that matches a filter:
val post = pb.collection("posts").getFirstListItem(
filter = "slug = 'my-post-slug'",
expand = "author,categories.tags"
)
println(post["title"]?.jsonPrimitive?.contentOrNull)
View Record
Retrieve a single record by ID:
// Basic retrieval
val record = pb.collection("posts").getOne("RECORD_ID")
// With expand
val record = pb.collection("posts").getOne(
id = "RECORD_ID",
expand = "author,categories.tags"
)
// With field selection
val record = pb.collection("posts").getOne(
id = "RECORD_ID",
fields = "id,title,content,author.email"
)
Create Record
Create a new record:
// Basic create
val newPost = pb.collection("posts").create(
body = mapOf(
"title" to "Hello Kotlin!",
"content" to "This is my first post",
"status" to "published"
)
)
println("Created record ID: ${newPost["id"]?.jsonPrimitive?.contentOrNull}")
Create with File Upload
import com.bosbase.sdk.FileAttachment
import java.io.File
// Create record with file attachment
val file = File("path/to/image.jpg")
val attachment = FileAttachment.fromFile(file)
val newPost = pb.collection("posts").create(
body = mapOf(
"title" to "Post with Image",
"content" to "Content here"
),
files = mapOf(
"image" to listOf(attachment)
)
)
Create with Multiple Files
val attachments = listOf(
FileAttachment.fromFile(File("image1.jpg")),
FileAttachment.fromFile(File("image2.jpg"))
)
val newPost = pb.collection("posts").create(
body = mapOf("title" to "Post with Multiple Images"),
files = mapOf("images" to attachments)
)
Update Record
Update an existing record:
// Basic update
val updated = pb.collection("posts").update(
id = "RECORD_ID",
body = mapOf(
"title" to "Updated Title",
"status" to "archived"
)
)
println("Updated: ${updated["title"]?.jsonPrimitive?.contentOrNull}")
Update with File Replacement
val updated = pb.collection("posts").update(
id = "RECORD_ID",
body = mapOf("title" to "New Title"),
files = mapOf(
"image" to listOf(FileAttachment.fromFile(File("new-image.jpg")))
)
)
Delete Record
Delete a record:
pb.collection("posts").delete("RECORD_ID")
Filtering
Filter Syntax
BosBase uses a custom filter syntax similar to SQL WHERE clauses:
// Comparison operators
"status = 'published'"
"views > 100"
"created >= '2022-01-01 00:00:00'"
// Logical operators
"status = 'published' && views > 100"
"status = 'draft' || status = 'archived'"
// String operators
"title ~ 'javascript'" // contains
"title !~ 'test'" // not contains
"title ? 'test'" // like (case-insensitive)
"title !? 'test'" // not like
// Null checks
"description != null"
"deleted = null"
Safe Filter Parameter Binding
Use the filter() helper method to safely bind parameters and prevent injection:
val searchTerm = "user's post" // Contains apostrophe
val minViews = 100
// Safe: automatically escapes special characters
val filter = pb.filter(
"title ~ {:term} && views > {:minViews}",
mapOf(
"term" to searchTerm,
"minViews" to minViews
)
)
// Result: "title ~ 'user\'s post' && views > 100"
val result = pb.collection("posts").getList(
page = 1,
perPage = 50,
filter = filter
)
Supported Parameter Types
The filter helper supports:
String(auto-escaped with single quotes)Number(Int, Long, Double, etc.)Boolean(true/false)Date(converted to ISO format)null- Other types (converted via JSON.stringify)
Sorting
Sort records using the sort parameter:
// Single field (ascending)
sort = "created"
// Single field (descending)
sort = "-created"
// Multiple fields
sort = "-created,title,-views" // DESC by created, ASC by title, DESC by views
Expansion (Relations)
Expand related records up to 6 levels deep:
// Single relation
val post = pb.collection("posts").getOne(
id = "RECORD_ID",
expand = "author"
)
// Multiple relations
val post = pb.collection("posts").getOne(
id = "RECORD_ID",
expand = "author,categories"
)
// Nested relations
val post = pb.collection("posts").getOne(
id = "RECORD_ID",
expand = "author,categories.tags"
)
// Access expanded data
val authorName = post["expand"]?.jsonObject
?.get("author")?.jsonObject
?.get("name")?.jsonPrimitive?.contentOrNull
Field Selection
Select specific fields to reduce payload size:
// Select specific fields
val record = pb.collection("posts").getOne(
id = "RECORD_ID",
fields = "id,title,content,author.email"
)
// In list queries
val result = pb.collection("posts").getList(
page = 1,
perPage = 50,
fields = "id,title,created"
)
Batch Operations
Perform multiple operations in a single transaction:
import com.bosbase.sdk.BosBase
val pb = BosBase("http://localhost:8090")
// Create a batch
val batch = pb.createBatch()
// Register operations
batch.collection("posts").create(mapOf("title" to "Post 1"))
batch.collection("posts").create(mapOf("title" to "Post 2"))
batch.collection("posts").update("RECORD_ID", mapOf("title" to "Updated"))
batch.collection("posts").delete("ANOTHER_RECORD_ID")
// Execute batch
val results = batch.send()
// Results contain responses for each operation
results.forEach { result ->
println("Operation result: $result")
}
Error Handling
All record operations throw ClientResponseError on failure:
import com.bosbase.sdk.ClientResponseError
try {
val record = pb.collection("posts").getOne("INVALID_ID")
} catch (e: ClientResponseError) {
when (e.status) {
404 -> println("Record not found")
403 -> println("Access forbidden")
400 -> println("Bad request: ${e.response}")
else -> println("Error: ${e.status}")
}
}
Examples
Complete CRUD Example
import com.bosbase.sdk.BosBase
import com.bosbase.sdk.ClientResponseError
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.contentOrNull
fun main() {
val pb = BosBase("http://localhost:8090")
try {
// Create
val newPost = pb.collection("posts").create(
body = mapOf(
"title" to "My First Post",
"content" to "This is the content",
"status" to "published"
)
)
val postId = newPost["id"]?.jsonPrimitive?.contentOrNull
println("Created post: $postId")
// Read
val post = pb.collection("posts").getOne(
id = postId ?: "",
expand = "author"
)
println("Title: ${post["title"]?.jsonPrimitive?.contentOrNull}")
// Update
val updated = pb.collection("posts").update(
id = postId ?: "",
body = mapOf("title" to "Updated Title")
)
println("Updated: ${updated["title"]?.jsonPrimitive?.contentOrNull}")
// List with filter
val results = pb.collection("posts").getList(
page = 1,
perPage = 10,
filter = "status = 'published'",
sort = "-created"
)
println("Found ${results.totalItems} published posts")
// Delete
pb.collection("posts").delete(postId ?: "")
println("Deleted post")
} catch (e: ClientResponseError) {
println("Error: ${e.status} - ${e.response}")
}
}
Search with Pagination
fun searchPosts(pb: BosBase, query: String, page: Int = 1): ResultList {
return pb.collection("posts").getList(
page = page,
perPage = 20,
filter = pb.filter("title ~ {:query} || content ~ {:query}", mapOf("query" to query)),
sort = "-created"
)
}
// Usage
val results = searchPosts(pb, "kotlin", page = 1)
results.items.forEach { post ->
println(post["title"]?.jsonPrimitive?.contentOrNull)
}
File Upload Example
import com.bosbase.sdk.FileAttachment
import java.io.File
fun uploadPostWithImage(pb: BosBase, title: String, content: String, imagePath: String) {
val imageFile = File(imagePath)
val attachment = FileAttachment.fromFile(imageFile)
val post = pb.collection("posts").create(
body = mapOf(
"title" to title,
"content" to content
),
files = mapOf(
"image" to listOf(attachment)
)
)
println("Post created with image: ${post["id"]?.jsonPrimitive?.contentOrNull}")
}
Working with Relations
// Get post with author and categories
val post = pb.collection("posts").getOne(
id = "POST_ID",
expand = "author,categories"
)
// Access expanded author
val author = post["expand"]?.jsonObject?.get("author")?.jsonObject
val authorName = author?.get("name")?.jsonPrimitive?.contentOrNull
val authorEmail = author?.get("email")?.jsonPrimitive?.contentOrNull
// Access expanded categories (array)
val categories = post["expand"]?.jsonObject?.get("categories")?.jsonArray
categories?.forEach { category ->
val name = category.jsonObject["name"]?.jsonPrimitive?.contentOrNull
println("Category: $name")
}