@tapstack/db
v3.0.0
Published
Tapstack Database Client - Official SDK for the Tapstack API
Downloads
98
Readme
@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/dbQuick Start
import { createClient } from '@tapstack/db';
// Create a client with API key authentication
const client = createClient({
apiKey: 'your-api-key',
workspaceId: 'your-workspace-id',
});
// List all objects in the workspace
const { objects } = await client.objects.list();
// Create a record
const record = await client.records.create('contacts', {
data: {
name: 'John Doe',
email: '[email protected]',
},
});Configuration
Basic Configuration
import { createClient } from '@tapstack/db';
const client = createClient({
// API URL (optional, defaults to https://api.tapstack.com)
baseUrl: 'https://api.tapstack.com',
// Authentication (choose one)
apiKey: 'your-api-key', // For server-side/programmatic access
userToken: 'jwt-token', // For authenticated user sessions
// Workspace context
workspaceId: 'workspace-uuid',
// Error handling
onAuthError: () => {
// Handle authentication errors (e.g., redirect to login)
},
});Frontend Configuration (Dynamic Token)
For frontend applications where the access token may change:
const client = createClient({
getUserToken: async () => {
// Return the current access token from your auth state
return localStorage.getItem('access_token');
},
workspaceId: 'workspace-uuid',
onAuthError: () => {
// Redirect to login
window.location.href = '/login';
},
});Switching Workspaces
// Set workspace context for all subsequent requests
client.setWorkspaceId('new-workspace-id');
// Get current 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); // "Contact"Create Object
const newObject = await client.objects.create({
singleName: 'Contact',
pluralName: 'Contacts',
slug: 'contacts', // Optional, auto-generated if not provided
icon: '👤', // Optional
});Update Object
const updated = await client.objects.update('contacts', {
singleName: 'Customer',
pluralName: 'Customers',
icon: '🧑💼',
});Delete Object
// Soft delete (default)
await client.objects.delete('contacts');
// Hard delete
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 textnumber- Numeric valuesboolean- True/falsedate- Date onlydatetime- Date and timeemail- Email addressphone- Phone numberurl- URL/linkselect/single_select- Single selection dropdownmulti_select- Multiple selectioncheckbox- Checkboxrelation- Relationship to another objectcurrency- Currency valuesrating- Star ratingtextarea- Long textjson- JSON datafiles- File attachmentsperson- User referenceai- 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', // Field key (used in record data)
type: 'email',
data: {
required: true,
placeholder: 'Enter email address',
},
});
// Create a select field with options
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' },
],
},
});
// Create a relation field
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: '[email protected]',
status: 'active',
company: 'company-record-id', // Relation field
},
status: 'published', // Optional: 'draft' | 'published'
});Update Record
const updated = await client.records.update('contacts', 'record-id', {
data: {
name: 'Jane Doe',
},
});Delete Record
// Soft delete (default)
await client.records.delete('contacts', 'record-id');
// Hard delete
await client.records.delete('contacts', 'record-id', false);Bulk Operations
// Bulk create
const { records, count } = await client.records.bulkCreate('contacts', [
{ data: { name: 'Contact 1', email: '[email protected]' } },
{ data: { name: 'Contact 2', email: '[email protected]' } },
{ data: { name: 'Contact 3', email: '[email protected]' } },
]);
// Bulk delete
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
// List organizations the user belongs to
const { organizations } = await client.organizations.list();
// Create an organization
const org = await client.organizations.create({
name: 'Acme Corp',
slug: 'acme-corp',
});
// Get organization details
const org = await client.organizations.get('org-id');
// Update organization
await client.organizations.update('org-id', {
name: 'Acme Corporation',
});
// List organization members
const { members } = await client.organizations.listMembers('org-id');
// Add a member
await client.organizations.addMember('org-id', {
email: '[email protected]',
role: 'member',
});Workspaces
// List all workspaces
const { workspaces } = await client.workspaces.list();
// Create a workspace in an organization
const workspace = await client.workspaces.create('org-id', {
name: 'Production',
slug: 'production',
description: 'Production environment',
});
// Get workspace by ID
const ws = await client.workspaces.get('workspace-id');
// Resolve workspace by slugs (useful for routing)
const ws = await client.workspaces.resolveBySlug('acme-corp', 'production');
// Get workspace statistics
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
// Upload a file
const file = await client.files.upload(fileBlob, {
folderId: 'folder-id', // Optional
});
console.log(`Uploaded: ${file.name} (${file.size} bytes)`);List Files
// List all files
const { files } = await client.files.list();
// List files in a specific folder
const { files } = await client.files.list('folder-id');Download Files
// Get a signed download URL
const { url } = await client.files.getDownloadUrl('file-id', 3600); // 1 hour expiryFile Operations
// Rename a file
await client.files.rename('file-id', 'new-name.pdf');
// Move to another folder
await client.files.move('file-id', 'target-folder-id');
// Delete a file
await client.files.delete('file-id');Folders
// List folders
const { folders } = await client.files.listFolders();
// List subfolders
const { folders } = await client.files.listFolders('parent-folder-id');
// Create a folder
const folder = await client.files.createFolder('Documents', 'parent-folder-id');
// Rename folder
await client.files.renameFolder('folder-id', 'New Name');
// Move folder
await client.files.moveFolder('folder-id', 'new-parent-id');
// Delete folder (and all contents)
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 createdrecord.updated- When a record is updatedrecord.deleted- When a record is deleted
Automation Actions
create_record- Create a record in another objectupdate_record- Update related recordsdelete_record- Delete related recordswebhook- Call an external webhook
Manage Automations
// Toggle enabled/disabled
await client.automations.toggle('automation-id');
// Update automation
await client.automations.update('automation-id', {
name: 'Updated name',
isEnabled: true,
});
// Test execute (for debugging)
const result = await client.automations.execute('automation-id', {
// Test data matching trigger schema
});
// Delete
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
// Add user message
await client.conversations.addMessage('conversation-id', {
role: 'user',
content: 'Hello, I need help!',
});
// Add assistant response
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') {
// Handle auth error
}
}
}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> {
// Your implementation
}
async upload<T>(path: string, file: File | Blob, metadata?: Record<string, string>): Promise<T> {
// Your implementation
}
setWorkspaceId(id: string | null): void {
// Your implementation
}
getWorkspaceId(): string | null {
// Your implementation
}
}
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-keyconst client = createClient({
baseUrl: process.env.NEXT_PUBLIC_TAPSTACK_API_URL,
apiKey: process.env.TAPSTACK_API_KEY,
});License
MIT
