# `NebulaAPI.AST`
[🔗](https://github.com/podCloud/nebula_api/blob/v0.7.1/lib/nebula_api/ast.ex#L1)

AST macros for NebulaAPI.

Provides:
- `defapi` - Define an API method with node targeting
- `on_nebula_nodes` - Conditional compilation based on node
- `call_on_node` - Unicast call with node selector
- `call_on_nodes` - Multicast call with node selector
- `call_on_all_nodes` - Multicast call on all nodes

# `call_on_all_nodes`
*macro* 

Multicast call on all available nodes.

Named alias of the selector-less `call_on_nodes` form: it targets every node
serving the method (i.e. with a registered worker for it).

## Examples

    call_on_all_nodes do
      MyModule.api_method()
    end

    call_on_all_nodes timeout: 5000, strategy: :first do
      MyModule.api_method()
    end

# `call_on_all_nodes`
*macro* 

# `call_on_node`
*macro* 

Unicast call - execute API calls on a specific node.

The selector must be written literally at the call site — one of:
- A Nebula AST expression (like `@api`, `&db`)
- A literal function that receives the nodes_info map and returns a single node atom
- Omitted (the options-only form, or a literal `nil`) — "no restriction": the call routes
  to the first available worker, with the block's options still applying.

A variable or computed selector is a compile error (branch in Elixir and write a separate
`call_on_*` per case). Distinct from omitting it, a selector *function* that returns `nil`
means "nothing matched" — the call fails with `{:nebula_error, {:no_worker_on_node, nil}}`,
it never widens the target.

Inside the block, the innermost explicit routing wins: a call carrying its
own truthy `node_selector:`/`multicast:` trailing opts routes itself (the
block's routing and options are ignored for that call), and a routing key
explicitly set to `nil` opts the call out of the block, back to default
routing.

## Examples

    # With Nebula expression
    call_on_node @api do
      MyModule.api_method()
    end

    # With selector function
    call_on_node fn nodes_info ->
      nodes_info
      |> Enum.filter(fn {_node, info} -> :worker in info.tags end)
      |> Enum.map(fn {node, _} -> node end)
      |> Enum.random()
    end, timeout: 5000 do
      MyModule.api_method()
    end

    # Options only — no selector: any available worker, with these options.
    # The semantic with_options, free of the trailing-opts positional gotcha.
    call_on_node timeout: 30_000 do
      MyModule.api_method()
    end

# `call_on_node`
*macro* 

# `call_on_node`
*macro* 

# `call_on_nodes`
*macro* 

Multicast call - execute API calls on multiple nodes.

The selector must be written literally at the call site — one of:
- A Nebula AST expression (like `@api`, `&db`) - all matching nodes
- A literal function that receives the nodes_info map and returns a list of node atoms
- Omitted (the options-only form, or a literal `nil`) — "no restriction": the call fans out
  to every node serving the method (like `call_on_all_nodes`), with the block's options
  still applying.

A variable or computed selector is a compile error (branch in Elixir and write a separate
`call_on_*` per case). Distinct from omitting it, a selector *function* that returns `nil`
or `[]` means "nothing matched" — zero calls are made (`:all` returns `[]`, `:first`
returns `{:nebula_error, :no_success, []}`, `:quorum` fails `:quorum_unreachable`); it never
widens the target.

Inside the block, the innermost explicit routing wins: a call carrying its
own truthy `node_selector:`/`multicast:` trailing opts routes itself (the
block's routing and options are ignored for that call), and a routing key
explicitly set to `nil` or `false` opts the call out of the block, back to
default routing — `MyMod.f(x, multicast: false)` inside a multicast block
is a plain default call.

## Examples

    # With Nebula expression (calls all &db nodes)
    call_on_nodes &db do
      MyModule.api_method()
    end

    # With selector function
    call_on_nodes fn nodes_info ->
      nodes_info
      |> Enum.filter(fn {_node, info} -> :storage in info.tags end)
      |> Enum.map(fn {node, _} -> node end)
    end, timeout: 5000, strategy: :all do
      MyModule.api_method()
    end

    # Options only — no selector: every node serving the method.
    # `call_on_all_nodes` is the named alias of this form.
    call_on_nodes strategy: :quorum, at_least: 2 do
      MyModule.api_method()
    end

# `call_on_nodes`
*macro* 

# `call_on_nodes`
*macro* 

# `defapi`
*macro* 

Defines an API method with node targeting.

The method is compiled either as a local implementation or a remote stub
depending on whether the current node matches the target nodes.

## Node Selectors

Selectors are juxtaposed by a space (never commas, never a `[list]`):

- `@node` - specific node (short or full name)
- `&tag` - nodes with the tag
- `!@node` / `!&tag` - negation
- `@a &b` - combine by juxtaposition (e.g. `@worker &gpu`)
- *(no selector)* - the body runs on every node (local everywhere)

## Examples

    defapi @api, get_user(id) do
      Repo.get(User, id)
    end

    defapi &db !@worker, find_podcast(slug) do
      MyApp.Repo.find_one(:db, "records", %{identifier: slug})
    end

    # No selector → available on every node, each returns its own data.
    defapi get_node_health() do
      collect_runtime_info()
    end

# `defapi`
*macro* 

# `defapi`
*macro* 

# `on_nebula_nodes`
*macro* 

# `on_nebula_nodes`
*macro* 

Conditional compilation based on node.

Just like `if` but for NebulaAPI. Only compiles the `do` block on matching nodes,
otherwise compiles the `else` block (if provided).

## Examples

    on_nebula_nodes &db do
      # This code only exists on &db nodes
      use MyApp.Repo, otp_app: :my_app
    end

    on_nebula_nodes @api do
      # Code for @api nodes
    else
      # Code for other nodes
    end

---

*Consult [api-reference.md](api-reference.md) for complete listing*
