quick-builder-admin
v0.1.1
Published
Server-side Admin SDK for Quick Builder - manage workspace resources programmatically
Maintainers
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-adminQuick 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
