Skip to main content
The graph builder gives you full control over execution flow. You define nodes (HTTP requests, browser steps, waits, assertions) and edges (the order they run in). This is the right choice when you need branching, parallel paths, or non-linear flows. For simple linear monitors, see the Sequential Builder. For browser-based monitoring, see Browser Monitors.

Basic example

import {
  createGraphBuilder, HttpRequest, Assertion,
  GET, Json, START, END, Frequency, Assert
} from "@griffin-app/griffin";

const monitor = createGraphBuilder({
  name: "health-check",
  frequency: Frequency.every(5).minutes(),
})
  .addNode("check", HttpRequest({
    method: GET,
    base: "https://api.example.com",
    response_format: Json,
    path: "/health",
  }))
  .addEdge(START, "check")
  .addEdge("check", END)
  .build();

export default monitor;

Configuration

createGraphBuilder({
  name: "my-test",                          // Required: unique monitor name
  frequency: Frequency.every(1).minute(),   // Required: how often to run
  locations: ["us-east-1", "eu-west-1"],    // Optional: where to execute
  notifications: [                          // Optional: alert rules
    notify.onFailure().toSlack("#alerts"),
  ],
})

Adding nodes

Use addNode(name, node) to register steps. Four node types are available:

HTTP requests

import { HttpRequest, GET, POST, Json } from "@griffin-app/griffin";

builder.addNode("get-users", HttpRequest({
  method: GET,
  base: "https://api.example.com",
  response_format: Json,
  path: "/users",
}))

builder.addNode("create-user", HttpRequest({
  method: POST,
  base: "https://api.example.com",
  response_format: Json,
  path: "/users",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer token",
  },
  body: { name: "Test User", email: "test@example.com" },
}))
HttpRequest config:
FieldTypeRequiredDescription
methodGET, POST, PUT, DELETE, PATCHYesHTTP method
basestring, variable(), or templateYesBase URL (e.g., "https://api.example.com")
pathstring, variable(), or templateYesURL path (e.g., "/users")
response_formatJson, Xml, Text, NoContentYesExpected response format (NoContent for 204 with no body)
headersRecord<string, any>NoRequest headers
bodyanyNoRequest body

Browser steps

import {
  BrowserAction, navigate, waitForSelector, extractText,
} from "@griffin-app/griffin";

builder.addNode("check_page", BrowserAction({
  browser: "chromium",
  steps: [
    navigate("https://example.com"),
    waitForSelector("h1"),
    extractText("heading", "h1"),
  ],
}))
See Browser Monitors for the full step reference and BrowserAction config.

Waits

import { Wait } from "@griffin-app/griffin";

builder.addNode("pause", Wait({ seconds: 2 }))

// Also accepts:
Wait(1000)            // milliseconds
Wait({ minutes: 1 })  // minutes

Assertions

import { Assertion, Assert } from "@griffin-app/griffin";

// Create a state proxy to reference previous node results
import { createStateProxy } from "@griffin-app/griffin";
const state = createStateProxy(["get-users"]);

builder.addNode("validate", Assertion([
  Assert(state["get-users"].status).equals(200),
  Assert(state["get-users"].body["data"]).not.isEmpty(),
]))
See Assertions for the full assertion API.

Adding edges

Edges define execution order. Use addEdge(from, to) with START and END constants:
import { START, END } from "@griffin-app/griffin";

builder
  .addEdge(START, "create-user")   // Start with create
  .addEdge("create-user", "pause") // Then wait
  .addEdge("pause", "get-user")    // Then fetch
  .addEdge("get-user", "validate") // Then assert
  .addEdge("validate", END)        // Done
Rules:
  • Every graph must have at least one edge from START
  • Every graph must have at least one edge to END
  • Every node must have at least one incoming and one outgoing edge
  • The TypeScript compiler enforces these constraints — you’ll get a type error if a node is unconnected

Compile-time safety

The graph builder uses TypeScript’s type system to catch errors at compile time:
// Type error: "get-user" has no outgoing edge
builder
  .addNode("get-user", HttpRequest({ ... }))
  .addEdge(START, "get-user")
  .build()  // Error: "Some nodes have no outgoing edges"

Complete example

import {
  createGraphBuilder, HttpRequest, Wait, Assertion,
  GET, POST, DELETE, Json, START, END,
  Frequency, Assert, createStateProxy, secret
} from "@griffin-app/griffin";

const state = createStateProxy(["create", "get", "delete"]);

const monitor = createGraphBuilder({
  name: "user-crud",
  frequency: Frequency.every(15).minutes(),
})
  // Define steps
  .addNode("create", HttpRequest({
    method: POST,
    base: "https://api.example.com",
    response_format: Json,
    path: "/users",
    headers: { "Authorization": secret("API_KEY") },
    body: { name: "Test User" },
  }))
  .addNode("pause", Wait({ seconds: 1 }))
  .addNode("get", HttpRequest({
    method: GET,
    base: "https://api.example.com",
    response_format: Json,
    path: "/users/1",
  }))
  .addNode("validate", Assertion([
    Assert(state["create"].status).equals(201),
    Assert(state["get"].status).equals(200),
    Assert(state["get"].body["name"]).equals("Test User"),
  ]))
  .addNode("delete", HttpRequest({
    method: DELETE,
    base: "https://api.example.com",
    response_format: Json,
    path: "/users/1",
  }))
  // Define flow
  .addEdge(START, "create")
  .addEdge("create", "pause")
  .addEdge("pause", "get")
  .addEdge("get", "validate")
  .addEdge("validate", "delete")
  .addEdge("delete", END)
  .build();

export default monitor;