Use from Python, Go, Java, and other languages
sfn-diagram is written in TypeScript, but generating a diagram is a build-time or CI-time task, not something your application does at runtime. That makes the CLI the natural boundary: any language that can spawn a process can render a diagram, and you never need to write JavaScript.
If you use the AWS CDK, you already have Node
Section titled “If you use the AWS CDK, you already have Node”The CDK ships in Python, Java, C#, and Go through jsii, and every one of those bindings requires Node.js at synth time. So in a CDK project of any language, this already works:
npx sfn-diagram cdk.out/MyStack.template.json --resource MyStateMachine -o diagram.svgSee the CLI guide for the full option list, including CloudFormation and SAM template input.
No Node? Install a standalone binary
Section titled “No Node? Install a standalone binary”Each release attaches self-contained executables for Linux (x64, arm64), macOS (Apple
silicon, Intel), and Windows (x64), plus a SHA256SUMS file. They bundle the CLI and
its runtime in one file, so nothing else needs to be installed.
Homebrew (macOS and Linux):
brew install yusufaf/tap/sfn-diagramDirect download (substitute your platform; see the releases page for the full list):
curl -fsSL -o sfn-diagram \ https://github.com/yusufaf/sfn-diagram/releases/latest/download/sfn-diagram-linux-x64chmod +x sfn-diagram./sfn-diagram --versionOn Windows, download sfn-diagram-windows-x64.exe.
The binaries render SVG, Mermaid, and HTML. --format png needs the native
rasterizer and is not available in the standalone build; use the
Docker image, which ships the rasterizer, or the npm package with
@resvg/resvg-js installed.
Docker
Section titled “Docker”The Docker image is the other zero-install option, and the only one that supports PNG. It reads from a mounted directory or from stdin:
docker run --rm -i ghcr.io/yusufaf/sfn-diagram:latest - --format svg \ < state.asl.json > diagram.svgCalling the CLI from your language
Section titled “Calling the CLI from your language”Pass - as the input to read the definition from stdin; the diagram is written to
stdout. Every option from the CLI guide applies.
Python
Section titled “Python”import jsonimport subprocess
def render_svg(definition: dict, layout: str = "TB") -> str: result = subprocess.run( ["sfn-diagram", "-", "--format", "svg", "--layout", layout], input=json.dumps(definition), capture_output=True, check=True, text=True, ) return result.stdoutSwap "sfn-diagram" for ["npx", "sfn-diagram"] in a CDK project, or for
["docker", "run", "--rm", "-i", "ghcr.io/yusufaf/sfn-diagram:latest"] if you prefer
the image.
package sfndiagram
import ( "bytes" "fmt" "os/exec")
func RenderSVG(definition []byte) ([]byte, error) { cmd := exec.Command("sfn-diagram", "-", "--format", "svg") cmd.Stdin = bytes.NewReader(definition) var stderr bytes.Buffer cmd.Stderr = &stderr out, err := cmd.Output() if err != nil { return nil, fmt.Errorf("sfn-diagram: %w: %s", err, stderr.String()) } return out, nil}Process process = new ProcessBuilder("sfn-diagram", "-", "--format", "svg") .redirectError(ProcessBuilder.Redirect.INHERIT) .start();try (var stdin = process.getOutputStream()) { stdin.write(definitionJson.getBytes(StandardCharsets.UTF_8));}String svg = new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8);if (process.waitFor() != 0) { throw new IllegalStateException("sfn-diagram failed");}Pull requests and CI
Section titled “Pull requests and CI”For diagrams on pull requests you do not need any of the above: the GitHub Action and the GitLab CI integration run the CLI for you, regardless of the language the rest of the repository is written in.
Why not a native port?
Section titled “Why not a native port?”We looked at shipping native Python, Go, and Rust libraries and decided against it for now. The renderer depends on the exact node ranking and ordering of the dagre layout engine, and a port that does not reproduce it bit-for-bit produces different diagrams from the same definition. Each port would also be a separate product to keep in sync. If you need an in-process library outside Node, open an issue describing the use case; the leading candidate is a WebAssembly build of the existing core rather than a rewrite.