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 🙏

© 2025 – Pkg Stats / Ryan Hefner

quick-builder-admin

v0.1.1

Published

Server-side Admin SDK for Quick Builder - manage workspace resources programmatically

Readme

Quick Builder Admin SDK

Server-side SDK for managing Quick Builder workspace resources programmatically.

Warning: This SDK is for server-side use only. Do not use it in browser environments as it exposes your private API key.

Installation

npm install quick-builder-admin
# or
yarn add quick-builder-admin
# or
pnpm add quick-builder-admin

Quick Start

import { createAdminClient } from 'quick-builder-admin';

const admin = createAdminClient({
  privateKey: process.env.QUICK_BUILDER_PRIVATE_KEY!,
  apiHost: 'https://your-quickbuilder-instance.com', // optional
});

// Verify connection
const workspace = await admin.workspace.get();
console.log(`Connected to workspace: ${workspace.name}`);

Configuration

| Option | Type | Default | Description | |--------|------|---------|-------------| | privateKey | string | required | Your private API key (starts with sk_live_) | | apiHost | string | https://api.quickbuilder.dev | API host URL | | timeout | number | 30000 | Request timeout in milliseconds | | retries | number | 3 | Number of retries on failure |

Resources

Workspace

Get and update workspace settings.

// Get workspace details
const workspace = await admin.workspace.get();

// Update workspace
const updated = await admin.workspace.update({
  name: 'My Updated Workspace',
  description: 'A description',
  color: '#3B82F6',
  settings: {
    defaultLocale: 'en',
    locales: ['en', 'es', 'fr'],
  },
});

Models

Manage data models (schemas) in your workspace.

// List all models
const models = await admin.models.list();

// List models by type
const dataModels = await admin.models.list({ type: 'DATA' });

// Get a single model
const model = await admin.models.get('model_id');

// Create a model
const blogPost = await admin.models.create({
  name: 'BlogPost',
  type: 'DATA',
  description: 'Blog post content',
  fields: [
    { name: 'title', type: 'string', required: true },
    { name: 'content', type: 'richText' },
    { name: 'author', type: 'reference', model: 'Author' },
    { name: 'tags', type: 'list', subFields: [{ name: 'tag', type: 'string' }] },
    { name: 'publishedAt', type: 'datetime' },
  ],
});

// Update a model
await admin.models.update('model_id', {
  description: 'Updated description',
  fields: [...], // New field definitions
});

// Delete a model (cascades to entries)
await admin.models.delete('model_id');

Field Types

| Type | Description | |------|-------------| | string | Text field | | number | Numeric field | | boolean | True/false field | | date | Date only | | datetime | Date and time | | richText | Rich text editor | | file | File upload | | reference | Reference to another model | | list | Array of items | | object | Nested object | | color | Color picker | | url | URL field | | email | Email field | | json | Raw JSON |

Entries (Write API)

Create, update, and delete content entries.

// List entries for a model
const posts = await admin.entries.list('BlogPost', {
  status: 'published',
  limit: 10,
  offset: 0,
  orderBy: 'createdAt',
  order: 'desc',
});

// Get a single entry
const post = await admin.entries.get('BlogPost', 'entry_id');

// Create an entry
const newPost = await admin.entries.create('BlogPost', {
  data: {
    title: 'Hello World',
    content: '<p>My first post!</p>',
    tags: [{ tag: 'welcome' }, { tag: 'intro' }],
  },
  status: 'draft', // or 'published'
  slug: 'hello-world',
});

// Update an entry (partial update)
await admin.entries.update('BlogPost', 'entry_id', {
  data: { title: 'Updated Title' },
});

// Replace an entry (full replacement)
await admin.entries.replace('BlogPost', 'entry_id', {
  data: {
    title: 'Completely New Content',
    content: '<p>Everything replaced</p>',
  },
});

// Publish/unpublish
await admin.entries.publish('BlogPost', 'entry_id');
await admin.entries.unpublish('BlogPost', 'entry_id');

// Delete an entry
await admin.entries.delete('BlogPost', 'entry_id');

Members

Manage workspace members and their roles.

// List members
const members = await admin.members.list();

// Get a single member
const member = await admin.members.get('member_id');

// Update member role
await admin.members.updateRole('member_id', 'editor');
// Roles: 'admin' | 'editor' | 'viewer'

// Remove a member
await admin.members.remove('member_id');

API Keys

Manage API keys for your workspace.

// List API keys
const keys = await admin.apiKeys.list();

// Create a new API key
const { apiKey, key } = await admin.apiKeys.create({
  name: 'CI/CD Pipeline',
  scopes: ['content:read', 'content:write'],
  rateLimit: 1000, // requests per window
  rateLimitWindow: '1m', // 1m, 1h, 1d
  expiresAt: '2025-12-31T23:59:59Z', // optional
  ipWhitelist: ['192.168.1.0/24'], // optional
});

// IMPORTANT: Save the key - it's only shown once!
console.log('Save this key:', key);

// Revoke an API key
await admin.apiKeys.revoke('key_id');

API Key Scopes

| Scope | Description | |-------|-------------| | content:read | Read entries | | content:write | Create/update/delete entries | | models:read | Read model definitions | | models:write | Create/update/delete models | | members:read | Read member list | | members:write | Manage members | | admin | Full administrative access |

Webhooks

Set up webhooks to receive notifications when events occur.

// List webhooks
const webhooks = await admin.webhooks.list();

// Get a webhook with delivery stats
const webhook = await admin.webhooks.get('webhook_id');
console.log(webhook.stats); // { successCount, failureCount, lastTriggeredAt, ... }

// Create a webhook
const newWebhook = await admin.webhooks.create({
  name: 'Content Updates',
  url: 'https://api.example.com/webhooks/quickbuilder',
  events: ['entry.created', 'entry.updated', 'entry.deleted'],
  headers: {
    'X-Custom-Header': 'my-value',
  },
});
// Note: A secret is auto-generated. Save webhook.secret for signature verification!

// Update a webhook
await admin.webhooks.update('webhook_id', {
  events: ['entry.published'],
  isActive: true,
});

// Test a webhook
const result = await admin.webhooks.test('webhook_id');
if (result.success) {
  console.log('Webhook is working!');
} else {
  console.error('Webhook failed:', result.error);
}

// Delete a webhook
await admin.webhooks.delete('webhook_id');

Webhook Events

| Event | Triggered When | |-------|----------------| | entry.created | New entry created | | entry.updated | Entry updated | | entry.deleted | Entry deleted | | entry.published | Entry published | | entry.unpublished | Entry unpublished | | model.created | New model created | | model.updated | Model updated | | model.deleted | Model deleted | | member.joined | Member joined workspace | | member.removed | Member removed | | member.role_updated | Member role changed | | api_key.created | API key created | | api_key.revoked | API key revoked | | workspace.updated | Workspace settings updated |

Webhook Payload

interface WebhookPayload {
  event: string;           // e.g., 'entry.created'
  timestamp: string;       // ISO 8601
  workspaceId: string;
  data: {
    // Event-specific data
  };
}

Verifying Webhook Signatures

import crypto from 'crypto';

function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string,
  timestamp: string
): boolean {
  const signaturePayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signaturePayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler:
app.post('/webhooks/quickbuilder', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const timestamp = req.headers['x-webhook-timestamp'];
  const payload = JSON.stringify(req.body);

  if (!verifyWebhookSignature(payload, signature, WEBHOOK_SECRET, timestamp)) {
    return res.status(401).send('Invalid signature');
  }

  // Process the webhook...
});

Error Handling

The SDK throws typed errors for different failure scenarios:

import {
  AdminSDKError,
  AuthenticationError,
  AuthorizationError,
  NotFoundError,
  ValidationError,
  ConflictError,
  RateLimitError,
  NetworkError,
  TimeoutError,
  ServerError,
  isAdminSDKError,
  isRetryableError,
} from 'quick-builder-admin';

try {
  await admin.models.get('non-existent-id');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log('Model not found');
  } else if (error instanceof AuthenticationError) {
    console.log('Invalid API key');
  } else if (error instanceof RateLimitError) {
    console.log(`Rate limited. Retry after ${error.retryAfter}ms`);
  } else if (isRetryableError(error)) {
    console.log('Transient error, safe to retry');
  }
}

Error Types

| Error | Status | Description | |-------|--------|-------------| | AuthenticationError | 401 | Invalid or missing API key | | AuthorizationError | 403 | Insufficient permissions | | NotFoundError | 404 | Resource not found | | ValidationError | 400 | Invalid input data | | ConflictError | 409 | Resource conflict (e.g., duplicate) | | RateLimitError | 429 | Rate limit exceeded | | TimeoutError | 408 | Request timed out | | ServerError | 5xx | Server error | | NetworkError | - | Network/connection failure |

TypeScript Support

The SDK is written in TypeScript and exports all types:

import type {
  AdminClientConfig,
  Workspace,
  Model,
  ModelField,
  FieldType,
  Entry,
  EntryStatus,
  Member,
  MemberRole,
  ApiKey,
  ApiKeyScope,
  Webhook,
  WebhookEvent,
} from 'quick-builder-admin';

Examples

Sync Content from External CMS

import { createAdminClient } from 'quick-builder-admin';

const admin = createAdminClient({
  privateKey: process.env.QUICK_BUILDER_PRIVATE_KEY!,
});

async function syncProducts(externalProducts: ExternalProduct[]) {
  for (const product of externalProducts) {
    const existing = await admin.entries.list('Product', {
      query: { externalId: product.id },
      limit: 1,
    });

    if (existing.items.length > 0) {
      await admin.entries.update('Product', existing.items[0].id, {
        data: {
          name: product.name,
          price: product.price,
          description: product.description,
        },
      });
    } else {
      await admin.entries.create('Product', {
        data: {
          externalId: product.id,
          name: product.name,
          price: product.price,
          description: product.description,
        },
        status: 'published',
      });
    }
  }
}

Bulk Operations

async function publishAllDrafts(modelName: string) {
  let offset = 0;
  const limit = 50;

  while (true) {
    const drafts = await admin.entries.list(modelName, {
      status: 'draft',
      limit,
      offset,
    });

    if (drafts.items.length === 0) break;

    await Promise.all(
      drafts.items.map((entry) =>
        admin.entries.publish(modelName, entry.id)
      )
    );

    offset += limit;

    if (!drafts.pagination.hasMore) break;
  }
}

CI/CD Integration

// scripts/deploy-content.ts
import { createAdminClient } from 'quick-builder-admin';

const admin = createAdminClient({
  privateKey: process.env.QUICK_BUILDER_PRIVATE_KEY!,
});

async function deployContent() {
  // Create/update models from schema files
  const modelDefinitions = loadModelDefinitions('./schemas');

  for (const def of modelDefinitions) {
    const existing = await admin.models.list({ search: def.name });

    if (existing.length > 0) {
      await admin.models.update(existing[0].id, def);
    } else {
      await admin.models.create(def);
    }
  }

  console.log('Content deployment complete!');
}

deployContent();

Requirements

  • Node.js 18.0.0 or higher
  • Server-side environment only (not for browsers)

License

MIT

Support