Skip to content

DB Native ​

The DB Native block gives you direct access to your database with JavaScript. Write a raw SQL query (or, for MongoDB, use the driver) for anything the other DB blocks can't do, and the value you return goes to the next block.

When to use it ​

  • Write a query the standard blocks can't, like GROUP BY, window functions, unions, or database-specific features.
  • Call a stored procedure, or run a one-off maintenance command.
  • Don't use it for simple reads and writes. DB Get All, DB Insert and friends are safer, easier to read, and work on every database.
  • Don't build the query text from user input. Always use placeholders, see below.

Inputs ​

FieldRequiredDefaultWhat it does
ConnectionYesnoneThe database integration to use.
JSYesnoneThe code to run. It can call dbQuery(query, params?) (or use db on MongoDB) and must return the result.
Save output to variableNooffStore the result in outputs.<name>.
  • SQL (PostgreSQL, MySQL): dbQuery(query, params?) runs the query and returns the result rows as a list.
  • MongoDB: use db and ObjectId instead. See MongoDB below.

Outputs ​

HandleWhen it is used
Next (right)The code finished. The next block receives whatever your code returns.

Example ​

Count orders per status.

FieldValue
Connectionmain-db
JSsee below
javascript
const rows = await dbQuery(
  "SELECT status, COUNT(*) AS total FROM orders WHERE created_at > $1 GROUP BY status",
  [getQueryParam("since")],
);
return rows;

The next block receives:

json
[{ "status": "paid", "total": 12 }, { "status": "pending", "total": 3 }]

How it behaves ​

  • You must return the result. Without return, the next block receives nothing.
  • Always await the call. Without it you get a pending promise instead of rows.
  • Errors fail the block. A bad query, a missing table or a lost connection goes to the Error Handler.
  • Inside a transaction, the query is part of the DB Transaction it runs in. On MongoDB this covers every db.collection(...) read and write.
  • Value types follow the other DB blocks. For example, COUNT(*) is a number and a DECIMAL is text. See Value types.
  • No safety net. The query runs exactly as written. A DELETE without WHERE deletes everything.
  • It replaces the flowing data with what you return.

Passing values safely ​

Never put user input straight into the query text. Pass it in the params array and use a placeholder instead:

DatabasePlaceholder
PostgreSQL$1, $2, …
MySQL$1, $2, … or ?

$1 is the first value in params, $2 the second, and so on. The same number can appear more than once. The placeholders work the same on both databases. On MySQL, use either $1 or ? in one query, not both. A $1 inside quotes, like '$1' or the JSON path '$.name', is plain text and not a placeholder.

javascript
const search = getQueryParam("q");
const users = await dbQuery(
  "SELECT id, username, email FROM users WHERE username = $1 LIMIT 20",
  [search],
);
return users; // [{ id: 1, username: "john", email: "[email protected]" }]

WARNING

Writing `... WHERE username = ${search}` puts the raw text into the SQL. A value like john:doe then breaks the query with a syntax error, and a crafted value can change what the query does (SQL injection). Placeholders avoid both.

MongoDB ​

On a MongoDB connection, the code gets two things instead of dbQuery:

NameWhat it is
dbThe database, already connected, from the official MongoDB driver.
ObjectIdBuilds document ids: new ObjectId("665f…").
javascript
const id = new ObjectId(getRouteParam("id"));
await db.collection("orders").updateOne({ _id: id }, { $set: { status: "paid" } });
return await db.collection("orders").findOne({ _id: id });
  • Inside a DB Transaction, every db.collection(...) read and write joins the transaction on its own. If the transaction rolls back, the writes are undone. Commands on db itself (like db.command(...)) and index changes don't join it.
  • dbQuery("...") with a query throws on MongoDB, with the message "dbQuery takes SQL; on MongoDB use db.collection(...)". Older code that calls await dbQuery() with nothing still works: it returns the same db.
  • The code editor knows the driver's types, so db. suggests methods as you type.
  • Ids from other blocks are text. The other DB blocks give and take ids as text. Turn text into an id with new ObjectId(text), and turn an id back into text with id.toHexString() before you return it. See MongoDB ids.

Joining collections ​

The other DB blocks can't join on MongoDB. Use MongoDB's $lookup here instead. This returns each order with its rider nested under rider:

javascript
return await db.collection("orders").aggregate([
  { $match: { status: "open" } },
  { $lookup: { from: "riders", localField: "riderId", foreignField: "_id", as: "rider" } },
  { $unwind: { path: "$rider", preserveNullAndEmptyArrays: true } },
]).toArray();
  • Leave out preserveNullAndEmptyArrays to drop orders with no rider, like an inner join. With it, they stay with no rider, like a left join.
  • Both fields must hold the same type. An id saved as text never matches an ObjectId. Store ids as ObjectIds, or convert one side with $toObjectId.
  • The results keep MongoDB's own types, so ids come back as ObjectIds. Call .toHexString() on one to get text.

Released under the Apache License 2.0. Enterprise features are under the Fluxify Enterprise Edition License.