SWIRLS_
Language

Context and Type Safety

How the context object works in TypeScript blocks and how the LSP provides type safety.

Every ts block in a .swirls file has a typed context object in scope. The Swirls LSP generates TypeScript types from your schemas so you get autocomplete and type checking without leaving the .swirls file.

context.nodes

  • context.nodes.root.input: The graph trigger payload (form, webhook, schedule, etc.), typed from the root node's inputSchema. Use context.nodes.root.input.email, context.nodes.root.input.name, etc.
  • context.nodes.root.output: After the root node runs, its return value (typed from the root node's outputSchema). Downstream nodes read normalized or derived fields here.
  • context.nodes.<nodeName>.output: Output of an upstream node, typed from that node's outputSchema. For example: context.nodes.entry.output.email.

Downstream nodes do not define inputSchema in the DSL. Their input is derived from upstream output schemas automatically.

context.iteration

Inside type: map and type: while child graphs, and in those nodes’ items, condition, and update @ts blocks, context.iteration is in scope:

FieldWhereDescription
itemmapCurrent element from items (typed from the subgraph root inputSchema).
indexmap, whileZero-based iteration index.
totalmapTotal item count for this map run.
inputwhileThreaded state for this iteration (typed from root inputSchema).
previousmap, whilePrior iteration’s leaf outputs (shape keyed by leaf node names).

See Node types (map and while) and Graphs.

context.reviews

Available when upstream nodes have review enabled. Keyed by node name.

  • context.reviews.<nodeName>: The review form output from a reviewed node.
  • The current node can also read its own review data: context.reviews.<currentNodeName>.
  • When a review node defines a schema, the LSP types the review data from that schema.

See Reviews for details.

context.secrets

Project secrets available to this node’s TypeScript blocks. Declare secret blocks, wire secrets: { … } on nodes, and read context.secrets.<blockName>.<VAR>. Full reference: Secrets and auth.

Inferred vendor keys (OPENROUTER_API_KEY, RESEND_API_KEY, FIRECRAWL_API_KEY, PARALLEL_API_KEY) are resolved internally; they are not on context.secrets for user code.

The LSP types context.secrets from the secrets map on each node.

context.meta

Execution metadata:

  • context.meta.triggerId: ID of the trigger that started this execution.
  • context.meta.triggerType: Type of trigger (form, webhook, schedule).

How the LSP builds types

The LSP creates a virtual TypeScript document for each ts block:

  1. Reads JSON Schemas from inputSchema, outputSchema, and review schema fields.
  2. Converts them to TypeScript interfaces.
  3. Declares context with the correct types.
  4. The TypeScript language service provides completions, hover info, and error checking.

The LSP derives types from your schemas. Write schemas, not type annotations.

Example

This graph shows how context typing flows through nodes. The root node normalizes input from context.nodes.root.input, the downstream greet node reads context.nodes.root.output with full type safety, and the notify node accesses a secret via context.secrets.

secret api_creds {
  label: "API credentials"
  vars: [NOTIFY_KEY]
}

graph typed_example {
  label: "Type Safety Demo"

  root {
    type: code
    label: "Entry"
    inputSchema: @json {
      {
        "type": "object",
        "required": ["name", "email"],
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string" }
        },
        "additionalProperties": false
      }
    }
    outputSchema: @json {
      {
        "type": "object",
        "required": ["name", "email"],
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string" }
        },
        "additionalProperties": false
      }
    }
    code: @ts {
      // context.nodes.root.input is typed from inputSchema
      const { name, email } = context.nodes.root.input
      return { name: name.trim(), email: email.toLowerCase() }
    }
  }

  node greet {
    type: code
    label: "Generate greeting"
    schema: @json {
      {
        "type": "object",
        "required": ["greeting"],
        "properties": { "greeting": { "type": "string" } },
        "additionalProperties": false
      }
    }
    code: @ts {
      // context.nodes.root.output is typed from the root outputSchema
      const name = context.nodes.root.output.name
      return { greeting: `Hello, ${name}!` }
    }
  }

  node notify {
    type: code
    label: "Send notification"
    secrets: {
      api_creds: [NOTIFY_KEY]
    }
    code: @ts {
      // context.secrets is typed from the secrets map above
      const key = context.secrets.api_creds.NOTIFY_KEY
      return { sent: Boolean(key) }
    }
  }

  flow {
    root -> greet
    greet -> notify
  }
}

Further reading

  • Graphs: graph structure and node definitions
  • Reviews: accessing review data via context.reviews
  • Secrets and auth: secret / auth blocks and context.secrets

On this page