@tapstack/db
Official SDK for the Tapstack API. Build applications with a Notion-like database backend featuring objects (models), fields, records, file storage, automations, and more.
Installation
npm install @tapstack/db
Quick Start
import { createClient } from '@tapstack/db';
const client = createClient({
apiKey: 'your-api-key',
workspaceId: 'your-workspace-id',
});
const { objects } = await client.objects.list();
const record = await client.records.create('contacts', {
data: {
name: 'John Doe',
email: 'john@example.com',
},
});
Configuration
Basic Configuration
import { createClient } from '@tapstack/db';
const client = createClient({
baseUrl: 'https://api.tapstack.com',
apiKey: 'your-api-key',
userToken: 'jwt-token',
workspaceId: 'workspace-uuid',
onAuthError: () => {
},
});
Frontend Configuration (Dynamic Token)
For frontend applications where the access token may change:
const client = createClient({
getUserToken: async () => {
return localStorage.getItem('access_token');
},
workspaceId: 'workspace-uuid',
onAuthError: () => {
window.location.href = '/login';
},
});
Switching Workspaces
client.setWorkspaceId('new-workspace-id');
const workspaceId = client.getWorkspaceId();
Objects (Models)
Objects define your data schema, similar to tables in a database or models in Notion.
List Objects
const { objects } = await client.objects.list();
for (const obj of objects) {
console.log(`${obj.icon} ${obj.pluralName} (${obj.slug})`);
}
Get Object by Slug
const contacts = await client.objects.get('contacts');
console.log(contacts.singleName);
Create Object
const newObject = await client.objects.create({
singleName: 'Contact',
pluralName: 'Contacts',
slug: 'contacts',
icon: '👤',
});
Update Object
const updated = await client.objects.update('contacts', {
singleName: 'Customer',
pluralName: 'Customers',
icon: '🧑💼',
});
Delete Object
await client.objects.delete('contacts');
await client.objects.delete('contacts', false);
Fields
Fields define the schema of an object, similar to columns in a database.
Supported Field Types
text - Plain text
number - Numeric values
boolean - True/false
date - Date only
datetime - Date and time
email - Email address
phone - Phone number
url - URL/link
select / single_select - Single selection dropdown
multi_select - Multiple selection
checkbox - Checkbox
relation - Relationship to another object
currency - Currency values
rating - Star rating
textarea - Long text
json - JSON data
files - File attachments
person - User reference
ai - AI-generated field
List Fields
const { fields } = await client.fields.list('contacts');
for (const field of fields) {
console.log(`${field.label}: ${field.type}`);
}
Create Field
const emailField = await client.fields.create('contacts', {
label: 'Email',
value: 'email',
type: 'email',
data: {
required: true,
placeholder: 'Enter email address',
},
});
const statusField = await client.fields.create('contacts', {
label: 'Status',
value: 'status',
type: 'single_select',
data: {
options: [
{ label: 'Active', value: 'active', color: '#22c55e' },
{ label: 'Inactive', value: 'inactive', color: '#ef4444' },
],
},
});
const companyField = await client.fields.create('contacts', {
label: 'Company',
value: 'company',
type: 'relation',
data: {
relatedObjectId: 'companies-object-uuid',
relationType: 'many-to-one',
},
});
Update Field
const updated = await client.fields.update('contacts', 'field-id', {
label: 'Email Address',
data: {
required: false,
},
});
Delete Field
await client.fields.delete('contacts', 'field-id');
Records
Records are the data entries within an object.
List Records
const response = await client.records.list('contacts', {
page: 1,
pageSize: 50,
sortBy: 'createdAt',
sortOrder: 'desc',
});
console.log(`Total: ${response.total}`);
for (const record of response.items) {
console.log(record.data.name);
}
Get Record
const record = await client.records.get('contacts', 'record-id');
console.log(record.data);
Create Record
const record = await client.records.create('contacts', {
data: {
name: 'Jane Smith',
email: 'jane@example.com',
status: 'active',
company: 'company-record-id',
},
status: 'published',
});
Update Record
const updated = await client.records.update('contacts', 'record-id', {
data: {
name: 'Jane Doe',
},
});
Delete Record
await client.records.delete('contacts', 'record-id');
await client.records.delete('contacts', 'record-id', false);
Bulk Operations
const { records, count } = await client.records.bulkCreate('contacts', [
{ data: { name: 'Contact 1', email: 'contact1@example.com' } },
{ data: { name: 'Contact 2', email: 'contact2@example.com' } },
{ data: { name: 'Contact 3', email: 'contact3@example.com' } },
]);
const { deleted } = await client.records.bulkDelete('contacts', [
'record-id-1',
'record-id-2',
]);
Advanced Queries
const results = await client.records.query('contacts', {
filter: {
and: [
{ field: 'status', op: 'eq', value: 'active' },
{ field: 'createdAt', op: 'gte', value: Date.now() - 86400000 },
],
},
sort: [
{ field: 'name', direction: 'asc' },
],
limit: 100,
});
Organizations & Workspaces
Tapstack uses a multi-tenant architecture with Organizations containing Workspaces.
Organizations
const { organizations } = await client.organizations.list();
const org = await client.organizations.create({
name: 'Acme Corp',
slug: 'acme-corp',
});
const org = await client.organizations.get('org-id');
await client.organizations.update('org-id', {
name: 'Acme Corporation',
});
const { members } = await client.organizations.listMembers('org-id');
await client.organizations.addMember('org-id', {
email: 'newuser@example.com',
role: 'member',
});
Workspaces
const { workspaces } = await client.workspaces.list();
const workspace = await client.workspaces.create('org-id', {
name: 'Production',
slug: 'production',
description: 'Production environment',
});
const ws = await client.workspaces.get('workspace-id');
const ws = await client.workspaces.resolveBySlug('acme-corp', 'production');
const stats = await client.workspaces.getStats('workspace-id');
console.log(`Objects: ${stats.objectCount}, Records: ${stats.recordCount}`);
File Storage
Upload and manage files within your workspace.
Upload Files
const file = await client.files.upload(fileBlob, {
folderId: 'folder-id',
});
console.log(`Uploaded: ${file.name} (${file.size} bytes)`);
List Files
const { files } = await client.files.list();
const { files } = await client.files.list('folder-id');
Download Files
const { url } = await client.files.getDownloadUrl('file-id', 3600);
File Operations
await client.files.rename('file-id', 'new-name.pdf');
await client.files.move('file-id', 'target-folder-id');
await client.files.delete('file-id');
Folders
const { folders } = await client.files.listFolders();
const { folders } = await client.files.listFolders('parent-folder-id');
const folder = await client.files.createFolder('Documents', 'parent-folder-id');
await client.files.renameFolder('folder-id', 'New Name');
await client.files.moveFolder('folder-id', 'new-parent-id');
await client.files.deleteFolder('folder-id');
Automations
Create automated workflows triggered by record events.
List Automations
const { automations } = await client.automations.list();
Create Automation
const automation = await client.automations.create({
name: 'Send welcome email',
description: 'Sends a welcome email when a new contact is created',
triggerObjectId: 'contacts-object-id',
triggerType: 'record.created',
triggerConditions: [
{ field: 'status', operator: 'eq', value: 'active' },
],
actions: [
{
type: 'webhook',
config: {
url: 'https://api.example.com/send-email',
method: 'POST',
headers: { 'Authorization': 'Bearer token' },
body: '{"email": "{{record.email}}"}',
},
},
],
});
Automation Triggers
record.created - When a new record is created
record.updated - When a record is updated
record.deleted - When a record is deleted
Automation Actions
create_record - Create a record in another object
update_record - Update related records
delete_record - Delete related records
webhook - Call an external webhook
Manage Automations
await client.automations.toggle('automation-id');
await client.automations.update('automation-id', {
name: 'Updated name',
isEnabled: true,
});
const result = await client.automations.execute('automation-id', {
});
await client.automations.delete('automation-id');
AI Conversations
Manage AI chat conversations for building chatbots and assistants.
Create Conversation
const { conversation } = await client.conversations.create({
title: 'Support Chat',
});
List Conversations
const { conversations, total } = await client.conversations.list({
limit: 20,
offset: 0,
});
Add Messages
await client.conversations.addMessage('conversation-id', {
role: 'user',
content: 'Hello, I need help!',
});
await client.conversations.addMessage('conversation-id', {
role: 'assistant',
content: 'Hi! How can I assist you today?',
});
Get Conversation with Messages
const conversation = await client.conversations.get('conversation-id');
for (const message of conversation.messages) {
console.log(`${message.role}: ${message.content}`);
}
TypeScript Support
The SDK is fully typed. Import types as needed:
import {
TapstackObject,
TapstackField,
TapstackRecord,
TapstackOrganization,
TapstackWorkspace,
TapstackFile,
FieldType,
RecordStatus,
CreateObjectPayload,
CreateFieldPayload,
CreateRecordPayload,
} from '@tapstack/db';
Error Handling
The SDK throws AdapterError for API errors:
import { AdapterError } from '@tapstack/db';
try {
await client.records.get('contacts', 'invalid-id');
} catch (error) {
if (error instanceof AdapterError) {
console.error(`Error: ${error.message}`);
console.error(`Code: ${error.code}`);
console.error(`Status: ${error.statusCode}`);
if (error.code === 'UNAUTHORIZED') {
}
}
}
Custom Adapters
For advanced use cases, you can create a custom adapter:
import { createCustomClient, IInstanceAdapter } from '@tapstack/db';
class MyCustomAdapter implements IInstanceAdapter {
async request<T>(method: string, path: string, options?: RequestOptions): Promise<T> {
}
async upload<T>(path: string, file: File | Blob, metadata?: Record<string, string>): Promise<T> {
}
setWorkspaceId(id: string | null): void {
}
getWorkspaceId(): string | null {
}
}
const client = createCustomClient(new MyCustomAdapter());
Environment Variables
For Next.js or other frameworks, configure via environment variables:
# .env.local
NEXT_PUBLIC_TAPSTACK_API_URL=https://api.tapstack.com
TAPSTACK_API_KEY=your-api-key
const client = createClient({
baseUrl: process.env.NEXT_PUBLIC_TAPSTACK_API_URL,
apiKey: process.env.TAPSTACK_API_KEY,
});
License
MIT