Data API
Typed-table CRUD service with dynamic row schemas, full-text search, bulk operations, CSV import/export, and real-time SSE change streams.
Tables
Tables define the schema for your data. Each table has typed columns that are enforced on write. Schemas can be migrated (add/drop columns) without downtime.
| Method | Path | Description |
|---|---|---|
| GET | /v1/tables | List all tables for the org |
| POST | /v1/tables | Create a new table with column definitions |
| DELETE | /v1/tables/{table} | Drop a table and all its data |
| POST | /v1/tables/{table}/migrate | Migrate schema (add/drop columns) |
SDK Examples
// Create a table
const table = await client.data.createTable({
name: 'contacts',
columns: [
{ name: 'email', type: 'text', required: true },
{ name: 'name', type: 'text' },
{ name: 'age', type: 'integer' },
{ name: 'active', type: 'boolean', default: true },
],
});
// List tables
const { data: tables } = await client.data.listTables();
// Add a column
await client.data.migrateTable('contacts', {
add: [{ name: 'company', type: 'text' }],
});
// Drop a table
await client.data.dropTable('contacts');
// Rust SDK
use kapable_sdk::data::types::*;
let table = client.data().create_table(&CreateTableRequest {
name: "contacts".into(),
columns: vec![
ColumnDef { name: "email".into(), col_type: "text".into(), required: Some(true), default: None },
ColumnDef { name: "name".into(), col_type: "text".into(), required: None, default: None },
ColumnDef { name: "age".into(), col_type: "integer".into(), required: None, default: None },
],
}).await?;
let tables = client.data().list_tables().await?;
client.data().migrate_table("contacts", &MigrateTableRequest {
add: Some(vec![ColumnDef { name: "company".into(), col_type: "text".into(), required: None, default: None }]),
drop: None,
}).await?;
client.data().drop_table("contacts").await?;
Row CRUD
Rows are dynamic JSON objects whose shape matches the table schema. The Data service validates column types on write and returns errors for type mismatches.
| Method | Path | Description |
|---|---|---|
| GET | /v1/{table} | List rows with pagination and filters |
| POST | /v1/{table} | Create one or more rows |
| GET | /v1/{table}/{id} | Get a single row by UUID |
| PUT | /v1/{table}/{id} | Replace a row (full update) |
| PATCH | /v1/{table}/{id} | Patch a row (partial update) |
| DELETE | /v1/{table}/{id} | Delete a row |
| POST | /v1/{table}/search | Full-text search across all text columns |
SDK Examples
// Create a single row
const row = await client.data.createRows('contacts', {
email: 'alice@example.com',
name: 'Alice',
age: 30,
});
// Create multiple rows (batch)
const rows = await client.data.createRows('contacts', [
{ email: 'bob@example.com', name: 'Bob', age: 25 },
{ email: 'carol@example.com', name: 'Carol', age: 35 },
]);
// List with pagination
const { data, total } = await client.data.listRows('contacts', {
limit: 20,
offset: 0,
sort: 'name',
order: 'asc',
});
// Get a single row
const contact = await client.data.getRow('contacts', rowId);
// Patch a row (partial update)
await client.data.patchRow('contacts', rowId, {
name: 'Alice Updated',
});
// Full-text search
const results = await client.data.searchRows('contacts', {
query: 'alice',
limit: 10,
});
// Auto-paginate
for await (const row of client.data.paginateRows('contacts')) {
console.log(row.email, row.name);
}
// Rust SDK
use serde_json::json;
let row = client.data().create_rows("contacts", &json!({
"email": "alice@example.com",
"name": "Alice",
"age": 30
})).await?;
let result = client.data().list_rows("contacts", &DataListParams {
limit: Some(20),
offset: Some(0),
sort: Some("name".into()),
order: Some("asc".into()),
..Default::default()
}).await?;
let contact = client.data().get_row("contacts", row_id).await?;
client.data().patch_row("contacts", row_id, &json!({
"name": "Alice Updated"
})).await?;
let search = client.data().search_rows("contacts", &SearchRowsRequest {
query: "alice".into(),
limit: Some(10),
..Default::default()
}).await?;
Bulk Operations
Bulk endpoints accept arrays of IDs or objects and operate in a single database transaction for consistency.
| Method | Path | Description |
|---|---|---|
| POST | /v1/{table}/bulk/update | Update multiple rows by ID |
| POST | /v1/{table}/bulk/delete | Delete multiple rows by ID |
| POST | /v1/{table}/import | Import rows from CSV |
| POST | /v1/{table}/export | Export all rows as JSON (max 10,000) |
SDK Examples
// Bulk update
await client.data.bulkUpdate('contacts', {
rows: [
{ id: 'uuid-1', data: { active: false } },
{ id: 'uuid-2', data: { active: false } },
],
});
// Bulk delete
await client.data.bulkDelete('contacts', {
ids: ['uuid-1', 'uuid-2', 'uuid-3'],
});
// CSV import
const csv = `email,name,age
dave@example.com,Dave,40
eve@example.com,Eve,28`;
const imported = await client.data.importCsv('contacts', csv);
// Export all rows as JSON
const allRows = await client.data.exportData('contacts');
// Rust SDK
client.data().bulk_update("contacts", &BulkUpdateRequest {
rows: vec![
BulkUpdateRow { id: uuid1, data: json!({"active": false}) },
BulkUpdateRow { id: uuid2, data: json!({"active": false}) },
],
}).await?;
client.data().bulk_delete("contacts", &BulkDeleteRequest {
ids: vec![uuid1, uuid2, uuid3],
}).await?;
SSE Change Streams
Subscribe to real-time change events via Server-Sent Events. The stream emits insert, update, and delete events as they happen.
| Method | Path | Description |
|---|---|---|
| GET | /v1/sse | SSE stream for table change notifications (query: tables=a,b) |
Usage
// Subscribe to changes on the "contacts" table
const es = new EventSource(
'https://api.kapable.ai/v1/sse?tables=contacts',
// Add auth header via polyfill or proxy
);
es.addEventListener('insert', (e) => {
const row = JSON.parse(e.data);
console.log('New contact:', row);
});
es.addEventListener('update', (e) => {
const row = JSON.parse(e.data);
console.log('Updated:', row.id);
});
es.addEventListener('delete', (e) => {
const { id } = JSON.parse(e.data);
console.log('Deleted:', id);
});
Supported column types: text, integer,
float, boolean, timestamp,
json, uuid. Each column can be marked
required and given a default value.