Schema Query API - Swift SDK Documentation
The Schema Query API provides lightweight interfaces to retrieve collection field information without fetching full collection schemas.
Table of Contents
- Overview
- Get Collection Schema
- Get All Collection Schemas
- Type Definitions
- Use Cases
- Performance Considerations
Overview
The Schema Query API allows you to query collection field information efficiently. This is particularly useful for:
- AI systems that need to understand data structure
- Code generation tools
- Dynamic form builders
- API documentation generators
Unlike the full Collection API, the Schema Query API returns only essential field information, making it faster and more efficient.
Get Collection Schema
Get the schema for a specific collection:
import BosBase
let client = try BosBaseClient(baseURLString: "http://127.0.0.1:8090")
// Get schema for a specific collection
let schema: JSONRecord = try await client.collections.getOne("posts")
if let fields = schema["schema"]?.value as? [JSONRecord] {
for field in fields {
if let name = field["name"]?.value as? String,
let type = field["type"]?.value as? String {
print("\(name): \(type)")
}
}
}
Response Structure:
// Each field contains:
{
"id": "field-id",
"name": "field-name",
"type": "field-type",
"required": true/false,
"presentable": true/false,
"unique": true/false,
"system": true/false,
"options": { /* type-specific options */ }
}
Example:
let collection: JSONRecord = try await client.collections.getOne("posts")
if let fields = collection["schema"]?.value as? [JSONRecord] {
for field in fields {
if let name = field["name"]?.value as? String,
let type = field["type"]?.value as? String,
let required = field["required"]?.value as? Bool {
print("Field: \(name)")
print(" Type: \(type)")
print(" Required: \(required)")
// Check field options
if let options = field["options"]?.value as? JSONRecord {
print(" Options: \(options)")
}
}
}
}
Get All Collection Schemas
Get schemas for all collections:
let collections: ListResult<JSONRecord> = try await client.collections.getList(page: 1, perPage: 200)
if let items = collections.items {
for collection in items {
if let name = collection["name"]?.value as? String,
let type = collection["type"]?.value as? String,
let fields = collection["schema"]?.value as? [JSONRecord] {
print("Collection: \(name) (\(type))")
print(" Fields: \(fields.count)")
for field in fields {
if let fieldName = field["name"]?.value as? String,
let fieldType = field["type"]?.value as? String {
print(" - \(fieldName): \(fieldType)")
}
}
}
}
}
Or get all collections at once:
let allCollections: [JSONRecord] = try await client.collections.getFullList()
for collection in allCollections {
if let name = collection["name"]?.value as? String,
let fields = collection["schema"]?.value as? [JSONRecord] {
print("\(name): \(fields.count) fields")
}
}
Type Definitions
Field Types
Common field types and their structure:
Text Fields
{
"name": "title",
"type": "text",
"required": true,
"options": {
"min": 0,
"max": 255,
"pattern": ""
}
}
Number Fields
{
"name": "price",
"type": "number",
"required": false,
"options": {
"min": 0,
"max": 1000,
"noDecimal": false
}
}
Boolean Fields
{
"name": "published",
"type": "bool",
"required": false,
"options": {}
}
Date Fields
{
"name": "created",
"type": "date",
"required": false,
"options": {
"min": "",
"max": ""
}
}
Select Fields
{
"name": "status",
"type": "select",
"required": true,
"options": {
"maxSelect": 1,
"values": ["draft", "published", "archived"]
}
}
Relation Fields
{
"name": "author",
"type": "relation",
"required": false,
"options": {
"collectionId": "users",
"cascadeDelete": true,
"maxSelect": 1,
"displayFields": ["name", "email"]
}
}
File Fields
{
"name": "image",
"type": "file",
"required": false,
"options": {
"maxSelect": 1,
"maxSize": 5242880,
"mimeTypes": ["image/jpeg", "image/png"]
}
}
Use Cases
AI Systems
AI systems can use schema information to understand data structure and generate appropriate queries:
func getCollectionInfo(collectionName: String) async throws -> [String: Any] {
let collection: JSONRecord = try await client.collections.getOne(collectionName)
var info: [String: Any] = [:]
if let name = collection["name"]?.value as? String {
info["name"] = name
}
if let type = collection["type"]?.value as? String {
info["type"] = type
}
if let fields = collection["schema"]?.value as? [JSONRecord] {
var fieldInfo: [[String: Any]] = []
for field in fields {
var fieldData: [String: Any] = [:]
if let name = field["name"]?.value as? String {
fieldData["name"] = name
}
if let type = field["type"]?.value as? String {
fieldData["type"] = type
}
if let required = field["required"]?.value as? Bool {
fieldData["required"] = required
}
fieldInfo.append(fieldData)
}
info["fields"] = fieldInfo
}
return info
}
// Use in AI system
let postsInfo = try await getCollectionInfo(collectionName: "posts")
print("Posts collection has \(postsInfo["fields"] as? [[String: Any]] ?? []).count) fields")
Code Generation
Generate type-safe models from schema:
func generateSwiftModel(collectionName: String) async throws -> String {
let collection: JSONRecord = try await client.collections.getOne(collectionName)
var model = "struct \(collectionName.capitalized): Codable {\n"
if let fields = collection["schema"]?.value as? [JSONRecord] {
for field in fields {
if let name = field["name"]?.value as? String,
let type = field["type"]?.value as? String {
let swiftType = mapFieldTypeToSwift(type)
let optional = (field["required"]?.value as? Bool) == false ? "?" : ""
model += " let \(name): \(swiftType)\(optional)\n"
}
}
}
model += "}\n"
return model
}
func mapFieldTypeToSwift(_ type: String) -> String {
switch type {
case "text", "email", "url", "editor": return "String"
case "number": return "Double"
case "bool": return "Bool"
case "date": return "Date"
case "select": return "String"
case "relation": return "String"
case "file": return "String"
default: return "Any"
}
}
Dynamic Form Builders
Build forms dynamically based on schema:
func buildFormFields(collectionName: String) async throws -> [[String: Any]] {
let collection: JSONRecord = try await client.collections.getOne(collectionName)
var formFields: [[String: Any]] = []
if let fields = collection["schema"]?.value as? [JSONRecord] {
for field in fields {
if let name = field["name"]?.value as? String,
let type = field["type"]?.value as? String,
let required = field["required"]?.value as? Bool {
var formField: [String: Any] = [
"name": name,
"type": type,
"required": required
]
// Add type-specific options
if let options = field["options"]?.value as? JSONRecord {
formField["options"] = options
}
formFields.append(formField)
}
}
}
return formFields
}
API Documentation
Generate API documentation from schemas:
func generateAPIDocs() async throws -> String {
let collections: [JSONRecord] = try await client.collections.getFullList()
var docs = "# API Documentation\n\n"
for collection in collections {
if let name = collection["name"]?.value as? String,
let type = collection["type"]?.value as? String {
docs += "## \(name) Collection (\(type))\n\n"
if let fields = collection["schema"]?.value as? [JSONRecord] {
docs += "### Fields\n\n"
docs += "| Name | Type | Required |\n"
docs += "|------|------|----------|\n"
for field in fields {
if let fieldName = field["name"]?.value as? String,
let fieldType = field["type"]?.value as? String,
let required = field["required"]?.value as? Bool {
docs += "| \(fieldName) | \(fieldType) | \(required ? "Yes" : "No") |\n"
}
}
docs += "\n"
}
}
}
return docs
}
Performance Considerations
Caching
Cache schema information to avoid repeated API calls:
class SchemaCache {
private var cache: [String: JSONRecord] = [:]
private let client: BosBaseClient
init(client: BosBaseClient) {
self.client = client
}
func getSchema(collectionName: String) async throws -> JSONRecord {
if let cached = cache[collectionName] {
return cached
}
let schema: JSONRecord = try await client.collections.getOne(collectionName)
cache[collectionName] = schema
return schema
}
func invalidate(collectionName: String) {
cache.removeValue(forKey: collectionName)
}
func invalidateAll() {
cache.removeAll()
}
}
// Usage
let schemaCache = SchemaCache(client: client)
let schema = try await schemaCache.getSchema(collectionName: "posts")
Batch Schema Queries
When querying multiple collections, use batch operations:
func getAllSchemas(collectionNames: [String]) async throws -> [String: JSONRecord] {
let batch = client.createBatch()
var requests: [String] = []
for name in collectionNames {
_ = batch.collections.getOne(name)
requests.append(name)
}
let results = try await batch.submit()
var schemas: [String: JSONRecord] = [:]
for (index, result) in results.enumerated() {
if let schema = result as? JSONRecord {
schemas[requests[index]] = schema
}
}
return schemas
}
Selective Field Queries
Only query the fields you need:
// Get only field names and types
let collection: JSONRecord = try await client.collections.getOne("posts")
if let fields = collection["schema"]?.value as? [JSONRecord] {
let fieldInfo = fields.compactMap { field -> (String, String)? in
guard let name = field["name"]?.value as? String,
let type = field["type"]?.value as? String else {
return nil
}
return (name, type)
}
print("Fields: \(fieldInfo)")
}
Complete Example: Schema Analyzer
import BosBase
class SchemaAnalyzer {
let client: BosBaseClient
init(client: BosBaseClient) {
self.client = client
}
func analyzeCollection(_ collectionName: String) async throws -> [String: Any] {
let collection: JSONRecord = try await client.collections.getOne(collectionName)
var analysis: [String: Any] = [:]
// Basic info
if let name = collection["name"]?.value as? String {
analysis["name"] = name
}
if let type = collection["type"]?.value as? String {
analysis["type"] = type
}
// Field analysis
if let fields = collection["schema"]?.value as? [JSONRecord] {
var fieldStats: [String: Int] = [:]
var requiredCount = 0
var relationCount = 0
for field in fields {
if let fieldType = field["type"]?.value as? String {
fieldStats[fieldType, default: 0] += 1
if fieldType == "relation" {
relationCount += 1
}
}
if field["required"]?.value as? Bool == true {
requiredCount += 1
}
}
analysis["totalFields"] = fields.count
analysis["requiredFields"] = requiredCount
analysis["relationFields"] = relationCount
analysis["fieldTypes"] = fieldStats
}
return analysis
}
func compareCollections(_ names: [String]) async throws -> [[String: Any]] {
var analyses: [[String: Any]] = []
for name in names {
let analysis = try await analyzeCollection(name)
analyses.append(analysis)
}
return analyses
}
}
// Usage
let client = try BosBaseClient(baseURLString: "http://127.0.0.1:8090")
let analyzer = SchemaAnalyzer(client: client)
let analysis = try await analyzer.analyzeCollection("posts")
print("Collection analysis: \(analysis)")
let comparisons = try await analyzer.compareCollections(["posts", "users", "comments"])
for comp in comparisons {
print("\(comp["name"] ?? ""): \(comp["totalFields"] ?? 0) fields")
}
Error Handling
Always handle errors when querying schemas:
do {
let schema: JSONRecord = try await client.collections.getOne("posts")
// Process schema
} catch let error as ClientResponseError {
if error.status == 404 {
print("Collection not found")
} else if error.status == 403 {
print("Access denied")
} else {
print("Error: \(error.response ?? [:])")
}
} catch {
print("Unexpected error: \(error)")
}
For more information, see:
- Collections API - Full collection management
- AI Development Guide - Using schemas in AI systems