• Documentation
  • Ask a Question
  • Zeroplat.io
  • Documentation
  • Ask a Question
  • Zeroplat.io
  • Introduction
    • Welcome to Zeroplat
    • Video Intro
  • Get Started
    • Requirements
    • Installation
  • Management
    • Resources
    • SaaS
      • Multi-tenancy
      • Editions
    • Permissions
    • Organization Units
    • Language
      • Languages
      • Language Texts
    • Roles
    • User
      • Users
      • Invite Users
    • Audit Logs
  • Visual Builder
    • Overview
    • Resources
      • Menu & Navigation
      • Pages
      • Dashboards
      • Components
      • Generate Pages from Datasource
    • Design
      • JS Query
      • Transformer
      • Variable
    • Event Designer
      • Tasks
        • Execute query
        • Start workflow
        • Show page
        • Close Page
        • Set variable
        • Show notification
        • Set components props
      • Switch
        • If
        • Confirm box
        • Confirm popover
    • Data
    • Preview & Publish
    • Components
      • Common Properties
        • Layout
        • Style
        • Actions
        • Validations
        • Tooltip
      • Inputs
        • Button
        • Input
        • InputNumeric
        • ComboBox
        • MulticolumnCombobox
        • Checkbox
        • CheckboxGroup
        • Switch
        • RadioGroup
        • ToggleButtonGroup
        • LocalizedInput
        • Slider
      • Business
        • OrganizationUnitBrowser
        • RoleBrowser
        • UserBrowser
      • Data display
        • DataGrid
      • Editors
        • QuillEditor
      • Feedback
        • Alert
        • Progress
      • File
        • Dropzone
        • FileButton
        • FileInput
      • Layout
        • SplitLayout
      • Navigation
        • TabBar
      • Pickers
        • DatePicker
        • DateTimePicker
        • DateRangePicker
        • DateCalendar
        • TimePicker
        • IconPicker
      • Surfaces
        • Card
      • Charts
        • AreaChart
  • Backend
    • Overview
  • Integrations
    • Overview
    • Environments
    • Database
      • MS SQL
      • SQL Lite
      • My SQL
      • PostgreSQL
    • API
      • Rest API
      • SMTP
      • Google Sheets
      • Twillio SMS
    • Javascript
      • Variable
      • Transformer
      • JS Query
    • Zeroplat Hosted
      • Zeroplat E-mail
  • Workflow (BPM)
    • Overview
    • Building and managing workflows
    • Inbox
    • Outbox
  • Marketplace
    • Overview
    • Management

JS Query

8 views 0

Written by Zeroplat
17 September 2025

JS Query lets you write JavaScript to orchestrate data queries, transform results, and wire up UI logic without leaving the Designer. You can run JS code, call other queries, read component values, and return a structured result that the rest of the app can use.

When to use JS Query

  • Transform data coming from a SQL/API query before binding it to a table or chart
  • Orchestrate workflows (e.g., insert → refresh list → notify user)
  • Conditional logic (e.g., validate inputs, branch by role, gate execution)
  • Combine multiple queries (parallel or sequential)
  • Lightweight utilities (formatting, mapping, grouping)

Anatomy

  1. Explorer (left)
    • Lists your queries and components.
    • Example: jsQuery1 under Explorer.
  2. Editor (center)
    • JavaScript editor area with Execute button.
    • Placeholder shows typical data shaping, e.g.: // type your code here // example: return formatDataAsArray(data).filter(row => row.quantity > 20)
    • Response panel (bottom) previews the value you return.
  3. Settings (center, under editor)
    • Run on page load: Executes automatically when the page mounts.
    • Execute if: Runs only if the expression is truthy (guards execution).
  4. Event Handlers
    • onSuccess: Actions to run when the JS Query completes without throwing.
    • onFailure: Actions to run if code throws (or a promise rejects).
  5. Right Panel (component props)
    • Where you bind component events (e.g., Button → onClick → run jsQuery1)

Execution Model

  • A JS Query is asynchronous by default—use await freely.
  • Whatever you return becomes the query’s output and is available as jsQueryName.data.
  • Throwing an error (or rejecting a promise) will trigger onFailure.

Example (sync):

return { ok: true, sum: 1 + 1 };

Example (async):

// Sequential orchestration
await getOrders.run();
const items = getOrders.data ?? [];
return items.filter(x => x.quantity > 20);

Referencing Other Queries

From within a JS Query, you can trigger other Data Queries or JS Queries:

// Run a query (e.g., SQL, REST) and read its output
await getCustomers.run({ country: countrySelect.value });
const rows = getCustomers.data;

// Optionally run more queries
await getOrders.run({ customerId: rows[0]?.Id });

// Return any serializable value
return { firstCustomer: rows[0], orders: getOrders.data };

Note: Use .run(params?) to execute a query programmatically. In the UI, the same query is executed using the Execute button or via component events (e.g., Button → onClick → Run query).

Reading Component Values

You can directly read component values by their names:

const name = nameInput.value;
const activeOnly = activeCheckbox.value;
const limit = Number(limitInput.value) || 50;

await searchUsers.run({ name, activeOnly, limit });
return searchUsers.data;

Typical properties you’ll use:

  • TextInput.value, NumberInput.value
  • Select.value, MultiSelect.values
  • Checkbox.value, DatePicker.value
  • Table.selectedRow, Table.selectedRow.data, Table.selectedRows

If your app uses pages like TransactionPage1, component names are still globally addressable by their names (e.g., Button1, CustomerTable).

Returning Data

  • Always return the value you want to expose to bindings (tables, charts, etc.).
  • The Response panel shows the last returned value.
  • Access it with jsQuery1.data elsewhere (bindings, expressions, other queries).

Shape the data:

await getOrders.run();
const rows = Array.isArray(getOrders.data) ? getOrders.data : [];
const top = rows
  .filter(x => x.quantity > 20)
  .map(({ id, product, quantity }) => ({ id, product, quantity }))
  .sort((a, b) => b.quantity - a.quantity);

return top;

The editor hint shows formatDataAsArray(...). When present, you can use it to normalize query outputs into arrays before filtering/mapping.

Settings

Run on page load

  • Check this to execute once when the page loads.
  • Useful for initial data fetches or bootstrapping cache/state.

Execute if (guard)

  • Provide an expression; the query runs only if it evaluates to truthy.
  • Example: // Execute if: userSelect.value && userSelect.value !== 'all'

Event Handlers

onSuccess

  • Chain post-success actions (e.g., refresh a table, open a modal, navigate).
  • Prefer using onSuccess for side effects, and keep your JS Query focused on data logic.

onFailure

  • Handle errors gracefully (e.g., set error text, show a message).
  • Keep your JS code defensive (see error handling below).

Common Patterns

1) Validate → Run → Return

if (!nameInput.value) {
  throw new Error("Name is required.");
}

await createCustomer.run({
  name: nameInput.value,
  surname: surnameInput.value,
  balance: Number(balanceInput.value) || 0
});

// Optional: refresh list
await getCustomers.run();

return { created: true, count: (getCustomers.data || []).length };

2) Read–Transform–Bind

await getOrders.run({ start: startDate.value, end: endDate.value });
const rows = Array.isArray(getOrders.data) ? getOrders.data : [];
const byProduct = rows.reduce((acc, r) => {
  acc[r.product] = (acc[r.product] || 0) + r.amount;
  return acc;
}, {});
return Object.entries(byProduct).map(([product, total]) => ({ product, total }));

3) Parallel Queries

const [{ data: north }, { data: south }] = await Promise.all([
  getSalesNorth.run(), 
  getSalesSouth.run()
]);
return { north, south };

4) Conditional Orchestration

await getUser.run({ id: userIdInput.value });
const user = getUser.data;

if (!user) throw new Error("User not found.");

if (user.status === "pending") {
  await approveUser.run({ id: user.id });
  await auditLog.run({ action: "approve", userId: user.id });
} else {
  await archiveUser.run({ id: user.id });
}

return { status: "done" };

5) Pagination Helpers

const page = Number(pageNumber.value) || 1;
const size = Number(pageSize.value) || 20;
await getOrders.run({ offset: (page - 1) * size, limit: size });
return getOrders.data;

Error Handling

Use try/catch if you want to keep the query “successful” but return an error payload; otherwise just throw to trigger onFailure.

try {
  await riskyQuery.run();
  return { ok: true, data: riskyQuery.data };
} catch (e) {
  // Option 1: let onFailure handle it
  throw e;

  // Option 2: swallow and return a structured error
  // return { ok: false, message: e.message };
}

Best practice

  • Validate incoming inputs early
  • Fail fast on missing/invalid values
  • Keep return shapes consistent (e.g., always { ok, data, message })

Performance Tips

  • Do less per query: one responsibility (fetch OR transform OR orchestrate)
  • Use Execute if to avoid wasted calls
  • Cache in a page-level store or reuse existing query results where possible
  • Parallelize independent reads with Promise.all
  • Debounce input-driven calls from the UI (e.g., search) via button or a dedicated debounce in JS

Security & Safety

  • Never embed secrets in JS Queries—use secure Data Sources and server-side credentials.
  • Validate/escape user inputs if you pass them to SQL/API queries.
  • Avoid returning sensitive data unless explicitly needed by the UI.

Binding Examples (outside the JS Query)

  • Table data: {{ jsQuery1.data }}
  • Chart series: {{ jsQuery1.data.map(x => x.total) }}
  • Conditional UI: {{ !!jsQuery1.data && jsQuery1.data.length > 0 }}

Trigger from a Button:

  • Select the Button → onClick → Run query → choose jsQuery1.

Chain with onSuccess:

  • In jsQuery1 → onSuccess: Run query → getCustomers (to refresh list)
  • In jsQuery1 → onFailure: Set text of errorLabel or open a small error dialog

FAQ

Q: Where do I see what my query returned?
A: In the Response panel under the editor. You can also bind jsQueryName.data anywhere.

Q: Can I call multiple queries from one JS Query?
A: Yes—sequentially with await, or in parallel with Promise.all.

Q: How do I prevent it from running automatically?
A: Keep Run on page load unchecked, and bind execution to a button or a specific event.

Q: What happens if I don’t return anything?
A: jsQueryName.data will be undefined. Always return a value if you plan to bind the result.

Copy-paste Starters

Fetch & return

await getCustomers.run();
return getCustomers.data;

Input-driven fetch

await search.run({ term: searchInput.value?.trim() || "" });
return search.data;

Insert → Refresh → Return count

await createItem.run({ name: nameInput.value });
await listItems.run();
return { ok: true, count: (listItems.data || []).length };

Filter and format

await getOrders.run();
const rows = formatDataAsArray(getOrders.data);
return rows.filter(r => r.quantity > 20).map(({ id, product, quantity }) => ({ id, product, quantity }));

Was this helpful?

Yes  No
Related Articles
  • DateCalendar
  • Progress
  • TimePicker
  • IconPicker
  • DateTimePicker
  • DateRangePicker

Didn't find your answer? Contact Us

Previously
Design
Up Next
Transformer
Copyright 2025 Zeroplat.io. All Rights Reserved