Taking Ash Framework’s DSL for a Spin: From Code Generation to RPC Calls
I have lately been looking into more of the Ash framework’s code and exploring the DSL package — Spark that Ash uses to write pretty much…
Taking Ash Framework’s DSL for a Spin: From Code Generation to RPC Calls
I have lately been looking into more of the Ash framework’s code and exploring the DSL package — **Spark that Ash uses to write pretty much all its DSLs. While writing DSLs in Spark eliminates much of the macro magic from Elixir metaprogramming, code generation using [Manifest](https://ash.hexdocs.pm/code-generation.html) takes it to a whole new level. After reading the official documentation of Ash, I came across this fun idea to extend the code generation example to do RPC** calls to a remote BEAM node. I wanted to write about the process I followed from writing the extension to generating code using Manifest and sending RPC calls to a remote BEAM node to execute code remotely.
The article is focused on three concepts —
- Ash extension
- Code generation with Manifest
- Remote procedure call
While the first two steps are mostly based on the official docs (after fixing some issues and filling in some gaps to make it work), for fun I extended it to make RPC calls to a remote BEAM node.
Some familiarity with Spark and the Ash framework in general will help to understand the article.
Disclaimer: This article builds upon examples from the official Ash documentation. Some code snippets are reproduced as-is, while others have been modified or extended to demonstrate the concepts discussed here.
Step 0: Project Setup
We will start with an empty mix project with mix new ash_rpc. We will be using some DSLs from Ash. So we will need Igniter and Ash as dependencies in mix.exs .
After creating a new project, and addingigniter as dependecy in mix.exs —
{:igniter, “~> 0.6”, only: [:dev, :test]}
we fetched dependencies using mix deps.get and ran the following command to install Ash —
mix igniter.install ash
Step 1: Writing the AshRpc Extension
Our resource is a blog postwhich looks like this —
[embed]posts/post.ex
While most of it uses Ash DSLs such as attributes, actions, the new part is —

We will need to write a DSL extension to make this work. While official Spark documentation covers all the the details of writing DSLs in Spark, we will only need a simple DSL builder as shown below —
[embed]rpc_gen/dsl.ex
Here we are defining our DSL sectionas rpc_gen and an entity consisting of action and wire_name. Both action and wire_name are required. actionis of type atom, while wire_name is of type string .
Our resource file looks like this —
[embed]rpc_gen/resource.ex
This is the extension we used in our post resource —

Now that the DSL is ready, we can move on to generating code using Manifest.
Step 2: Code Generation with Manifest
As described on the original documentation (on which this article is largely based) — “[*Ash.Info.Manifest](https://ash.hexdocs.pm/Ash.Info.Manifest.html) is a single normalized data structure that captures all of that shape in one pass. It's the recommended starting point for any new code-generation extension. This guide describes the pattern.*”
The document describes a five-step pattern for code generation —
1. Generate the manifest —
We used [Ash.Info.Manifest.generate](https://ash.hexdocs.pm/code-generation.html#the-manifest) to generate manifest for our domains, resources, types, entrypoints, filter_capabilities and sort_capabilities.

Generated manifestrougly looks like this —
[embed]
2. Verify it has what we need —
Next, we want to add a validation to check that our post resource contains a primary create action. We did this by defining a validate!function as shown below —

Since we are validating for a primary action type create , if we set our create action to primary: false , we should receive the error below at compile time —

3. Walk the manifest and apply DSL configuration values—
Next, we want to apply our DSL configuration values to populate custom: %{} map under each extension key. We did this using theapply_config function as shown below —

This code walks over each entity for our rpc_gen block and populates the wire_name field for each method defined under therpc_gen block.
4. Verify resulting shape —
We want to verify none of the wire_name values are duplicated. We do this using thevalidate_wire_names! function as shown below —

This checks whether wire_name values are duplicated for different actions. In case of a duplication, we should receive the error shown below —

5. Persist —
While step 5 allows use to persist our manifest on disk as JSON or other formats and there is example for this in the official doc, we are not going to do that here.
Instead, we will move on to Step 3: Remote Procedure Call for extending our code to make RPC calls to a remote BEAM node. Our full builder module looks like this —
[embed]
Step 3: Remote Procedure Call
We generated a schema module and cached the validated manifest in a module attributes so that we do not need to parse the code every time —
[embed]
Here, we have three functions —
- manifest — returns the evaluated manifest as the module attribute stores it in AST form
- wire_names — returns the populated
wire names-

- dispatch —dispatches calls to a remote node or the local node when the node is
Node.self(). Insidedispatch,dispatch_remotedelegates the call to the remote configured inconfig.exs —

In our post resource, we have a ETS data layer to store the results of remote post.create call, which will facilitate our testing for the dispatch logic —

In our main AshRpc module, we have a run function that connects to the configured remote node if it is available. If the remote node is not available, it falls back to the local node —
[embed]
We started our remote node as —

and local node as —

Running the run function now should establish connection with the remote node and dispatch the calls to the remote node printing results back on the local node —

We can see in the above screenshot, our call is going to rpc@Mac node.
In this article, we took a simple example from Ash’s official documentation and walked through end-to-end development of an exension, generating code using Manifest, and finally making remote procedure calls to a remote BEAM node. While the Ash framework has a steep learning curve, it seems to be full of hidden gems. Writing this article is my first attempt at this topic to share my experience going through this process while making it fun for readers (hopefully!).
Full source code for the project is available here — https://github.com/imeraj/metaprogramming_elixir/tree/master/ash_rpc
For more in-depth technical posts in the future, follow me here, on Bluesky or on X.
메타데이터
- post_id
- 3db7393405dc
- slug
- taking-ash-frameworks-dsl-on-a-spin-for-rpc-fun-3db7393405dc
- url
- https://itnext.io/taking-ash-frameworks-dsl-on-a-spin-for-rpc-fun-3db7393405dc
- canonical_url
- https://itnext.io/taking-ash-frameworks-dsl-on-a-spin-for-rpc-fun-3db7393405dc
- author_url
- https://medium.com/@imeraj
- status
- ok
- fetched_at
- 2026-08-17 00:58:49