Skills nocobase-workflow
Guide AI to create NocoBase workflows — triggers, conditions, data operations, SQL, scheduling
install
source · Clone the upstream repo
git clone https://github.com/openclaw/skills
OpenClaw · Install into ~/.openclaw/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/alexander-lq/work-fllows" ~/.openclaw/skills/openclaw-skills-nocobase-workflow && rm -rf "$T"
manifest:
skills/alexander-lq/work-fllows/skill.mdsource content
NocoBase Workflow Building
You are guiding the user to create automated workflows in NocoBase.
Key Concepts
Trigger Types
| Type | Description | Mode |
|---|---|---|
| Data change trigger | 1=create, 2=update, 3=create+update, 4=delete |
| Time-based trigger | 0=cron, 1=date field |
| Manual button trigger | — |
Node Types
| Type | Description |
|---|---|
| If/else branch (basic engine or math.js) |
| Update existing records |
| Create new records |
| Query records for use in downstream nodes |
| Execute raw SQL |
| HTTP request (webhooks, external APIs) |
| Iterate over array data |
| Terminate workflow (1=success, 0=failure) |
Node Linking Model
- Nodes form a linked list:
→upstreamIddownstreamId - Branch nodes (condition, loop) use
:branchIndex
= true branch (condition) or loop body (loop)1
= false branch (condition)0
= main line continuationnull
Variable System
| Variable | Description |
|---|---|
| Field from the trigger record |
| ID of the trigger record |
| Result from a previous node |
| Current item in a loop |
Workflow Patterns
Pattern 1: Auto-numbering (on create)
Generate sequential IDs like
PR-2026-001:
# Step 1: Create workflow nb_create_workflow("Auto Purchase Number", "collection", '{"mode": 1, "collection": "purchase_requests", "appends": [], "condition": {"$and": []}}') # Step 2: Add SQL node nb_add_node(wf_id, "sql", "Generate Number", '{"dataSource": "main", "sql": "UPDATE purchase_requests SET request_no = \'PR-\' || TO_CHAR(NOW(), \'YYYY\') || \'-\' || LPAD((SELECT COALESCE(MAX(CAST(SUBSTRING(request_no FROM \'[0-9]+$\') AS INT)),0)+1 FROM purchase_requests WHERE request_no LIKE \'PR-\' || TO_CHAR(NOW(), \'YYYY\') || \'-%\')::TEXT, 3, \'0\') WHERE id = {{$context.data.id}}"}') # Step 3: Enable nb_enable_workflow(wf_id)
Pattern 2: Status Sync (on create)
Set default status when a record is created:
nb_create_workflow("Default Status", "collection", '{"mode": 1, "collection": "orders", "appends": [], "condition": {"$and": []}}') nb_add_node(wf_id, "update", "Set Draft Status", '{"collection": "orders", "params": {"filter": {"id": "{{$context.data.id}}"}, "values": {"status": "draft"}}}') nb_enable_workflow(wf_id)
Pattern 3: Conditional Branch (on create)
Different actions based on field value:
nb_create_workflow("Transfer Type Handler", "collection", '{"mode": 1, "collection": "transfers", "appends": [], "condition": {"$and": []}}') # Condition node cond = nb_add_node(wf_id, "condition", "Is Transfer?", '{"rejectOnFalse": false, "engine": "basic", "calculation": {"group": {"type": "and", "calculations": [{"calculator": "equal", "operands": ["{{$context.data.type}}", "transfer"]}]}}}') # True branch: update status to "transferred" nb_add_node(wf_id, "update", "Mark Transferred", '{"collection": "assets", "params": {"filter": {"id": "{{$context.data.asset_id}}"}, "values": {"status": "transferred"}}}', upstream_id=cond_id, branch_index=1) # False branch: update status to "borrowed" nb_add_node(wf_id, "update", "Mark Borrowed", '{"collection": "assets", "params": {"filter": {"id": "{{$context.data.asset_id}}"}, "values": {"status": "borrowed"}}}', upstream_id=cond_id, branch_index=0) nb_enable_workflow(wf_id)
Pattern 4: Field Change Trigger (on update)
React when specific fields change:
nb_create_workflow("Disposal Complete", "collection", '{"mode": 2, "collection": "disposals", "changed": ["status"], "condition": {"status": {"$eq": "disposed"}}, "appends": []}') nb_add_node(wf_id, "update", "Update Asset Status", '{"collection": "assets", "params": {"filter": {"id": "{{$context.data.asset_id}}"}, "values": {"status": "disposed"}}}') nb_enable_workflow(wf_id)
Pattern 5: Date-based Schedule
Trigger N days before a date field:
# Trigger 30 days before insurance expiry nb_create_workflow("Insurance Expiry Warning", "schedule", '{"mode": 1, "collection": "insurance", "startsOn": {"field": "end_date", "offset": -30, "unit": 86400000}, "repeat": null, "appends": []}') nb_add_node(wf_id, "sql", "Mark Expiring", '{"dataSource": "main", "sql": "UPDATE insurance SET remark = \'expiring soon\' WHERE id = {{$context.data.id}}"}') nb_enable_workflow(wf_id)
Pattern 6: Cron Schedule
# Run every weekday at 9 AM nb_create_workflow("Daily Report", "schedule", '{"mode": 0, "startsOn": "2026-01-01T00:00:00.000Z", "repeat": "0 9 * * 1-5"}')
Pattern 7: Multi-node Chain
Chain multiple nodes on the main line:
nb_create_workflow("Complex Flow", "collection", '{"mode": 1, "collection": "orders", "appends": [], "condition": {"$and": []}}') # Node 1 (first node, no upstream_id needed) n1 = nb_add_node(wf_id, "query", "Get Customer", '{"collection": "customers", "multiple": false, "params": {"filter": {"id": "{{$context.data.customer_id}}"}}}') # Node 2 (chains after node 1) n2 = nb_add_node(wf_id, "update", "Update Order", '{"collection": "orders", "params": {"filter": {"id": "{{$context.data.id}}"}, "values": {"customer_name": "{{$jobsMapByNodeKey.NODE_KEY.name}}"}}}', upstream_id=n1_id) nb_enable_workflow(wf_id)
Workflow
Phase 1: Plan
Before creating workflows, identify:
- Trigger: What event starts the workflow? (record created/updated/deleted, schedule, manual)
- Collection: Which table triggers it?
- Logic: What conditions and actions are needed?
- Target: What data gets modified?
Phase 2: Create
- Create workflow with trigger config
- Add nodes in order (first node auto-links, subsequent need
)upstream_id - For branches: add condition node, then true/false branch nodes with
branch_index
Phase 3: Enable & Verify
- Enable the workflow
- List workflows to confirm:
nb_list_workflows() - Inspect with:
nb_get_workflow(wf_id)
Best Practices
- Title convention: Use prefixes for grouping, e.g. "AM-WF01 Auto Number"
- One workflow per concern: Don't combine unrelated logic
- Idempotent SQL: Write SQL that's safe to run multiple times
- Test before enable: Create all nodes first, then enable
- Batch cleanup: Use
to clean upnb_delete_workflows_by_prefix("AM-") - Duplicate prevention: Check
before creating — duplicate titles cause confusionnb_list_workflows() - Variable references: Always use double braces
— single braces won't work{{$context.data.field}}