How to Use SQL Queries to Drive Workflow Decisions

Query your database mid-workflow to make data-driven decisions.

What This Integration Does

Most real workflows need to look something up before they decide what to do next. Is this email already a customer? Is there inventory in this warehouse? What's the pricing rule for this tier? Hard-coding the answer is brittle - the answer changes the moment marketing or ops updates a row. This pattern keeps the answer in your database where it belongs and pulls it into the workflow at decision time with a SQL query.

The workflow inserts a Connector node that runs a parameterized SQL query, captures the result, and feeds it into a Condition node that branches the flow. The query is fast (single primary-key lookup or indexed scan), keeps your business rules out of the workflow definition, and is testable from outside Spojit.

Prerequisites

  • A MySQL connection with read access to the lookup table.
  • An index on whatever column the workflow queries (email, SKU, customer ID, etc.) so lookups stay fast.
  • A clear answer to "what does the lookup return?" - typically a single row or zero rows; pagination is rarely needed for decision logic.

Step 1: Identify the Decision Point

Open the host workflow and find the step where the decision happens. Usually it's right after a Trigger (e.g. webhook with an email) and before the work that needs to know the answer. Add the lookup nodes between those two.

Step 2: Run the Lookup Query

Add a Connector node pointing at the mysql connector and pick the execute-query tool. Write a parameterized query that returns the answer you need:

SELECT id, tier, lifetime_value
FROM customers
WHERE email = '{{ trigger.email }}'
LIMIT 1

Use parameter binding rather than string concatenation - the connector supports it - to avoid SQL injection if the input came from an external source.

Step 3: Branch on the Result

Add a Condition node that inspects {{ lookup.length }}:

  • True branch (length > 0): existing customer. Route to the path that uses their tier and lifetime value.
  • False branch (length === 0): new customer. Route to the onboarding flow.

For more than two outcomes (tier-based pricing, for example), chain multiple Condition nodes or use a single Condition with multiple expressions.

Step 4: Use the Result Downstream

On the existing-customer branch, the row from Step 2 is available as {{ lookup[0] }}. Reference fields directly in subsequent steps:

  • Apply a tier-specific discount: discount = {{ lookup[0].tier === 'gold' ? 0.15 : 0.05 }}
  • Personalize an email: "Welcome back, {{ lookup[0].first_name }}"
  • Decide whether to require manual approval based on {{ lookup[0].lifetime_value }}

Step 5: Aggregate Lookups

For decisions that depend on counts or sums rather than a single row, use an aggregate query:

SELECT COUNT(*) AS recent_orders
FROM orders
WHERE customer_id = {{ customer.id }}
  AND placed_at > NOW() - INTERVAL 30 DAY

The Condition then branches on {{ lookup[0].recent_orders }} - useful for "if customer has more than 5 orders this month, skip the welcome discount" type logic.

Step 6: Cache Hot Lookups

If the same lookup runs on every trigger and the underlying data changes rarely (pricing rules, tier definitions), cache the result. One approach: a second workflow on a schedule populates a small JSON document in mongodb with the latest rules; the host workflow reads that single document with find-documents instead of hitting MySQL on every trigger. Drops latency from tens of milliseconds to a single read and reduces DB load.

Tips

  • Always LIMIT - a stray query without LIMIT can return a million rows and grind the workflow to a halt. If you expect one row, write LIMIT 1.
  • Index the lookup column - it's the single biggest factor in keeping the workflow snappy. A full table scan on every trigger is a slow death.
  • Return only what you need - SELECT id, tier is faster than SELECT * and makes the variable references clearer.

Common Pitfalls

  • Empty result vs missing row - {{ lookup.length }} is the safe check. {{ lookup[0].id }} on an empty result throws an error and aborts the workflow.
  • Quoting strings in raw SQL - parameter binding handles escaping. Manually concatenating '{{ trigger.email }}' breaks the moment someone's email has an apostrophe.
  • Stale reads - if the row was written by a previous workflow step a few seconds ago, replication lag on a read replica can mean you don't see it. Either point the workflow at the primary or add a small retry.

Testing

Trigger the workflow with three inputs: a known existing customer, a known non-customer, and an edge case (e.g. an email with apostrophes). Confirm each routes down the right branch and the downstream steps see the right data. If you're using cached lookups, also confirm what happens when the cache is empty.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.