
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@tapstack/db
Advanced tools
Official SDK for the Tapstack API. Build applications with a Notion-like database backend featuring objects (models), fields, records, file storage, automations, and more.
npm install @tapstack/db
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: 'john@example.com',
},
});
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)
},
});
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';
},
});
// Set workspace context for all subsequent requests
client.setWorkspaceId('new-workspace-id');
// Get current workspace ID
const workspaceId = client.getWorkspaceId();
Objects define your data schema, similar to tables in a database or models in Notion.
const { objects } = await client.objects.list();
for (const obj of objects) {
console.log(`${obj.icon} ${obj.pluralName} (${obj.slug})`);
}
const contacts = await client.objects.get('contacts');
console.log(contacts.singleName); // "Contact"
const newObject = await client.objects.create({
singleName: 'Contact',
pluralName: 'Contacts',
slug: 'contacts', // Optional, auto-generated if not provided
icon: '👤', // Optional
});
const updated = await client.objects.update('contacts', {
singleName: 'Customer',
pluralName: 'Customers',
icon: '🧑💼',
});
// Soft delete (default)
await client.objects.delete('contacts');
// Hard delete
await client.objects.delete('contacts', false);
Fields define the schema of an object, similar to columns in a database.
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 fieldconst { fields } = await client.fields.list('contacts');
for (const field of fields) {
console.log(`${field.label}: ${field.type}`);
}
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',
},
});
const updated = await client.fields.update('contacts', 'field-id', {
label: 'Email Address',
data: {
required: false,
},
});
await client.fields.delete('contacts', 'field-id');
Records are the data entries within an object.
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);
}
const record = await client.records.get('contacts', 'record-id');
console.log(record.data);
const record = await client.records.create('contacts', {
data: {
name: 'Jane Smith',
email: 'jane@example.com',
status: 'active',
company: 'company-record-id', // Relation field
},
status: 'published', // Optional: 'draft' | 'published'
});
const updated = await client.records.update('contacts', 'record-id', {
data: {
name: 'Jane Doe',
},
});
// Soft delete (default)
await client.records.delete('contacts', 'record-id');
// Hard delete
await client.records.delete('contacts', 'record-id', false);
// Bulk create
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' } },
]);
// Bulk delete
const { deleted } = await client.records.bulkDelete('contacts', [
'record-id-1',
'record-id-2',
]);
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,
});
Tapstack uses a multi-tenant architecture with Organizations containing Workspaces.
// 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: 'newuser@example.com',
role: 'member',
});
// 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}`);
Upload and manage files within your workspace.
// Upload a file
const file = await client.files.upload(fileBlob, {
folderId: 'folder-id', // Optional
});
console.log(`Uploaded: ${file.name} (${file.size} bytes)`);
// List all files
const { files } = await client.files.list();
// List files in a specific folder
const { files } = await client.files.list('folder-id');
// Get a signed download URL
const { url } = await client.files.getDownloadUrl('file-id', 3600); // 1 hour expiry
// 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');
// 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');
Create automated workflows triggered by record events.
const { automations } = await client.automations.list();
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}}"}',
},
},
],
});
record.created - When a new record is createdrecord.updated - When a record is updatedrecord.deleted - When a record is deletedcreate_record - Create a record in another objectupdate_record - Update related recordsdelete_record - Delete related recordswebhook - Call an external webhook// 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');
Manage AI chat conversations for building chatbots and assistants.
const { conversation } = await client.conversations.create({
title: 'Support Chat',
});
const { conversations, total } = await client.conversations.list({
limit: 20,
offset: 0,
});
// 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?',
});
const conversation = await client.conversations.get('conversation-id');
for (const message of conversation.messages) {
console.log(`${message.role}: ${message.content}`);
}
The SDK is fully typed. Import types as needed:
import {
TapstackObject,
TapstackField,
TapstackRecord,
TapstackOrganization,
TapstackWorkspace,
TapstackFile,
FieldType,
RecordStatus,
CreateObjectPayload,
CreateFieldPayload,
CreateRecordPayload,
} from '@tapstack/db';
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
}
}
}
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());
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,
});
MIT
FAQs
Tapstack Database Client - Official SDK for the Tapstack API
The npm package @tapstack/db receives a total of 51 weekly downloads. As such, @tapstack/db popularity was classified as not popular.
We found that @tapstack/db demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.