This page define the format of the JSON file used to describe a workflow in MetroFlow. The file describes a set of nodes (steps), the edges (connections between steps), and optional subworkflows (groups of steps).
The JSON file must be a dictionnary with the following top-level keys:
| Key | Type | Required | Description |
|---|---|---|---|
nodes | Array | Required | List of all steps in the workflow. |
edges | Array | Required | List of connections between steps. |
subworkflows | Dictionnary | Optional | Named groups of nodes for visual organisation. |
Each entry in the nodes array represents a single step in the workflow.
| Field | Type | Required | Description |
|---|---|---|---|
id | String | Required | Unique identifier for the node. Used to reference it in edges and subworkflows. |
name | String | Required | Display name of the step. |
position | Dictionnary | Required | Visual position on the canvas. Must contain x and y as string numbers. |
code | String | Optional | The process code associated with this step. |
condition | String | Optional | Condition under which the step is executed, e.g. params.runStep1 == true. |
file_ref | String | Optional | Path to the source file for this step, e.g. (modules/step1.nf). |
Right-clicking on a step shows its code and condition, with its file_ref.
Node IDs follow a hierarchical naming convention using . as a separator between the subworkflow it is conatined in and the node name:
SUBWORKFLOW.nodeName
If the node belongs to multiple subworkflows, the subworkflow names are included:
SUBWORKFLOW1.SUBWORKFLOW2.nodeName
Each entry in the edges array defines a directed connection from one node to another.
| Field | Type | Required | Description |
|---|---|---|---|
id | String | Required | Unique identifier for the edge, typically A -> B. |
A | String | Required | ID of the source node. |
B | String | Required | ID of the destination node. |
condition | String | Optional | Condition under which the source node is executed and the path is taken, e.g. params.runStep1 == true. |
Subworkflows are optional and allow you to visually group nodes together. The subworkflows key is an dictionnary where each key is a subworkflow ID.
| Field | Type | Required | Description |
|---|---|---|---|
nodes | Array of strings | Required | List of node IDs that belong to this group. |
label | String | Required | Display name for the group. |
code | String | Optional | The code of the subworkflow. |
condition | String | Optional | Condition under which the subworkflow is executed, e.g. params.runSubworkflow == true. |
file_ref | String | Optional | Path to the source file of the subworkflow, e.g. (subworkflows/my_subworkflow.nf). |
The nodes list contains all the nodes of the subworkflow, including those of the subworkflows nested in it.
Once a subworkflow is collapsed (double-click on it), right-clicking on it shows its code, its condition
and its content (the subworkflows and steps it contains), with its file_ref.
Colors are computed automatically by Metroflow, any color field present in the JSON is ignored.
Edges sharing the same condition are drawn with the same color (there is no limit on the number of colors),
and subworkflows get a background which gets darker the deeper they are nested.
Different color palettes can be selected from the map controls.
The following is a simple 3-step workflow: load data → process data → export results.
{
"nodes": [
{
"id": "MY_SUBWORKFLOW.loadData",
"name": "loadData",
"position": { "x": "100", "y": "150" },
"code": "process loadData {\n output:\n path \"data.csv\"\n script:\n \"\"\"\n cp /source/data.csv data.csv\n \"\"\"\n}",
"condition": "params.loadData == true",
"file_ref": "(modules/loadData.nf)"
},
{
"id": "MY_SUBWORKFLOW.processData",
"name": "processData",
"position": { "x": "300", "y": "150" },
"code": "process processData {\n input:\n path data\n output:\n path \"processed.csv\"\n script:\n \"\"\"\n python process.py ${data}\n \"\"\"\n}",
"condition": "params.processData == true",
"file_ref": "(modules/processData.nf)"
},
{
"id": "MY_SUBWORKFLOW.exportResults",
"name": "exportResults",
"position": { "x": "500", "y": "150" },
"code": "process exportResults {\n input:\n path processed\n output:\n path \"results.zip\"\n script:\n \"\"\"\n zip results.zip ${processed}\n \"\"\"\n}",
"condition": "params.exportResults == true",
"file_ref": "(modules/exportResults.nf)"
}
],
"edges": [
{
"id": "MY_SUBWORKFLOW.loadData -> MY_SUBWORKFLOW.processData",
"A": "MY_SUBWORKFLOW.loadData",
"B": "MY_SUBWORKFLOW.processData",
"condition": "params.loadData == true"
},
{
"id": "MY_SUBWORKFLOW.processData -> MY_SUBWORKFLOW.exportResults",
"A": "MY_SUBWORKFLOW.processData",
"B": "MY_SUBWORKFLOW.exportResults",
"condition": "params.processData == true"
}
],
"subworkflows": {
"MY_SUBWORKFLOW": {
"nodes": [
"MY_SUBWORKFLOW.loadData",
"MY_SUBWORKFLOW.processData",
"MY_SUBWORKFLOW.exportResults"
],
"label": "MY_SUBWORKFLOW",
"code": "workflow MY_SUBWORKFLOW {\n data = loadData()\n processed = processData(data)\n exportResults(processed)\n}",
"condition": "params.runPipeline == true",
"file_ref": "(subworkflows/my_subworkflow.nf)"
}
}
}