Workflow
- 1Create and wait for a database
- 2Create a typed table
- 3Insert and filter records
- 4Update with __version
- 5Handle a conflict
Complete example
import { PermuteApiError, PermuteClient } from '@permute/sdk';
const orgClient = new PermuteClient({ apiKey: process.env.PERMUTE_API_KEY! });
const client = orgClient.withWorkspace(process.env.PERMUTE_WORKSPACE_ID!);
// Creation needs an eligible Enterprise plan and workspace database admin access.
let database = await client.databases.create({ name: 'CRM', key: 'crm' });
while (database.status === 'provisioning') {
await new Promise((resolve) => setTimeout(resolve, 2000));
database = await client.databases.get(database.id);
}
if (database.status !== 'ready') throw new Error('Database is not ready');
await client.databases.createTable(database.id, {
name: 'customers',
columns: [
{ name: 'id', type: 'text', nullable: false },
{ name: 'name', type: 'text', nullable: false },
],
primaryKey: ['id'],
});
const insert = {
requestId: 'customer-acme-insert-1',
mutations: [{ type: 'insert' as const, table: 'customers', values: { id: 'acme', name: 'Acme' } }],
};
await client.databases.transact(database.id, insert);
// If the response is uncertain, resend the identical requestId and payload.
await client.databases.transact(database.id, insert);
const filtered = await client.query({
sourceIds: [database.id],
sql: "SELECT id, name FROM app.customers WHERE id = 'acme'",
});
console.log(filtered.rows);
const rows = await client.databases.records(database.id, 'customers', { limit: 25 });
const customer = rows.items.find((row) => row.id === 'acme');
if (!customer || typeof customer.__version !== 'string') throw new Error('Customer not found');
await client.databases.transact(database.id, {
requestId: 'customer-acme-update-1',
mutations: [{
type: 'update', table: 'customers', key: { id: 'acme' },
expectedVersion: customer.__version, values: { name: 'Acme Inc.' },
}],
});
try {
await client.databases.transact(database.id, {
requestId: 'customer-acme-stale-update-1',
mutations: [{
type: 'update', table: 'customers', key: { id: 'acme' },
expectedVersion: customer.__version, values: { name: 'Acme LLC' },
}],
});
} catch (error) {
if (!(error instanceof PermuteApiError) || error.status !== 409) throw error;
const current = await client.databases.records(database.id, 'customers');
console.log('Record changed; review before retrying:', current.items);
}Write rules
Run this example once with a fresh database key. Table creation and insertion are one-time operations. Use a new requestId for each new mutation; reuse it only when retrying the same payload after an uncertain response.
Record reads return __version for managed tables. An update or delete needs the complete primary key and the version just read. A 409 means the record changed or the request ID was reused with different data (read current state and decide what to write).
Filtered reads use client.query with read-only SQL against the database. client.databases.records provides bounded current-state pages but does not accept a filter. Pagination is not a stable snapshot.