Skip to content

JavaScript API Reference ​

This is the API exposed to JS Runner, Transformer, and js: expressions. Fluxify's DAG compiler emits scripts into the generated Bun route handler.

Every script runs inside an async function: await works anywhere, and return sends a value to the next block. Only jwt is built in as a library. Install anything else under Project Settings > npm Packages and import it.

Where each API is available ​

APIRouteWorkflowCustom blockOnly in
input, outputs, trigger, logger, jwt, httpClient, getConfig, ValidationErrorYesYesYes
getRequestBody()Request bodyRun payloadCaller's
getQueryParam, getRouteParam, getHeader, getCookie, httpRequestMethod, httpRequestRouteRequest valuesEmptyCaller's
setHeader, setCookieYesNo effectCaller's
getResponseBody(), getResponseStatus()nullnullnullAfter middlewares
paramsNoNoYesCustom blocks
abortSignalSync repliesundefinedCaller's
dbQueryNoNoNoDB Native block (PostgreSQL, MySQL)
db, ObjectIdNoNoNoDB Native block (MongoDB)
kvNoNoNoKV Raw Connection block
testsuiteNoNoNoTest-only custom blocks

"Caller's" means the route or workflow that uses the block. More in Where Your Code Runs.

Scripts in an after middleware

In a block that runs after the route, the first input is the route's reply, { httpCode, body }, not the body itself. Work on input.body, or read the reply with getResponseBody() and getResponseStatus(), which keep it even after input has been reshaped.

Request values ​

typescript
const input: any;
const httpRequestMethod: string;
const httpRequestRoute: string;

function getQueryParam(key: string): string | undefined;
function getRouteParam(key: string): string;
function getHeader(key: string): string;
function getCookie(key: string): string;
function getRequestBody(): any;
APIParametersReturnsDescription
input—anyOutput from the preceding block.
httpRequestMethod—stringIncoming method, for example "GET".
httpRequestRoute—stringIncoming request path.
getQueryParamkey: string`stringundefined`
getRouteParamkey: stringstringNamed route parameter, or "" if absent.
getHeaderkey: stringstringCase-insensitive request header, or "" if absent.
getCookiekey: stringstringRequest cookie, or "" if absent.
getRequestBody—anyParsed request body, or null when there is none. In a workflow, the payload the run was given.
javascript
const id = getRouteParam("id");
const page = Number(getQueryParam("page") || 1);
return { id, page, body: getRequestBody() };

Response helpers ​

typescript
type CookieSameSite = "Strict" | "Lax" | "None";

interface CookieOptions {
  value: string | number;
  domain?: string;
  path?: string;
  expiry?: string | Date;
  httpOnly?: boolean;
  secure?: boolean;
  samesite?: CookieSameSite;
}

function setHeader(key: string, value: string): void;
function setCookie(name: string, options: CookieOptions): void;
APIParametersReturnsDescription
setHeaderkey: string, value: stringvoidAdds an outgoing response header.
setCookiename: string, options: CookieOptionsvoidAdds an outgoing cookie. samesite defaults to "Strict".

Response values ​

Only useful in an after middleware, where a reply already exists. Everywhere else both return null.

typescript
function getResponseBody(): any;
function getResponseStatus(): number | null;
APIParametersReturnsDescription
getResponseBody—anyThe body of the reply the after middlewares started from: the route's, or a before middleware's if one answered.
getResponseStatus—number | nullThat reply's status code.

Both stay the same for the whole after chain, so they still work after an earlier block replaced input:

javascript
// an earlier step returned something else; the reply is not lost
if (getResponseStatus() >= 400) return getResponseBody();
return { data: getResponseBody(), servedAt: new Date().toISOString() };

Configuration and state ​

typescript
function getConfig(key: string): string | number | boolean | undefined;

getConfig returns a project App Config value for key, or undefined when it is not configured. Values assigned in scripts are available to later blocks in the same request only:

javascript
currentUserId = input.id;
const secret = getConfig("JWT_SECRET");

Saved block outputs ​

typescript
const outputs: Record<string, any>;

When a block has Save output to variable turned on, its output is stored in outputs under the name you chose. Any later block in the same request can read it. outputs starts empty on every request, so values never carry over to the next one.

javascript
// "Fetch Users" block saves its output as "users"
return outputs.users.filter((user) => user.active);

outputs is created when the first block saves something. If a block might run before any save, read it as outputs?.users.

Trigger ​

typescript
interface TriggerEvent {
  data: any;
  meta: { id?: string; receivedAt?: string; source?: string; [key: string]: any };
}

const trigger: {
  kind: "route" | "job" | "workflow" | "cron" | "trigger";
  source: string;
  reply: "sync" | "async";
  id?: string;
  data: TriggerEvent[];
  meta: { batchId: string; size: number; firstReceivedAt?: string; lastReceivedAt?: string; attempt?: number };
  connection?: {
    raw: any;
    commit(): Promise<void>;
    moveToDLQ(error?: any): Promise<void>;
    lag(): Promise<number | null>;
  };
};
APIReturnsDescription
trigger.kindstring"route" for an HTTP request, "trigger" for a workflow run, "job" for a queued custom block. "workflow" and "cron" are reserved.
trigger.sourcestringWhere the work came from: "http", "internal", "schedule", "kafka", "nats" and so on.
trigger.reply"sync" | "async"Whether the caller waits for the answer.
trigger.idstring | undefinedCorrelation id, when there is one.
trigger.dataTriggerEvent[]The events of the run. Always a list, even for one event. Workflows only.
trigger.metaobjectBatch details. size is how many events. Workflows only.
trigger.connectionobject | undefinedA queue trigger in manual commit mode (Kafka, NATS, SQS, Redis Streams, RabbitMQ), or a plugin trigger whose author passed a connection. commit() marks the batch done, moveToDLQ() parks it, lag() counts what is waiting, and raw is the underlying client. Use raw with care; see Using the client directly.
javascript
if (trigger.source === "schedule") {
  logger.logInfo("Nightly run");
}
return trigger.data.map((event) => event.data);

See Triggers for batching, and input for the one-event shortcut.

Custom block parameters ​

typescript
const params: Record<string, any>; // the settings filled in where the block is used

params exists only inside a custom block. Each setting is a key, for example params.webhook_url. An App Config selector holds the key name, so read the value with getConfig(params.key_name).

Errors ​

typescript
class ValidationError extends Error {
  constructor(payload: any);
}

Throw ValidationError from a Use JavaScript request validator to fail validation with your own payload, returned to the caller in errors[].errors. See Routing.

JWT ​

jwt is available globally and uses jsonwebtoken under the hood.

typescript
const jwt: {
  sign(payload: object, secretKey: string, options?: object): string;
  verify(token: string, secretKey: string, options?: object): {
    success: boolean;
    payload: Record<string, string> | null;
  };
  decode(token: string, options?: object): Record<string, string> | null;
};
APIParametersReturnsDescription
jwt.signpayload: object, secretKey: string, options?: objectstringSigns and returns a JWT. Options follow jsonwebtoken sign options.
jwt.verifytoken: string, secretKey: string, options?: object{ success: boolean; payload: Record<string, string> | null }Verifies a token; invalid tokens return success: false rather than throwing.
jwt.decodetoken: string, options?: objectRecord<string, string> | nullDecodes a token without signature verification.

HTTP client ​

typescript
type HttpHeaders = Record<string, string>;
interface AxiosResponse<T = any> {
  data: T;
  status: number;
  statusText: string;
  headers: any;
  config: any;
}

const httpClient: {
  get<T = any>(url: string, headers?: HttpHeaders): Promise<AxiosResponse<T>>;
  post<T = any>(url: string, data?: any, headers?: HttpHeaders): Promise<AxiosResponse<T>>;
  put<T = any>(url: string, data?: any, headers?: HttpHeaders): Promise<AxiosResponse<T>>;
  delete<T = any>(url: string, headers?: HttpHeaders): Promise<AxiosResponse<T>>;
  patch<T = any>(url: string, data?: any, headers?: HttpHeaders): Promise<AxiosResponse<T>>;
};

The promise rejects on a non-2xx answer or a network error. Pass a full URL.

MethodParametersReturns
get<T>url: string, headers?: HttpHeadersPromise<AxiosResponse<T>>
post<T>url: string, data?: any, headers?: HttpHeadersPromise<AxiosResponse<T>>
put<T>url: string, data?: any, headers?: HttpHeadersPromise<AxiosResponse<T>>
delete<T>url: string, headers?: HttpHeadersPromise<AxiosResponse<T>>
patch<T>url: string, data?: any, headers?: HttpHeadersPromise<AxiosResponse<T>>

Cancellation ​

typescript
const abortSignal: AbortSignal | undefined;

abortSignal aborts when the caller disconnects before the route answers. Once it fires, there is no one left to receive the answer, so the run stops using resources:

  • Database, key-value, message queue, httpClient and AI calls stop by themselves and throw.
  • Anything else you start yourself keeps going unless you pass it the signal.
javascript
const res = await fetch("https://api.example.com/report", { signal: abortSignal });
return res.json();

It is undefined when nothing can cancel the run: on a route with an async reply and in a workflow.

INFO

Code that never waits (a long loop with no await) cannot be interrupted. It runs until it ends or the route's timeout stops it.

Logging ​

typescript
const logger: {
  logInfo(value: any): void;
  logWarn(value: any): void;
  logError(value: any): void;
};
APIParametersReturnsDescription
logger.logInfovalue: anyvoidWrites an informational log entry.
logger.logWarnvalue: anyvoidWrites a warning log entry.
logger.logErrorvalue: anyvoidWrites an error log entry.

DB Native only ​

typescript
function dbQuery(query: string, params?: unknown[]): Promise<Record<string, unknown>[]>; // PostgreSQL, MySQL
const db: Db;             // MongoDB: the driver's database
const ObjectId: ObjectId; // MongoDB: builds ids
APIParametersReturnsDescription
dbQueryquery: string, params?: unknown[]Promise<rows[]>Runs a SQL query and returns the rows. Put values in params and use $1, $2 placeholders (MySQL also takes ?). PostgreSQL and MySQL only. On MongoDB, a query throws and dbQuery() with nothing returns db.
dbnoneDbMongoDB only: the database, from the official MongoDB driver. Inside a transaction, every collection call joins it.
ObjectIdid?: stringObjectIdMongoDB only: new ObjectId(id) turns a string into an id you can query _id with.

KV Raw Connection only ​

typescript
const kv: any; // ioredis client for Redis, the memcached client for Memcached

kv is the raw client of the selected connection, so use that client's own methods, for example await kv.incr("hits"). Available only in KV Raw Connection blocks. See KV Raw Connection.

Test-only custom blocks ​

typescript
const testsuite: {
  phase: "setup" | "teardown";
  runId: string;
  suite: { id: string; name: string };
  setup?: any;    // teardown only: what setup returned
  outcome?: "passed" | "failed" | "error" | "timeout"; // teardown only
  request?: any;  // teardown only: what the suite sent
  response?: any; // teardown only: what came back, or null if nothing did
};

testsuite exists only in a custom block whose Used for is Test setup & teardown. See Setup and Teardown. Test hooks and checks use t and fluxify instead, see Hooks.

Import rules ​

Static imports are discovered by the AST parser, deduplicated per route at load time, and reused across requests. Workers execute minified generated JavaScript, so avoid importing names that collide with context globals such as input, jwt, logger, or httpClient. See Imports & Libraries.

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