Collections & Schemas
Collections are containers for your data, similar to tables in traditional databases. Each collection can have an optional schema that enforces data structure, types, and validation rules.
- Collection - The data container itself
- Schema - The validation rules and structure definition for that collection
Create Collection
Create a new collection with optional schema validation.
Creating collections requires admin permissions.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
use std::collections::HashMap;
client.create_collection(
"users",
Some(Schema {
fields: [
("name".to_string(), FieldSchema { field_type: "String", required: true, ..Default::default() }),
("email".to_string(), FieldSchema { field_type: "String", required: true, ..Default::default() }),
("age".to_string(), FieldSchema { field_type: "Number", required: false, ..Default::default() }),
].into_iter().collect(),
})
).await?;
client.create_collection('users', {
'fields': {
'name': {'field_type': 'String', 'required': True},
'email': {'field_type': 'String', 'required': True},
'age': {'field_type': 'Number'}
}
})
await client.createCollection("users", {
fields: {
name: { field_type: "String", required: true },
email: { field_type: "String", required: true },
age: { field_type: "Number" },
},
});
await client.createCollection("users", {
fields: {
name: { field_type: "String", required: true },
email: { field_type: "String", required: true },
age: { field_type: "Number" },
},
});
client.createCollection("users", mapOf(
"fields" to mapOf(
"name" to mapOf("type" to "String", "required" to true),
"email" to mapOf("type" to "String", "required" to true),
"age" to mapOf("type" to "Number")
)
))
client.CreateCollection("users", map[string]interface{}{
"fields": map[string]interface{}{
"name": map[string]interface{}{"field_type": "String", "required": true},
"email": map[string]interface{}{"field_type": "String", "required": true},
"age": map[string]interface{}{"field_type": "Number"},
},
})
curl -X POST https://{EKODB_API_URL}/api/collections/users \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"fields": {
"name": {
"field_type": "String",
"required": true
},
"email": {
"field_type": "String",
"required": true
},
"age": {
"field_type": "Number"
}
}
}'
# Response
{
"status": "success",
"message": "Collection created successfully"
}
Get Collection Info
Retrieve metadata about a collection.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
let info = client.get_collection_info("users").await?;
println!("Records: {}", info.record_count);
println!("Schema: {:?}", info.schema);
info = await client.get_collection_info('users')
print(info['record_count']) # 1234
print(info['schema'])
const info = await client.getCollectionInfo("users");
console.log(info.record_count); // 1234
console.log(info.schema);
const info = await client.getCollectionInfo("users");
console.log(info.record_count); // 1234
console.log(info.schema);
val info = client.getCollectionInfo("users")
println("Records: ${info.recordCount}")
println("Schema: ${info.schema}")
info, err := client.GetCollectionInfo("users")
fmt.Println("Records:", info.RecordCount)
fmt.Println("Schema:", info.Schema)
curl https://{EKODB_API_URL}/api/collections/users \
-H "Authorization: Bearer {TOKEN}"
# Response
{
"name": "users",
"record_count": 1234,
"created_at": "2026-01-10T08:00:00Z",
"schema": {
"fields": {
"name": {"field_type": "String", "required": true},
"email": {"field_type": "String", "required": true}
}
}
}
List All Collections
Get a list of all collections in the database.
Listing all collections requires admin permissions.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
let collections = client.list_collections().await?;
println!("{:?}", collections);
collections = client.list_collections()
print(collections)
const collections = await client.listCollections();
console.log(collections);
const collections = await client.listCollections();
console.log(collections);
val collections = client.listCollections()
println(collections)
collections, err := client.ListCollections()
fmt.Println(collections)
curl https://{EKODB_API_URL}/api/collections \
-H "Authorization: Bearer {ADMIN_TOKEN}"
# Response
{
"collections": [
"users",
"products",
"orders"
]
}
Delete Collection
Delete an entire collection and all its records permanently.
This operation requires Admin access and is irreversible. Use with extreme caution.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
client.delete_collection("users").await?;
await client.delete_collection('users')
await client.deleteCollection("users");
await client.deleteCollection("users");
client.deleteCollection("users")
err := client.DeleteCollection("users")
curl -X DELETE https://{EKODB_API_URL}/api/collections/{collection} \
-H "Authorization: Bearer {ADMIN_TOKEN}"
# Response
{
"status": "success",
"message": "Collection deleted successfully"
}
Restore Record from Trash
Restore a previously deleted record from trash.
Restoring records from trash requires admin permissions.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
client.restore_from_trash("users", &record_id).await?;
client.restore_from_trash('users', record_id)
await client.restoreFromTrash("users", recordId);
await client.restoreFromTrash("users", recordId);
client.restoreFromTrash("users", recordId)
err := client.RestoreFromTrash("users", recordId)
curl -X POST https://{EKODB_API_URL}/api/trash/{collection}/{record_id} \
-H "Authorization: Bearer {ADMIN_TOKEN}"
# Response
{
"status": "restored",
"id": "record_id_123",
"collection": "users"
}
Restore Collection from Trash
Restore all deleted records in a collection from trash.
Restoring collections from trash requires admin permissions.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
let result = client.restore_collection_from_trash("users").await?;
println!("Restored: {} records", result.records_restored);
result = client.restore_collection_from_trash('users')
print(result['records_restored'])
const result = await client.restoreCollectionFromTrash("users");
console.log(result.records_restored);
const result = await client.restoreCollectionFromTrash("users");
console.log(result.records_restored);
val result = client.restoreCollectionFromTrash("users")
println("Restored: ${result.recordsRestored} records")
result, err := client.RestoreCollectionFromTrash("users")
fmt.Printf("Restored: %d records\n", result.RecordsRestored)
curl -X POST https://{EKODB_API_URL}/api/trash/{collection} \
-H "Authorization: Bearer {ADMIN_TOKEN}"
# Response
{
"status": "restored",
"collection": "users",
"records_restored": 42
}
Deleted records are kept in trash for 30 days. After this period, they are permanently deleted and cannot be restored.
Get Schema
Retrieve the current schema for a collection.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
let schema = client.get_schema("users").await?;
println!("{:?}", schema.fields);
schema = await client.get_schema('users')
print(schema['fields'])
const schema = await client.getSchema("users");
console.log(schema.fields);
const schema = await client.getSchema("users");
console.log(schema.fields);
val schema = client.getSchema("users")
println(schema.fields)
schema, err := client.GetSchema("users")
fmt.Println(schema.Fields)
curl https://{EKODB_API_URL}/api/schemas/{collection} \
-H "Authorization: Bearer {ADMIN_TOKEN}"
# Response
{
"fields": {
"name": {
"field_type": "String",
"required": true,
"min": 2
},
"email": {
"field_type": "String",
"required": true
},
"age": {
"field_type": "Integer",
"min": 0,
"max": 150
}
}
}
Update Schema Constraints
Apply a partial update to one or more fields' schema constraints on an existing collection. Only the attributes you set are changed — anything left unset keeps its current value; this does not replace the schema wholesale (see Create Collection above for that).
Updating schema constraints requires admin permissions.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
use std::collections::HashMap;
let mut constraints = HashMap::new();
constraints.insert(
"email".to_string(),
SchemaConstraintUpdate {
required: Some(true),
..Default::default()
},
);
constraints.insert(
"age".to_string(),
SchemaConstraintUpdate {
field_type: Some("Integer".to_string()),
min: Some(18.0),
max: Some(100.0),
..Default::default()
},
);
client.update_schema_constraints("users", constraints).await?;
Updating schema constraints is not yet available in the Python client. Use the Direct API tab below.
Updating schema constraints is not yet available in the TypeScript client. Use the Direct API tab below.
Updating schema constraints is not yet available in the JavaScript client. Use the Direct API tab below.
Updating schema constraints is not yet available in the Kotlin client. Use the Direct API tab below.
required := true
fieldType := "Integer"
minAge := 18.0
maxAge := 100.0
constraints := map[string]SchemaConstraintUpdate{
"email": {
Required: &required,
},
"age": {
FieldType: &fieldType,
Min: &minAge,
Max: &maxAge,
},
}
client.UpdateSchemaConstraints("users", constraints)
curl -X PUT https://{EKODB_API_URL}/api/schemas/{collection} \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"constraints": {
"email": {
"required": true
},
"age": {
"field_type": "Integer",
"min": 18,
"max": 100
}
}
}'
# Response
{
"status": "success",
"message": "Schema constraints updated successfully"
}
Schema updates only affect new records. Existing records are not automatically migrated or validated against the new schema.
Set Primary Key Alias
Configure a custom field to use as the primary key instead of the default id.
Setting a primary key alias requires admin permissions.
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
client.set_primary_key_alias("products", "sku").await?;
await client.set_primary_key_alias('products', 'sku')
await client.setPrimaryKeyAlias("products", "sku");
await client.setPrimaryKeyAlias("products", "sku");
client.setPrimaryKeyAlias("products", "sku")
err := client.SetPrimaryKeyAlias("products", "sku")
curl -X PUT https://{EKODB_API_URL}/api/schemas/{collection}/primary-key-alias \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"alias": "sku"
}'
# Response
{
"message": "Primary key alias set successfully",
"alias": "sku"
}
Example - Using Custom Primary Key:
- Client Libraries (Recommended)
- Direct API
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
use ekodb_client::Record;
// Insert with custom primary key
let mut user = Record::new();
user.insert("user_id", "custom_user_001");
user.insert("name", "John Doe");
user.insert("email", "john@example.com");
let result = client.insert("users", user, None).await?;
// Retrieve using custom primary key
let user = client.find_by_id("users", "custom_user_001").await?;
println!("Found user: {:?}", user);
# Insert with custom primary key
user = {
"user_id": "custom_user_001",
"name": "John Doe",
"email": "john@example.com"
}
result = await client.insert("users", user)
# Retrieve using custom primary key
user = await client.find_by_id("users", "custom_user_001")
print(f"Found user: {user}")
// Insert with custom primary key
const user = {
user_id: "custom_user_001",
name: "John Doe",
email: "john@example.com",
};
const result = await client.insert("users", user);
// Retrieve using custom primary key
const found = await client.findById("users", "custom_user_001");
console.log("Found user:", found);
// Insert with custom primary key
const user = {
user_id: "custom_user_001",
name: "John Doe",
email: "john@example.com",
};
const result = await client.insert("users", user);
// Retrieve using custom primary key
const found = await client.findById("users", "custom_user_001");
console.log("Found user:", found);
import io.ekodb.client.types.Record
// Insert with custom primary key
val user = Record.new()
.insert("user_id", "custom_user_001")
.insert("name", "John Doe")
.insert("email", "john@example.com")
val result = client.insert("users", user)
// Retrieve using custom primary key
val found = client.findById("users", "custom_user_001")
println("Found user: $found")
// Insert with custom primary key
user := map[string]interface{}{
"user_id": "custom_user_001",
"name": "John Doe",
"email": "john@example.com",
}
result, err := client.Insert("users", user)
if err != nil {
log.Fatal(err)
}
// Retrieve using custom primary key
found, err := client.FindByID("users", "custom_user_001")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found user: %+v\n", found)
# Insert with custom primary key
curl -X POST https://{EKODB_API_URL}/api/insert/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {YOUR_API_TOKEN}" \
-d '{
"user_id": "custom_user_001",
"name": "John Doe",
"email": "john@example.com"
}'
# Retrieve using custom primary key
curl -X GET https://{EKODB_API_URL}/api/find/users/custom_user_001 \
-H "Authorization: Bearer {YOUR_API_TOKEN}"
Schema Field Types
field_typeEvery field definition must carry a field_type key, and it is required — a field definition without it is rejected (missing field 'field_type'). The value is case-sensitive ("String", "Integer", "Vector", …).
Do not confuse this with the type key used in typed value wrappers on records ({ "type": "String", "value": "Alice" }) or in index configs ({ "type": "vector", … }). Those use type; a schema field definition uses field_type.
Supported Data Types
| Type | Description | Validation Options |
|---|---|---|
String | Text values | min, max (length), regex, enums |
Integer | 64-bit signed integers | min, max, enums |
Float | 64-bit floating-point numbers | min, max, enums |
Number | Integers and floats | min, max, enums |
Boolean | True/false values | None |
Array | Lists of values | min, max (length) |
Object | Nested JSON objects | None |
DateTime | RFC 3339 datetime strings | min, max |
Decimal | Arbitrary-precision decimals | min, max |
UUID | RFC 4122 identifiers | None |
Vector | Numeric arrays for embeddings | min, max (length) |
Set | Unique value collections | min, max (length) |
Binary | Base64-encoded binary data | None |
Bytes | Raw byte arrays | None |
Duration | Time span values | None |
Field Constraints
String Constraints:
{
"username": {
"field_type": "String",
"required": true,
"min": 3,
"max": 20,
"regex": "^[a-zA-Z0-9_]+$"
}
}
Number Constraints:
{
"age": {
"field_type": "Number",
"min": 0,
"max": 150
},
"price": {
"field_type": "Number",
"min": 0.01,
"max": 999999.99
}
}
Array Constraints:
{
"tags": {
"field_type": "Array",
"min": 1,
"max": 10
}
}
Enum Validation:
{
"status": {
"field_type": "String",
"enums": ["draft", "published", "archived"],
"default": "draft"
}
}
Object Fields:
{
"address": {
"field_type": "Object",
"required": true
}
}
Complete Example
Here's a complete workflow for creating and managing a collection:
#!/bin/bash
# 1. Create collection with schema
curl -X POST https://{EKODB_API_URL}/api/collections/products \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-d '{
"fields": {
"sku": {
"field_type": "String",
"required": true,
"regex": "^[A-Z0-9-]+$"
},
"name": {
"field_type": "String",
"required": true,
"max": 200
},
"price": {
"field_type": "Number",
"min": 0.01,
"required": true
},
"category": {
"field_type": "String",
"enums": ["electronics", "clothing", "books", "food"]
},
"in_stock": {
"field_type": "Boolean",
"default": true
},
"tags": {
"field_type": "Array",
"max": 5
}
}
}'
# 2. Set custom primary key
curl -X PUT https://{EKODB_API_URL}/api/schemas/products/primary-key-alias \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-d '{
"alias": "sku"
}'
# 3. Insert product (validates against schema)
curl -X POST https://{EKODB_API_URL}/api/insert/products \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {YOUR_API_TOKEN}" \
-d '{
"sku": "ELEC-001",
"name": "Wireless Mouse",
"price": 29.99,
"category": "electronics",
"in_stock": true,
"tags": ["wireless", "bluetooth", "ergonomic"]
}'
# 4. Get collection info
curl -X GET https://{EKODB_API_URL}/api/collections/products \
-H "Authorization: Bearer {YOUR_API_TOKEN}"
# 5. Get current schema
curl -X GET https://{EKODB_API_URL}/api/schemas/products \
-H "Authorization: Bearer {YOUR_API_TOKEN}"
# 6. Update schema constraints
curl -X PUT https://{EKODB_API_URL}/api/schemas/products \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {ADMIN_TOKEN}" \
-d '{
"fields": {
"sku": {
"field_type": "String",
"required": true,
"regex": "^[A-Z0-9-]+$"
},
"name": {
"field_type": "String",
"required": true,
"max": 300
},
"price": {
"field_type": "Number",
"min": 0.01,
"max": 100000
}
}
}'
Best Practices
Schema Design
Start Simple, Evolve Gradually:
# Initial schema - minimal constraints
{
"fields": {
"name": {"field_type": "String", "required": true},
"email": {"field_type": "String", "required": true}
}
}
# Later - add more constraints as requirements clarify
{
"fields": {
"name": {
"field_type": "String",
"required": true,
"min": 2,
"max": 100
},
"email": {
"field_type": "String",
"required": true
}
}
}
Collection Naming
Use Clear, Plural Names:
# Good
/api/collections/users
/api/collections/products
/api/collections/order_items
# Avoid
/api/collections/user
/api/collections/Product
/api/collections/order-item
Schema Validation
Balance Strictness with Flexibility:
{
"fields": {
"core_field": {
"field_type": "String",
"required": true,
"max": 100
},
"optional_field": {
"field_type": "String",
"required": false
}
}
}
Primary Key Strategy
Choose Based on Your Use Case:
| Use Case | Primary Key Strategy |
|---|---|
| Auto-generated IDs | Use default id field |
| External system sync | Set alias to external ID |
| Natural keys (SKU, email) | Set alias to natural key |
| UUIDs | Set alias to UUID field |
Validation Errors
When a record violates the schema, you'll receive detailed error messages:
# Invalid insert attempt
POST /api/insert/users
{
"name": "A", // Too short
"email": "invalid-email", // Invalid format
"age": 200 // Exceeds max
}
# Error response
{
"error": "Schema validation failed",
"details": [
{
"field": "name",
"constraint": "min",
"expected": 2,
"actual": 1,
"message": "Field 'name' must be at least 2 characters"
},
{
"field": "email",
"constraint": "regex",
"message": "Field 'email' does not match required pattern"
},
{
"field": "age",
"constraint": "max",
"expected": 150,
"actual": 200,
"message": "Field 'age' exceeds maximum value of 150"
}
]
}
Related Documentation
- Basic Operations - Insert, query, update, and delete records
- Indexes - Create indexes for query performance
- Authentication - API key and permission management
- Data Types - Detailed data type reference
Example Code
Direct HTTP/REST API Examples
Raw HTTP examples demonstrating the REST API directly:
- JavaScript:
collection_management.js - Python:
collection_management.py - Go:
collection_management.go - Rust:
collection_management.rs
Client Library Examples
Production-ready examples using official client libraries:
- Rust:
client_collection_management.rs - Python:
client_collection_management.py - TypeScript:
client_collection_management.ts - Go:
client_collection_management.go - Kotlin:
ClientCollectionManagement.kt - JavaScript:
client_collection_management.js