npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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/db

Quick 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 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',        // 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 expiry

File 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 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

// 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-key
const client = createClient({
  baseUrl: process.env.NEXT_PUBLIC_TAPSTACK_API_URL,
  apiKey: process.env.TAPSTACK_API_KEY,
});

License

MIT