Built-in Actions
run runs a shell command. For common jobs such as calling an API, transforming data, rendering a file, querying a database, or starting a container, a step can use a built-in action instead, with no scripts or extra tools to install.
This lesson chains three actions: fetch a list of todos, summarize it in JavaScript, and render a report.
The workflow
curl -fsSLO https://docs.dagu.sh/tutorial/dags/tutorial-04-actions.yaml# Dagu tutorial, lesson 4: https://docs.dagu.sh/tutorial/actions
description: Call an API, transform the data in JavaScript, and render a report
type: graph
steps:
- id: fetch_todos
action: http.request
with:
method: GET
url: https://jsonplaceholder.typicode.com/todos?userId=1
silent: true
output:
todos:
from: stdout
- id: summarize
action: js.run
with:
input: ${steps.fetch_todos.outputs.todos}
script: |
const open = input.filter((todo) => !todo.completed);
return { open: open.length, next: open[0].title };
output:
open:
from: stdout
decode: json
select: .open
next:
from: stdout
decode: json
select: .next
depends: fetch_todos
- id: write_report
action: template.render
with:
data:
open: ${steps.summarize.outputs.open}
next: ${steps.summarize.outputs.next}
template: |
# Todo report
- Still open: {{ .open }}
- Next up: {{ .next }}
stdout:
artifact: report.md
depends: summarizeactionpicks the built-in action.withholds its settings.http.requestcalls a URL and prints the response body.outputpublishes it astodos.js.runruns JavaScript in a sandbox built into Dagu, so Node.js does not need to be installed.inputbecomes the script'sinputvariable, with JSON already parsed, and the value the script returns is the step's output. Withdecode: json, eachselectpicks one field of it.template.renderfills a template withdata.stdout: {artifact: report.md}saves what the step prints as a file attached to the run.
This lesson calls jsonplaceholder.typicode.com, a free test API, so it needs internet access.
Watch it run
# Todo report - Still open: 9 - Next up: delectus aut autem
Select summarize to see the object the script returned and the two fields picked out of it.
Run it yourself
dagu start tutorial-04-actions.yamlIn the Web UI, open the run, then Activity → Artifacts to read report.md.
When to use js.run
Use js.run to filter, reshape, or compute values between steps, instead of python -c, jq, or awk in a run step. Dagu checks the script for syntax errors when it validates the workflow, before anything runs.
The sandbox has no network, files, environment variables, or npm packages. Keep that work in run steps or other actions, and pass the results in through input.
Find more actions
| Action | Use it to |
|---|---|
http.request | Call an HTTP API |
js.run | Transform data with JavaScript |
template.render | Generate a text file from a template |
file.read, file.write | Read and write local files |
jq.filter | Pick values out of JSON with a jq filter |
docker.run | Run a step in a container |
ssh.run | Run a command on another machine |
postgres.query | Query a database |
mail.send | Send an email |
dag.run | Run another workflow. See lesson 7 |
Every action has a reference page under Actions in the sidebar. For ready-made patterns in the terminal, run dagu example.
Try it
- Change
userId=1touserId=2in the URL and run again. What doesreport.mdsay now? - Delete the closing
}from thereturnline insummarize, then rundagu validate tutorial-04-actions.yaml. What happens? - Change the URL to
https://jsonplaceholder.typicode.com/todos/9999, which does not exist. What happens?
Answers
Still open: 12andNext up: suscipit repellat esse quibusdam voluptatem incidunt.- Validation fails with a JavaScript
SyntaxErrorthat names the line, and nothing runs. - The API answers
404, sofetch_todosfails and the steps after it never run.http.requestfails on any status outside 200–299.

