Skip to main content
Technology & EngineeringAutomation Workflow Services272 lines

Superblocks

Build internal tools and workflows with Superblocks using API integrations, UI components, scheduled jobs, and permissions.

Quick Summary15 lines
You are an expert in Superblocks, building internal applications, scheduled jobs, and workflows with API integrations, drag-and-drop UI components, and enterprise access controls. You design maintainable tools with proper separation between backend APIs and frontend logic.

## Key Points

1. **Embedding SQL in frontend event handlers** - Always use Backend APIs for data access. Frontend should only call APIs, never execute queries directly.
2. **Relying on component visibility for security** - Hidden components can be accessed via browser dev tools. Enforce permissions in Backend API code steps.
3. **Building one mega-application** - Split tools by domain (orders, users, inventory). Use navigation links between applications for cross-domain flows.
4. **Skipping error handling in multi-step APIs** - Each step can fail independently. Wrap critical steps in try-catch and return meaningful error messages to the frontend.
- Building enterprise internal tools with role-based access control and audit requirements
- Creating admin panels that need to query multiple databases and REST APIs in a single view
- Teams with mixed technical skills need a platform where developers build APIs and operators build UIs
- Replacing spreadsheet-based workflows with structured applications that enforce data validation
- Scheduling data sync jobs, report generation, or cleanup tasks with built-in monitoring
skilldb get automation-workflow-services-skills/SuperblocksFull skill: 272 lines
Paste into your CLAUDE.md or agent config

Superblocks Internal Tool Builder

You are an expert in Superblocks, building internal applications, scheduled jobs, and workflows with API integrations, drag-and-drop UI components, and enterprise access controls. You design maintainable tools with proper separation between backend APIs and frontend logic.

Core Philosophy

Backend APIs as the Foundation

Superblocks separates backend APIs from frontend applications. Define your data access, transformations, and business logic in Backend APIs first. Frontend applications bind to these APIs, enabling reuse across multiple tools and enforcing consistent data access patterns.

Component-Driven UI

Build interfaces by composing pre-built components (tables, forms, charts, modals) that bind to API responses. Configure component properties with bindings ({{ }}) rather than imperative JavaScript. Reserve code for transformations and complex conditional logic.

Enterprise Access Control

Superblocks provides role-based permissions at the application, API, and component level. Design tools with permission boundaries from the start: who can view, who can edit, who can execute destructive actions.

Setup

// Superblocks API for programmatic management
const SB_API = "https://api.superblocks.com/api/v1";
const headers = {
  Authorization: `Bearer ${process.env.SUPERBLOCKS_API_KEY}`,
  "Content-Type": "application/json",
};

// List applications
const response = await fetch(`${SB_API}/applications`, { headers });
const { data: apps } = await response.json();

// Superblocks Agent (self-hosted deployment)
// docker pull superblocksteam/agent:latest
// Required env vars: SUPERBLOCKS_AGENT_KEY, SUPERBLOCKS_AGENT_HOST_URL
# Superblocks CLI
npm install -g @superblocksteam/cli

# Login and sync
superblocks login
superblocks pull --app "Order Management"
superblocks push --app "Order Management"

Key Patterns

Separate Backend APIs from Frontend

// Do: Define reusable Backend APIs
// Backend API: "getOrders"
// Step 1: SQL Query (PostgreSQL resource)
SELECT o.id, o.total, o.status, u.name as customer_name
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = {{ params.status || 'all' }}
  AND o.created_at >= {{ params.since || '2024-01-01' }}
ORDER BY o.created_at DESC
LIMIT {{ params.limit || 50 }};

// Step 2: JavaScript transform
const orders = Step1.output;
return orders.map(o => ({
  ...o,
  totalFormatted: `$${o.total.toFixed(2)}`,
  isOverdue: o.status === 'pending' && daysSince(o.created_at) > 7,
}));

// Frontend binds to: {{ getOrders.response }}
// Don't: Write SQL directly in frontend component event handlers

Use Bindings for Reactive UI

// Do: Bind component properties to API data
// Table component:
//   Data: {{ getOrders.response }}
//   Loading: {{ getOrders.loading }}
//   Empty message: {{ getOrders.response.length === 0 ? "No orders found" : "" }}

// Filter select component:
//   Options: {{ getStatuses.response.map(s => ({ label: s.name, value: s.id })) }}
//   onChange: {{ getOrders.run({ status: Select1.selectedValue }) }}

// Don't: Use event handlers to imperatively set table data

Apply Role-Based Permissions

// Do: Configure permissions at multiple levels
// Application level: Viewer, Editor, Admin roles
// Component level: conditionally show based on user role
//   Button "Delete": visible={{ Global.user.role === 'admin' }}
//   Tab "Settings": visible={{ ['admin', 'manager'].includes(Global.user.role) }}

// Backend API level: validate permissions in code steps
if (!['admin', 'finance'].includes(Global.user.role)) {
  throw new Error('Unauthorized: insufficient permissions');
}

// Don't: Rely solely on hiding UI components for security

Common Patterns

CRUD Application with Form Validation

// Backend API: createOrder
// Step 1: Validate input (JavaScript)
const { customerId, items, priority } = params;
if (!customerId) throw new Error("Customer ID is required");
if (!items || items.length === 0) throw new Error("At least one item is required");
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
return { customerId, items, priority: priority || "normal", total };

// Step 2: Insert order (SQL)
INSERT INTO orders (customer_id, total, priority, status, created_at)
VALUES (
  {{ Step1.output.customerId }},
  {{ Step1.output.total }},
  {{ Step1.output.priority }},
  'pending',
  NOW()
)
RETURNING id, customer_id, total, status;

// Step 3: Insert line items (SQL loop)
{{ Step1.output.items.forEach(async (item) => {
  await Step3.run({
    orderId: Step2.output[0].id,
    productId: item.productId,
    quantity: item.quantity,
    price: item.price,
  });
}) }}

// Frontend form onSubmit:
// {{ createOrder.run({ customerId: Form1.data.customerId, items: Table2.data, priority: Select1.value }) }}
// {{ getOrders.run() }}  // refresh table
// {{ showNotification("Order created successfully") }}

Multi-Step Workflow with Approvals

// Backend API: submitExpenseReport
// Step 1: Create expense report (SQL)
INSERT INTO expense_reports (submitter_id, amount, description, status)
VALUES ({{ Global.user.id }}, {{ params.amount }}, {{ params.description }}, 'pending_approval')
RETURNING id;

// Step 2: Notify approver (REST API - Slack)
// POST https://slack.com/api/chat.postMessage
{
  "channel": "#expense-approvals",
  "blocks": [
    {
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*New Expense Report*\nFrom: {{ Global.user.name }}\nAmount: ${{ params.amount }}\n{{ params.description }}"
      }
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": { "type": "plain_text", "text": "Review" },
          "url": "https://app.superblocks.com/applications/expense-tool?reportId={{ Step1.output[0].id }}"
        }
      ]
    }
  ]
}

// Backend API: approveExpense (called by approver)
// Step 1: Verify approver permission
if (Global.user.role !== 'manager' && Global.user.role !== 'finance') {
  throw new Error('Only managers and finance can approve expenses');
}
// Step 2: Update status
UPDATE expense_reports SET status = 'approved', approved_by = {{ Global.user.id }}
WHERE id = {{ params.reportId }} AND status = 'pending_approval';

Scheduled Job for Data Sync

// Superblocks Scheduled Job: syncInventory
// Schedule: Every 6 hours

// Step 1: Fetch from external API (REST)
// GET https://warehouse-api.example.com/inventory
// Headers: { Authorization: Bearer {{ secrets.WAREHOUSE_API_KEY }} }

// Step 2: Transform data (JavaScript)
const inventory = Step1.output;
return inventory.map(item => ({
  sku: item.sku,
  quantity: item.available_qty,
  warehouse: item.location_code,
  lastUpdated: new Date().toISOString(),
}));

// Step 3: Upsert to database (SQL)
{{ Step2.output.forEach(async (item) => {
  await sql`
    INSERT INTO inventory (sku, quantity, warehouse, synced_at)
    VALUES (${item.sku}, ${item.quantity}, ${item.warehouse}, ${item.lastUpdated})
    ON CONFLICT (sku, warehouse) DO UPDATE
    SET quantity = ${item.quantity}, synced_at = ${item.lastUpdated}
  `;
}) }}

// Step 4: Log summary (JavaScript)
return {
  syncedCount: Step2.output.length,
  timestamp: new Date().toISOString(),
};

Dashboard with Charts and Filters

// Backend API: getDashboardData
// Step 1: Revenue by month (SQL)
SELECT DATE_TRUNC('month', created_at) as month,
       SUM(total) as revenue,
       COUNT(*) as order_count
FROM orders
WHERE created_at >= {{ params.startDate || '2024-01-01' }}
GROUP BY month ORDER BY month;

// Step 2: Top customers (SQL)
SELECT u.name, SUM(o.total) as total_spent, COUNT(*) as orders
FROM orders o JOIN users u ON o.user_id = u.id
WHERE o.created_at >= {{ params.startDate || '2024-01-01' }}
GROUP BY u.id, u.name ORDER BY total_spent DESC LIMIT 10;

// Step 3: Combine (JavaScript)
return {
  revenueChart: Step1.output,
  topCustomers: Step2.output,
  totalRevenue: Step1.output.reduce((s, m) => s + m.revenue, 0),
};

// Frontend components:
// Chart1 (Line): data={{ getDashboardData.response.revenueChart }}
// Table1: data={{ getDashboardData.response.topCustomers }}
// Stat1: value={{ `$${getDashboardData.response.totalRevenue.toLocaleString()}` }}
// DatePicker onChange: {{ getDashboardData.run({ startDate: DatePicker1.value }) }}

Anti-Patterns

  1. Embedding SQL in frontend event handlers - Always use Backend APIs for data access. Frontend should only call APIs, never execute queries directly.
  2. Relying on component visibility for security - Hidden components can be accessed via browser dev tools. Enforce permissions in Backend API code steps.
  3. Building one mega-application - Split tools by domain (orders, users, inventory). Use navigation links between applications for cross-domain flows.
  4. Skipping error handling in multi-step APIs - Each step can fail independently. Wrap critical steps in try-catch and return meaningful error messages to the frontend.

When to Use

  • Building enterprise internal tools with role-based access control and audit requirements
  • Creating admin panels that need to query multiple databases and REST APIs in a single view
  • Teams with mixed technical skills need a platform where developers build APIs and operators build UIs
  • Replacing spreadsheet-based workflows with structured applications that enforce data validation
  • Scheduling data sync jobs, report generation, or cleanup tasks with built-in monitoring

Install this skill directly: skilldb add automation-workflow-services-skills

Get CLI access →