Files
wehub-resource-sync a9cd7750f4
CI / unit-test (push) Has been cancelled
CI / detect-changes (push) Has been cancelled
CI / build (push) Has been cancelled
Publish docs via GitHub Pages / Deploy docs (push) Has been cancelled
CI / test-harness (push) Has been cancelled
CI / generate-e2e-matrix (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / build-ui (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
UI v2 Integration CI / E2E (Integration) (push) Has been cancelled
UI v2 CI / Lint, Format & Test (push) Has been cancelled
UI v2 CI / E2E (Mocked) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:37:56 +08:00

414 lines
13 KiB
Markdown

---
description: Run your first Conductor workflow in 2 minutes. Call an API, parse the response with server-side JavaScript, and see durable execution in action — no workers needed.
---
# Run Your First Workflow
**See a workflow execute in 2 minutes. Build your own in 5.**
You need [Node.js](https://nodejs.org/) (v16+) and Java 21+ installed. That's it.
## Phase 1: See it work
> **Prerequisite:** Java 21+ is required to run the Conductor server. Run `java --version` to check. Install Java 21 if needed.
### Start Conductor
```bash
npm install -g @conductor-oss/conductor-cli
conductor server start
```
Wait for the server to start, then open the UI at [http://localhost:8080](http://localhost:8080).
!!! note "Troubleshooting"
- **"Java not found" or server won't start?** Install Java 21+ and make sure `java -version` shows 21 or higher.
- **Port 8080 already in use?** Start on a different port: `conductor server start --port 9090`
- **Prefer Docker?** Skip the CLI server and run: `docker run -p 8080:8080 conductoross/conductor:latest`
### Define the workflow
Save `workflow.json` — a two-task workflow that calls an API and parses the response, all server-side:
```json
{
"name": "hello_workflow",
"version": 1,
"tasks": [
{
"name": "fetch_data",
"taskReferenceName": "fetch_ref",
"type": "HTTP",
"inputParameters": {
"http_request": {
"uri": "https://orkes-api-tester.orkesconductor.com/api",
"method": "GET"
}
}
},
{
"name": "parse_response",
"taskReferenceName": "parse_ref",
"type": "INLINE",
"inputParameters": {
"data": "${fetch_ref.output.response.body}",
"evaluatorType": "graaljs",
"expression": "(function() { var d = $.data; return { summary: 'Host ' + d.hostName + ' responded in ' + d.apiRandomDelay + ' with random value ' + d.randomInt, host: d.hostName, randomValue: d.randomInt }; })()"
}
}
],
"outputParameters": {
"summary": "${parse_ref.output.result.summary}",
"apiResponse": "${fetch_ref.output.response.body}"
},
"schemaVersion": 2,
"ownerEmail": "dev@example.com"
}
```
**What's happening here:**
- **`fetch_data`** — an [HTTP task](../documentation/configuration/workflowdef/systemtasks/http-task.md) that calls an external API. No worker needed.
- **`parse_response`** — an [Inline task](../documentation/configuration/workflowdef/systemtasks/inline-task.md) that runs JavaScript server-side to extract and summarize the API response.
- Both are **system tasks** — Conductor executes them directly. No external code to deploy.
### Register and run
**Register the workflow:**
```bash
conductor workflow create workflow.json
```
**Start the workflow:**
```bash
conductor workflow start -w hello_workflow --sync
```
The `--sync` flag waits for completion and prints the full workflow execution JSON to stdout (server detection messages go to stderr).
To extract just the output in a readable form, pipe through `jq`:
```bash
conductor workflow start -w hello_workflow --sync 2>/dev/null | jq '.output'
```
```json
{
"summary": "Host orkes-api-sampler-... responded in 0 ms with random value 1141",
"apiResponse": {
"randomString": "gbgkaofnvesptvlmocpk",
"randomInt": 1141,
"hostName": "orkes-api-sampler-...",
"apiRandomDelay": "0 ms",
"sleepFor": "0 ms",
"statusCode": "200",
"queryParams": {}
}
}
```
Open [http://localhost:8080](http://localhost:8080) to see the execution visually — the task timeline, inputs/outputs, and status of each step.
!!! success "What just happened"
Conductor called an external API, passed the response to server-side JavaScript for parsing, tracked every step, and would have retried on failure — all without writing or deploying any worker code.
## Phase 2: Add a worker
Now write real code that Conductor orchestrates — with automatic retries.
### Update the workflow
Save `workflow-v2.json` — adds a worker task that processes the parsed data:
```json
{
"name": "hello_workflow",
"version": 2,
"tasks": [
{
"name": "fetch_data",
"taskReferenceName": "fetch_ref",
"type": "HTTP",
"inputParameters": {
"http_request": {
"uri": "https://orkes-api-tester.orkesconductor.com/api",
"method": "GET"
}
}
},
{
"name": "parse_response",
"taskReferenceName": "parse_ref",
"type": "INLINE",
"inputParameters": {
"data": "${fetch_ref.output.response.body}",
"evaluatorType": "graaljs",
"expression": "(function() { var d = $.data; return { summary: 'Host ' + d.hostName + ' responded in ' + d.apiRandomDelay + ' with random value ' + d.randomInt, host: d.hostName, randomValue: d.randomInt }; })()"
}
},
{
"name": "process_result",
"taskReferenceName": "process_ref",
"type": "SIMPLE",
"inputParameters": {
"summary": "${parse_ref.output.result.summary}",
"randomValue": "${parse_ref.output.result.randomValue}"
}
}
],
"outputParameters": {
"finalResult": "${process_ref.output.result}"
},
"schemaVersion": 2,
"ownerEmail": "dev@example.com"
}
```
**Register the updated workflow and task definition:**
```bash
conductor workflow create workflow-v2.json
```
```bash
curl -X POST http://localhost:8080/api/metadata/taskdefs \
-H 'Content-Type: application/json' \
-d '[{
"name": "process_result",
"retryCount": 2,
"retryLogic": "FIXED",
"retryDelaySeconds": 1,
"responseTimeoutSeconds": 10,
"ownerEmail": "dev@example.com"
}]'
```
### Write the worker
Save `worker.py`:
```python
import threading
from conductor.client.automator.task_handler import TaskHandler
from conductor.client.configuration.configuration import Configuration
from conductor.client.worker.worker_task import worker_task
@worker_task(task_definition_name="process_result")
def process_result(task) -> dict:
summary = task.input_data.get("summary", "")
random_value = task.input_data.get("randomValue", 0)
# Fail on first attempt to demonstrate retries
if task.retry_count == 0:
raise Exception(f"Simulated failure processing: {summary}")
return {
"result": summary.upper(),
"doubled": random_value * 2,
"attempt": task.retry_count + 1,
}
def main():
config = Configuration(server_api_url="http://localhost:8080/api")
handler = TaskHandler(configuration=config)
handler.start_processes()
try:
threading.Event().wait() # block until KeyboardInterrupt; no busy-wait
except KeyboardInterrupt:
handler.stop_processes()
if __name__ == "__main__":
main()
```
**Install and run:**
```bash
pip install conductor-python
python worker.py
```
### Start the workflow and watch retries
In a separate terminal:
```bash
conductor workflow start -w hello_workflow --version 2 --sync
```
In the terminal running your worker, you'll see:
```
Simulated failure processing: Host orkes-api-sampler-... responded in 0 ms with random value 1141
...
# 1 second later, the retry succeeds
```
Expected output:
```json
{
"finalResult": {
"result": "HOST ORKES-API-SAMPLER-... RESPONDED IN 0 MS WITH RANDOM VALUE 1141",
"doubled": 2282,
"attempt": 2
}
}
```
Open [http://localhost:8080](http://localhost:8080) to see the retry visually in the execution diagram.
!!! success "What just happened"
Your worker failed, Conductor retried it after 1 second, and the retry succeeded. This is durable execution — Conductor manages retries so your code doesn't have to.
## Phase 3: Replay a workflow
Every Conductor workflow execution is fully replayable — restart from the beginning, rerun from a specific task, or retry the failed step. This works on any workflow, at any time, even months after the original execution.
### Restart from the beginning
Take any workflow execution ID from Phase 1 or Phase 2 and restart it:
```bash
# Start a workflow and capture its ID (printed as a plain UUID)
WORKFLOW_ID=$(conductor workflow start -w hello_workflow --version 2)
# Restart the entire workflow from the beginning
curl -X POST "http://localhost:8080/api/workflow/$WORKFLOW_ID/restart"
```
The workflow re-executes all tasks from scratch, creating a new execution trace while preserving the original.
### Retry from the failed task
If a workflow failed (like the simulated failure in Phase 2), you can retry just the failed task instead of re-running everything:
```bash
# Retry from the last failed task
curl -X POST "http://localhost:8080/api/workflow/$WORKFLOW_ID/retry"
```
Conductor picks up from the failed task, reusing the outputs of all previously completed tasks.
!!! success "What just happened"
You replayed a workflow execution using two different strategies — full restart and retry from failure. Conductor preserved the full execution history, so you could replay at any time. This works on completed, failed, or timed-out workflows, indefinitely.
??? note "Workers in other languages"
=== "Java"
```java
@WorkerTask("process_result")
public Map<String, Object> processResult(Map<String, Object> input) {
String summary = (String) input.get("summary");
int randomValue = (int) input.get("randomValue");
return Map.of(
"result", summary.toUpperCase(),
"doubled", randomValue * 2
);
}
```
See the [Java SDK](https://github.com/conductor-oss/java-sdk) for full setup.
=== "JavaScript"
```bash
npm install @io-orkes/conductor-javascript
```
```javascript
const { OrkesClients, TaskHandler } = require("@io-orkes/conductor-javascript");
async function main() {
const clients = await OrkesClients.from({ serverUrl: "http://localhost:8080/api" });
const handler = new TaskHandler(clients, [{
taskDefName: "process_result",
execute: async (task) => {
const { summary, randomValue } = task.inputData;
return { outputData: { result: summary.toUpperCase(), doubled: randomValue * 2 }, status: "COMPLETED" };
},
}]);
handler.startPolling();
}
main();
```
See the [JavaScript SDK](https://github.com/conductor-oss/javascript-sdk) for full setup.
=== "Go"
```go
func ProcessResult(task *model.Task) (interface{}, error) {
summary := task.InputData["summary"].(string)
randomValue := int(task.InputData["randomValue"].(float64))
return map[string]interface{}{
"result": strings.ToUpper(summary),
"doubled": randomValue * 2,
}, nil
}
```
See the [Go SDK](https://github.com/conductor-oss/go-sdk) for full setup.
=== "C#"
```csharp
[WorkerTask("process_result")]
public static TaskResult ProcessResult(Task task)
{
var summary = task.InputData["summary"].ToString();
var randomValue = (int)task.InputData["randomValue"];
return task.Completed(new {
result = summary.ToUpper(),
doubled = randomValue * 2
});
}
```
See the [C# SDK](https://github.com/conductor-oss/csharp-sdk) for full setup.
## Cleanup
```bash
conductor server stop
```
## Using Docker instead
If you prefer Docker over the CLI, you can run Conductor with:
```bash
docker run --name conductor -p 8080:8080 conductoross/conductor:latest
```
All the workflow commands above work the same — just replace the CLI commands with their cURL equivalents:
| CLI | cURL |
|-----|------|
| `conductor workflow create workflow.json` | `curl -X POST http://localhost:8080/api/metadata/workflow -H 'Content-Type: application/json' -d @workflow.json` |
| `conductor workflow start -w hello_workflow --sync` | `curl -s -X POST "http://localhost:8080/api/workflow/execute/hello_workflow/1?waitForSeconds=10" -H 'Content-Type: application/json' -d '{}'` |
| `conductor server stop` | `docker rm -f conductor` |
For production deployment options, see [Running with Docker](../devguide/running/deploy.md).
## Next steps
- **[System tasks](../documentation/configuration/workflowdef/systemtasks/index.md)** — HTTP, Wait, Event tasks without workers
- **[Operators](../documentation/configuration/workflowdef/operators/index.md)** — Fork/join, switch, loops, sub-workflows
- **[Error handling](../devguide/how-tos/Workflows/handling-errors.md)** — Saga pattern, compensation flows
- **[Client SDKs](../documentation/clientsdks/index.md)** — Java, Python, Go, C#, JavaScript, and more