- Documentation
MySQL integration
Start workflows from new or changed rows and read and write a client's MySQL database from inside a workflow.
What it does
The MySQL integration lets your agency react to and change a client's MySQL (or MariaDB) database from a workflow. Connect the database once with a connection string or discrete host, port, user, password, and database. Triggers start a workflow when a row is added or changed, when your own query returns a new row, or when the schema gains a table or column. Actions find rows without writing SQL, run parameterized queries, insert one or many rows, update, upsert, and delete rows, and run any single statement, including stored procedures. Connection details are encrypted at rest, every value you pass is bound as a parameter rather than spliced into the SQL, and results are capped so a large read cannot overwhelm a run.
Connect a MySQL database
- Open your workspace in TaskJuice and add a MySQL connection on the node that uses it.
- Paste a connection string, for example
mysql://user:password@db.example.com:3306/appdb. - Or fill in the host, port (3306 by default), user, password, and database separately. Connections require TLS and verify the certificate chain by default.
- Save the connection. The password and connection string are stored encrypted and are never shown again or written to run output.
Provision a least-privilege database user scoped to the tables the workflow touches, and expose the database on a public host the workflow can reach over TLS. Private and internal addresses are blocked. A user that only needs triggers and reads can be granted SELECT alone.
Network access. TaskJuice reaches the database from more than one network. Workflow steps in a published run connect from a fixed set of outbound addresses. Triggers, which check the database on an interval, and the editor's Test step connect from cloud infrastructure that has no fixed IP addresses. If the database only accepts listed addresses, triggers and editor tests time out, so a step that fails its editor test with a connection error can still work in a published run. Unless you have a reason to restrict it, allow access from anywhere (0.0.0.0/0) and let a long, random password and TLS protect the database.
Triggers
MySQL has no way to call out when data changes, so each trigger checks the database on an interval you choose (every 5 minutes by default, as often as every minute). Each check runs one small query. Rows, tables, and columns that already exist when you turn a trigger on never fire; only changes after that point do. Every check that finds something starts one run whose items array holds everything found, so add a Loop node to handle the items one at a time.
mysql/new-rowfires when a row is added to a table. Point it at a column whose value only grows for new rows, usually theAUTO_INCREMENTprimary key.mysql/new-or-updated-rowfires when a row is added or changed. Point it at aDATETIMEorTIMESTAMPcolumn your schema sets on every write, for exampleupdated_at DATETIME(3) ON UPDATE CURRENT_TIMESTAMP(3), with a unique ID column as the tie-breaker.mysql/new-row-custom-queryfires when your ownSELECTreturns a row it has not returned before. Name a unique column so a row fires once even if its other values change later.mysql/new-tablefires when a table or view is created in the connected database.mysql/new-columnfires when a column is added to a table. Name a table to watch just that one, or leave it empty to watch them all.
Actions
mysql/findfinds rows in a table without writing SQL. Filter with conditions such asstatus = openortotal >= 100(operators=,!=,>,>=,<,<=,like,not-like,in,not-in,is-null,is-not-null), match all or any of them, pick the columns to return, sort, and cap the row count.mysql/queryruns a read-onlySELECTwith?placeholders and a matchingparamsarray, returning the result rows and the row count.mysql/insertinserts a row from a column-to-value map and returns the new row'sinsertId.mysql/insert-manyinserts up to 1,000 rows in one statement from an array of column-to-value objects, such as theitemsof a trigger. A column missing from a row is inserted asNULL. It returns theinsertIdMySQL reports for the statement, the first new row's id.mysql/updateupdates the rows matching an equality filter, setting new column values, and returns the affected row count.mysql/upsertinserts a row or, on a duplicate unique or primary key, updates it (INSERT ... ON DUPLICATE KEY UPDATE). MySQL chooses the conflicting index; the columns you name select which columns are refreshed. It returns theinsertIdMySQL reports for the statement, and arowCountthat tells you what happened: 1 when the row was inserted, 2 when an existing row was updated, 0 when the row already held these values.mysql/deletedeletes the rows matching an equality filter and returns the deleted row count.mysql/executeruns one SQL statement of any kind: anUPDATEwith a customWHERE, aCALLto a stored procedure (its first result set is returned asrows), or a schema change. Treat it with care; it can change or remove data and cannot be undone.
MySQL cannot return the written row itself. Insert, Insert Many and Upsert return rows holding one object, { insertId }, with the id MySQL reports for the statement (the first id of a multi-row insert) when the table has an AUTO_INCREMENT column, and an empty rows otherwise.
Write a safe query
Always pass values through params, never by building the SQL string yourself:
sql: SELECT id, email FROM customers WHERE status = ? AND created_at > ?
params: ["active", "2026-01-01"]Table and column names in the structured actions and triggers are validated against a strict allowlist and quoted for you, so a name containing a backtick, semicolon, or whitespace is rejected before anything runs. The query, custom-query trigger, and execute action accept one statement at a time; a second statement after a ; is rejected.
Known limitations
- Triggers check on an interval rather than reacting instantly. Keep the watched columns indexed;
new-or-updated-rowreads best with a composite index on the timestamp and ID columns. new-or-updated-rowtracks its position to the millisecond and reads rows that share a millisecond in ID order, so every row is delivered exactly once at any column precision,DATETIME(6)andTIMESTAMP(6)included, even when one bulkUPDATEstamps thousands of rows with the same time. A backlog spread over time is read straight from the composite index, a few milliseconds per check however large the backlog. Rows that share a single millisecond are scanned together on every check, and keep being scanned after they have all been delivered, until a newer row moves the trigger's position past that millisecond: measured at about half a second per million such rows.- The ID column of
new-rowand the timestamp and ID columns ofnew-or-updated-rowmust never beNULL. A row with aNULLin one of them is never delivered, and it can stop the trigger with a configuration error. That happens when the table is empty at the moment you turn the trigger on and a row with aNULLtimestamp (fornew-row, aNULLID) then arrives, or when the row with the newest timestamp has aNULLID. ANULLtimestamp that is already in a table with other rows when you turn the trigger on does not stop it; that row is simply never delivered. Declare the columnsNOT NULL. DATETIME,TIMESTAMPandDATEvalues are handled as plain wall-clock values: the trigger and the actions read and compare them exactly as the server returns them, with no further time-zone conversion, so values in a daylight-saving changeover hour behave like any other.- The custom-query trigger re-runs your whole query on every check. Keep it selective with a
WHEREand aLIMITof a few hundred rows. - Find and query return at most 1,000 rows and around 5 MB per call; when a read is clipped the result sets
truncatedtotrue. - A statement that runs longer than 30 seconds is cancelled.
- A few statements, such as
CREATE PROCEDUREandLOCK TABLES, cannot take?parameters in MySQL. The execute action runs them when Parameters is empty; write any values into the statement directly. - Connections are short-lived: each action and each trigger check opens and closes its own connection. Point the connection at a pooler if your database has a low connection limit.