<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Artyom Molchanov </title>
    <description>The latest articles on DEV Community by Artyom Molchanov  (@adamanq).</description>
    <link>https://dev.to/adamanq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1280112%2Fc0fa00bf-1167-4bb4-a7ea-c72f1f5be58e.jpg</url>
      <title>DEV Community: Artyom Molchanov </title>
      <link>https://dev.to/adamanq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamanq"/>
    <language>en</language>
    <item>
      <title>Ash Framework: ways to contact — AshJsonApi, AshGraphQL and AshTypescript</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Fri, 03 Jul 2026 08:21:04 +0000</pubDate>
      <link>https://dev.to/adamanq/ash-ways-to-contact-ashjsonapi-ashgraphql-and-ashtypescript-c8</link>
      <guid>https://dev.to/adamanq/ash-ways-to-contact-ashjsonapi-ashgraphql-and-ashtypescript-c8</guid>
      <description>&lt;p&gt;Building a stable product capable of withstanding high loads across different servers is a complex and resource-intensive task. A programmer can't focus on business logic alone: first you need to set up the database, design the server-side &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;, and much more.&lt;/p&gt;

&lt;p&gt;The complexity grows manyfold when you work solo. You end up implementing the interface yourself on your framework of choice, ensuring the layers stay compatible, packaging the project into &lt;strong&gt;&lt;em&gt;Docker&lt;/em&gt;&lt;/strong&gt; images, and deploying it to &lt;strong&gt;&lt;em&gt;Kubernetes&lt;/em&gt;&lt;/strong&gt;. Add to that configuring request routing through &lt;strong&gt;&lt;em&gt;Nginx&lt;/em&gt;&lt;/strong&gt; and designing the system according to clean-architecture principles — and you get an extremely labor-intensive process.&lt;/p&gt;

&lt;p&gt;That's why many languages are actively adopting tools to automate routine work (&lt;em&gt;boilerplate&lt;/em&gt;), letting you concentrate on the unique code. In &lt;strong&gt;&lt;em&gt;Go&lt;/em&gt;&lt;/strong&gt;, for example, there's the &lt;strong&gt;&lt;em&gt;oapi-codegen&lt;/em&gt;&lt;/strong&gt; library: you describe the contract in &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt; format, and the server code is generated from it automatically. This format simplifies frontend–backend integration and makes changing logic less painful — just update the description in the &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt; file and regenerate the service.&lt;/p&gt;

&lt;p&gt;Approaches like this — systematizing repetitive tasks — are appearing everywhere. The main goal is to make integration predictable and eliminate the situation where a developer has to rewrite and test the same thing dozens of times just to achieve perfect compatibility. And in this area there's a clear leader — &lt;strong&gt;Ash&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 Acknowledgments
&lt;/h2&gt;

&lt;p&gt;I want to express my deep gratitude to Zach Daniel and everyone who responded to my previous article about &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; (&lt;a href="https://dev.to/adamanq/ash-framework-introduction-3lm3"&gt;&lt;em&gt;Ash Framework: Introduction&lt;/em&gt;&lt;/a&gt;) and shared it. Your support gave me a huge boost of energy and motivation and confirmed that the &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; community is amazing!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Foreword
&lt;/h2&gt;

&lt;p&gt;In this material I deliberately won't provide a complete practical guide to the topics covered, since I'm counting on your curiosity. As much as I'd like to cover everything, I can't describe the entire practical side of these frameworks in a single article and teach you everything at once. Instead, I'll share information with you and set a direction for reflection — which is the main purpose of this publication. For a deeper dive into how these libraries work, I recommend starting to build your own projects on top of them — that's the approach that once helped me understand their internals best. Remember that your main mentor will be practice, so good luck mastering it!&lt;/p&gt;

&lt;p&gt;Important: the goal of this article is by no means to talk you into using Ash everywhere and always!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🧐 What the Ash ecosystem offers
&lt;/h2&gt;

&lt;p&gt;If you're familiar with &lt;strong&gt;&lt;em&gt;Elixir&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;Phoenix&lt;/em&gt;&lt;/strong&gt;, you know the framework already contains built-in mechanisms to simplify development. One of them is &lt;strong&gt;&lt;em&gt;LiveView&lt;/em&gt;&lt;/strong&gt;. It lets you drop the classic split into an &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt; backend and a separate &lt;strong&gt;&lt;em&gt;JavaScript&lt;/em&gt;&lt;/strong&gt; frontend: the interface is written right inside the application. This speeds up development and is convenient for backend programmers working without a frontend team. For simple and medium projects the &lt;strong&gt;&lt;em&gt;LiveView&lt;/em&gt;&lt;/strong&gt; path is a great fit, but when building large-scale systems the code risks turning into "spaghetti," where it's hard to track dependencies between components and to find handlers like &lt;code&gt;def handle_params/3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But what if you need a solid, standalone frontend, yet you're tired of writing a boilerplate &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt; interface and wiring up the connection from scratch every time? The answer is to master what &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; has to offer. That's what we'll talk about.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😮 REST with &lt;a href="https://ash-json-api.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;AshJsonApi&lt;/a&gt;: a new approach
&lt;/h2&gt;

&lt;p&gt;Standard practice involves pinning down the contract between frontend and backend not only verbally but also in writing — usually via &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt;. It describes the data structures and API methods, after which the documentation is handed to the interface team. Any changes require care so as not to break compatibility. Even the basic setup of this approach costs considerable effort: you need to integrate a generation library, create configurations per the OpenAPI (&lt;em&gt;Swagger&lt;/em&gt;) specification, and manually describe the endpoints. If you're rewriting an existing project, the process can easily take more than a workday.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ash-json-api.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;&lt;strong&gt;AshJsonApi&lt;/strong&gt;&lt;/a&gt; offers an alternative. The main advantage is that you no longer need to write a specification in &lt;strong&gt;&lt;em&gt;YAML/JSON&lt;/em&gt;&lt;/strong&gt; upfront and generate code from it. To get started, all you need is a ready domain of resources and the &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; extension enabled.&lt;/p&gt;

&lt;p&gt;You can create a new project with support for this tool right on the main page of the &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; portal via the builder. If you already have an &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; project, you can add the functionality with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix igniter.install ash_json_api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During installation, the system asks clarifying questions (&lt;em&gt;Y/N&lt;/em&gt;). You can agree with all the suggestions or study each item and decide for yourself. After installation, a few simple steps remain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring a resource to work with JSON:API
&lt;/h3&gt;

&lt;p&gt;To make resources available through the &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;, enable the &lt;strong&gt;&lt;em&gt;AshJsonApi.Resource&lt;/em&gt;&lt;/strong&gt; extension. This will let you automatically generate endpoints based on your domain definitions. Add &lt;code&gt;AshJsonApi.Resource&lt;/code&gt; to the &lt;code&gt;extensions&lt;/code&gt; list and set the resource name in the &lt;code&gt;type&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshJsonApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Enable endpoint generation&lt;/span&gt;
  &lt;span class="n"&gt;json_api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="s2"&gt;"lesson"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons_allowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;allowed_courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"go"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Golang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"elixir"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Elixir"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"rust"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Rust"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allowed_courses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;type&lt;/code&gt; parameter defines the resource name in the JSON:API namespace. It's used primarily for documentation (&lt;em&gt;e.g., in &lt;strong&gt;Swagger&lt;/strong&gt; or &lt;strong&gt;Redoc&lt;/strong&gt;&lt;/em&gt;) and lets you group methods by tags.&lt;/p&gt;

&lt;p&gt;Next, describe the routes in the domain, specifying the paths and linking them to specific resource actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshJsonApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;json_api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;base_route&lt;/span&gt; &lt;span class="s2"&gt;"/lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Get the list of lessons"&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Get a single lesson"&lt;/span&gt;
        &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Create a lesson"&lt;/span&gt;
        &lt;span class="n"&gt;patch&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Update a lesson"&lt;/span&gt;
        &lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Delete a lesson"&lt;/span&gt;

        &lt;span class="c1"&gt;# Path is relative to base_route; final endpoint: /lessons/allowed&lt;/span&gt;
        &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/allowed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons_allowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Get the list of allowed lessons"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've done this, you get six fully functional endpoints. All the logic is now tied to your &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt; inside the domain model. Separate &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt; files or manual route definitions aren't needed: a change to the action's logic in the code is automatically reflected in the behavior of the corresponding &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt; endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key strengths of AshJsonApi
&lt;/h3&gt;

&lt;h4&gt;
  
  
  A unified approach and predictability
&lt;/h4&gt;

&lt;p&gt;The main advantage is the strict consistency of your application. When switching to a different contract type (&lt;em&gt;e.g., &lt;strong&gt;GraphQL&lt;/strong&gt;&lt;/em&gt;), you won't have to rewrite your business logic. Because &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; works on top of your &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt;, you're insured against the situation where changes in the code aren't reflected in one of the interfaces: all the logic is encapsulated in the &lt;strong&gt;&lt;em&gt;action&lt;/em&gt;&lt;/strong&gt;, which you "hand off" to different contracts. This noticeably speeds up development — by my own estimate, a finished product comes together roughly five times faster. To automate the process, you can use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix ash.patch.extend CourseHub.Courses.Lesson json_api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By specifying the resource's address, you let &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; automatically generate all the plumbing needed to work with the &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Working with data: filtering, sorting, and relationships
&lt;/h4&gt;

&lt;p&gt;As a backend developer, I used to spend a lot of time on the routine implementation of filters and sorting, hand-writing &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt; with &lt;strong&gt;&lt;em&gt;LIMIT&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;OFFSET&lt;/em&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;em&gt;WHERE&lt;/em&gt;&lt;/strong&gt;. &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; removes this burden: filtering and ordering happen automatically. The frontend just needs to specify the required fields and conditions right in the request. This dramatically simplifies collaboration between teams: the frontend forms its own data queries without distracting the backend. And all computation happens on the server, so the client bears no extra load — it merely describes the desired shape of the data. The full list of parameters is in the official documentation. An example request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET /courses/1?include&lt;span class="o"&gt;=&lt;/span&gt;lessons&amp;amp;included_page[lessons][limit]&lt;span class="o"&gt;=&lt;/span&gt;10&amp;amp;included_page[lessons][offset]&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The request returns information about the course and loads the related lessons with the given pagination. The response comes in &lt;strong&gt;&lt;em&gt;application/vnd.api+json&lt;/em&gt;&lt;/strong&gt; format — a standardized specification (more at &lt;a href="https://jsonapi.org/format/1.0/" rel="noopener noreferrer"&gt;jsonapi.org&lt;/a&gt;) that structures JSON for predictable handling.&lt;/p&gt;

&lt;p&gt;An example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"course"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Course!"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"relationships"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"self"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1/relationships/lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"related"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1/lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"first"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1?include=lessons&amp;amp;included_page[lessons][limit]=10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1?include=lessons&amp;amp;included_page[lessons][limit]=10&amp;amp;included_page[lessons][offset]=10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"prev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"last"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1?include=lessons&amp;amp;included_page[lessons][limit]=10&amp;amp;included_page[lessons][offset]=40"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"included"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lessons about Elixir 1!"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lessons about Elixir 2!"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Wrap-up and personal experience
&lt;/h4&gt;

&lt;p&gt;To sum up, I'll share my experience. On my current project, creating the &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt; endpoints took just 20–30 minutes. Most of the time went into customizing how the documentation is rendered in &lt;strong&gt;&lt;em&gt;Redoc&lt;/em&gt;&lt;/strong&gt; — that turned out to be fairly labor-intensive (&lt;em&gt;unlike generating the &lt;strong&gt;API&lt;/strong&gt; itself&lt;/em&gt;). If you decide to use &lt;strong&gt;&lt;em&gt;Redoc&lt;/em&gt;&lt;/strong&gt;, be prepared to manually rewrite its blocks for a nice presentation of responses and examples.&lt;/p&gt;

&lt;p&gt;Otherwise the tool performed excellently: superb flexibility, customizable error handling, fine-grained control over endpoint access. If your goal is to build a convenient and powerful &lt;strong&gt;&lt;em&gt;REST API&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; is an excellent choice.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🙂‍↕️ AshGraphQL
&lt;/h2&gt;

&lt;p&gt;This library may come with some difficulties: not that many people have worked with &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; at all. So first I'll give an introduction — to refresh your memory or learn what it is — and then we'll look at the library itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  What GraphQL is
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; is a query language for &lt;strong&gt;&lt;em&gt;APIs&lt;/em&gt;&lt;/strong&gt; and a runtime for executing them. The client itself describes what data it needs, and the server returns exactly that shape. The contract is a strictly typed schema.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fundamental principles
&lt;/h4&gt;

&lt;p&gt;Interaction happens through a single &lt;strong&gt;&lt;em&gt;endpoint&lt;/em&gt;&lt;/strong&gt; (&lt;em&gt;usually &lt;code&gt;POST /graphql&lt;/code&gt;&lt;/em&gt;). The logic is built around a graph of types. A query has a hierarchical structure, and the server's response mirrors it exactly. This solves the problems of &lt;strong&gt;&lt;em&gt;over-fetching&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;under-fetching&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Types of operations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query&lt;/strong&gt; — read operations (idempotent).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutation&lt;/strong&gt; — data-modifying operations (executed sequentially).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt; — subscribing to a stream of real-time events (via &lt;em&gt;WebSocket&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Schema and resolvers
&lt;/h4&gt;

&lt;p&gt;The central element is the schema, described in &lt;strong&gt;&lt;em&gt;SDL&lt;/em&gt;&lt;/strong&gt;. It serves as the source of truth and enables introspection. Each field is mapped to a resolver — a function that returns a value. The server traverses the query tree and calls the resolvers for each field.&lt;/p&gt;

&lt;h4&gt;
  
  
  Difficulties
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The N+1 problem.&lt;/strong&gt; Resolvers work independently, and without optimization (batching) this leads to a multitude of database queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching.&lt;/strong&gt; Standard &lt;strong&gt;&lt;em&gt;HTTP&lt;/em&gt;&lt;/strong&gt; mechanisms (&lt;strong&gt;&lt;em&gt;CDN&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;ETag&lt;/em&gt;&lt;/strong&gt;) don't work, since everything goes through &lt;strong&gt;&lt;em&gt;POST&lt;/em&gt;&lt;/strong&gt;. A normalized cache on the client is required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security.&lt;/strong&gt; A client can send a deeply nested query that overloads the database. Depth limiting and complexity analysis are needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Errors.&lt;/strong&gt; The server almost always returns status &lt;code&gt;200&lt;/code&gt;, and the errors themselves are placed in an &lt;code&gt;errors&lt;/code&gt; array alongside partial data. This breaks monitoring based on &lt;strong&gt;&lt;em&gt;HTTP&lt;/em&gt;&lt;/strong&gt; status codes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure.&lt;/strong&gt; File uploads, &lt;strong&gt;&lt;em&gt;rate limiting&lt;/em&gt;&lt;/strong&gt;, and observability require a separate infrastructure layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Where it's effective
&lt;/h4&gt;

&lt;p&gt;The technology pays off when there are many diverse clients with complex data requirements. For a single client with simple resources, it's easier to go with &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt;. For internal service-to-service communication with rigid contracts and minimal latency, &lt;strong&gt;&lt;em&gt;gRPC&lt;/em&gt;&lt;/strong&gt; is a better fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  What &lt;a href="https://ash-graphql.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;AshGraphQL&lt;/a&gt; is
&lt;/h3&gt;

&lt;p&gt;Despite &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; still being less popular than other approaches, its integration with &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; is done very well. The technology is easy to bring into &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt;-based projects.&lt;/p&gt;

&lt;p&gt;To get started, add the library to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix igniter.install ash_graphql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in your schema module, specify the domains you use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;domains:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Your&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Your&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuring resources and domains
&lt;/h3&gt;

&lt;p&gt;To make a resource available through &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt;, enable the &lt;code&gt;AshGraphql.Resource&lt;/code&gt; extension and describe the base configuration block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="ss"&gt;:lesson&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ... rest of the resource logic&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the domain module, define the operations (handles) via the &lt;code&gt;AshGraphql.Domain&lt;/code&gt; extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:get_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;
      &lt;span class="n"&gt;read_one&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:most_popular_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:most_popular&lt;/span&gt;
      &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;mutations&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;
      &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;
      &lt;span class="n"&gt;destroy&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These handles can be described not only in the domain module, but also right inside the resource.&lt;/p&gt;

&lt;p&gt;In addition, you can create your own actions for custom responses. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="ss"&gt;:lesson&lt;/span&gt;

  &lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:random_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:random_lesson&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:random_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:struct&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="ss"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;instance_of:&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="c1"&gt;# return a list of structs, as declared in the action type&lt;/span&gt;
      &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Generating the SDL schema
&lt;/h4&gt;

&lt;p&gt;As with &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; (&lt;em&gt;where &lt;strong&gt;Swagger&lt;/strong&gt; is generated automatically after edits&lt;/em&gt;), the &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; schema is updated in a similar way. Specify the path for saving it in &lt;code&gt;use AshGraphql&lt;/code&gt; via &lt;code&gt;generate_sdl_file:&lt;/code&gt;. The file will be generated automatically on every run of &lt;code&gt;mix ash.codegen&lt;/code&gt; (if &lt;code&gt;auto_generate_sdl_file?: true&lt;/code&gt; is enabled):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;domains:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Domain1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Domain2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;generate_sdl_file:&lt;/span&gt; &lt;span class="s2"&gt;"priv/schema.graphql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;auto_generate_sdl_file?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Creating Subscriptions
&lt;/h4&gt;

&lt;p&gt;You can set up receiving data in real time through the subscriptions mechanism. This uses &lt;strong&gt;&lt;em&gt;Absinthe&lt;/em&gt;&lt;/strong&gt; configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# in your Absinthe schema file&lt;/span&gt;
&lt;span class="n"&gt;subscription&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type_name&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;
      &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;current_user:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;topic:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;context_id:&lt;/span&gt; &lt;span class="s2"&gt;"user/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:unauthorized&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Subscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_for_subscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="no"&gt;YourResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;YourDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resolution&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;actor:&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Monitoring
&lt;/h4&gt;

&lt;p&gt;For a deeper dive I recommend the &lt;a href="https://hexdocs.pm/ash/monitoring.html" rel="noopener noreferrer"&gt;official Ash monitoring guide&lt;/a&gt;. Here we'll briefly describe the tracing mechanisms and telemetry events that become available with this extension.&lt;/p&gt;

&lt;p&gt;You can define your own tracer at the domain level — its settings take precedence over the global &lt;code&gt;config :ash, :tracer&lt;/code&gt; parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tracer&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Traces
&lt;/h4&gt;

&lt;p&gt;The operations of &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; resolvers and of the underlying batch data loaders are wrapped in spans with appropriate names. For convenience, they're tagged with the &lt;code&gt;source: :graphql&lt;/code&gt; metadata, which lets you filter or annotate this data in your monitoring system.&lt;/p&gt;

&lt;h4&gt;
  
  
  Telemetry
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;AshGraphql&lt;/em&gt;&lt;/strong&gt; module publishes telemetry events, each suffixed with &lt;code&gt;:start&lt;/code&gt; and &lt;code&gt;:stop&lt;/code&gt;. Start events (&lt;code&gt;:start&lt;/code&gt;) carry a &lt;code&gt;system_time&lt;/code&gt; measurement, while stop events (&lt;code&gt;:stop&lt;/code&gt;) carry &lt;code&gt;system_time&lt;/code&gt; and the execution &lt;code&gt;duration&lt;/code&gt;. All time values are given in the native time unit.&lt;/p&gt;

&lt;p&gt;The list of emitted events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;domain_name&amp;gt;, :gql_mutation]&lt;/code&gt;&lt;/strong&gt; — execution of a mutation. To break down measurements, use the &lt;code&gt;resource_short_name&lt;/code&gt; and &lt;code&gt;mutation&lt;/code&gt; (or &lt;code&gt;action&lt;/code&gt;) metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;domain_name&amp;gt;, :gql_query]&lt;/code&gt;&lt;/strong&gt; — execution of a query. To break down measurements, use the &lt;code&gt;resource_short_name&lt;/code&gt; and &lt;code&gt;query&lt;/code&gt; (or &lt;code&gt;action&lt;/code&gt;) metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;domain_name&amp;gt;, :gql_relationship]&lt;/code&gt;&lt;/strong&gt; — resolution of a relationship. To break down measurements, use the &lt;code&gt;resource_short_name&lt;/code&gt; and &lt;code&gt;relationship&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;domain_name&amp;gt;, :gql_calculation]&lt;/code&gt;&lt;/strong&gt; — resolution of a calculation. To break down measurements, use the &lt;code&gt;resource_short_name&lt;/code&gt; and &lt;code&gt;calculation&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;domain_name&amp;gt;, :gql_relationship_batch]&lt;/code&gt;&lt;/strong&gt; — batch resolution of relationships by the data loader. To break down measurements, use the &lt;code&gt;resource_short_name&lt;/code&gt; and &lt;code&gt;relationship&lt;/code&gt; metadata&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;domain_name&amp;gt;, :gql_calculation_batch]&lt;/code&gt;&lt;/strong&gt; — batch resolution of calculations by the data loader. To break down measurements, use the &lt;code&gt;resource_short_name&lt;/code&gt; and &lt;code&gt;calculation&lt;/code&gt; metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Verdict
&lt;/h3&gt;

&lt;p&gt;Honestly, I haven't reached for this kind of interaction often, but from the little experience I do have, I'll say one thing — it really is convenient.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😳 &lt;a href="https://ash-typescript.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;AshTypescript&lt;/a&gt; — the hidden gem
&lt;/h2&gt;

&lt;p&gt;We've already gotten acquainted with &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;AshGraphQL&lt;/em&gt;&lt;/strong&gt;, and it was a useful experience. These two approaches are a kind of industry standard for web development, and they hold no special surprises for seasoned specialists. But &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; has a "trump card" — &lt;a href="https://ash-typescript.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;AshTypescript&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Imagine you want to interact with the frontend with &lt;strong&gt;&lt;em&gt;gRPC&lt;/em&gt;&lt;/strong&gt;-level type safety, while keeping an endpoint structure like &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; and the data-handling flexibility of &lt;strong&gt;&lt;em&gt;AshGraphQL&lt;/em&gt;&lt;/strong&gt;. That's exactly what &lt;strong&gt;&lt;em&gt;AshTypescript&lt;/em&gt;&lt;/strong&gt; solves.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Defining the structure.&lt;/strong&gt; You describe your domain and resources, specifying the actions (&lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt;) and attributes you need&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enabling the extensions.&lt;/strong&gt; Add &lt;code&gt;AshTypescript.Resource&lt;/code&gt; to the resource and &lt;code&gt;AshTypescript.Rpc&lt;/code&gt; to the domain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuring the resource.&lt;/strong&gt; In the &lt;code&gt;typescript&lt;/code&gt; block inside the resource, set its name for generation
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;domain:&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshTypescript&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# Enable the extension&lt;/span&gt;

  &lt;span class="c1"&gt;# Set the type name for the generated RPC file&lt;/span&gt;
  &lt;span class="n"&gt;typescript&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;type_name&lt;/span&gt; &lt;span class="s2"&gt;"Lesson"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Standard CRUD actions&lt;/span&gt;
  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Define the resource attributes&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;uuid_primary_key&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default:&lt;/span&gt; &lt;span class="s2"&gt;"Some text!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Define the relationship with Course&lt;/span&gt;
  &lt;span class="n"&gt;relationships&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:course&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Course&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Binding RPC methods.&lt;/strong&gt; In the domain module you define which methods will be available to the frontend and link them to the resource's actions.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshTypescript&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Rpc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Public RPC methods for the frontend&lt;/span&gt;
  &lt;span class="n"&gt;typescript_rpc&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:create_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:get_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:delete_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:update_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generating the code.&lt;/strong&gt; By running &lt;code&gt;mix ash_typescript.codegen&lt;/code&gt; (&lt;em&gt;or the unified &lt;code&gt;mix ash.codegen&lt;/code&gt;&lt;/em&gt;), you get the generated files at the path from your configuration. The settings live in &lt;code&gt;config/config.exs&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:ash_typescript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;ash_domains:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="no"&gt;Backend&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;output_file:&lt;/span&gt; &lt;span class="s2"&gt;"assets/js/ash_rpc.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Path for saving the generated code&lt;/span&gt;
  &lt;span class="ss"&gt;run_endpoint:&lt;/span&gt; &lt;span class="s2"&gt;"/rpc/run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# Endpoint for sending requests&lt;/span&gt;
  &lt;span class="ss"&gt;validate_endpoint:&lt;/span&gt; &lt;span class="s2"&gt;"/rpc/validate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Endpoint for validating data against the schema&lt;/span&gt;
  &lt;span class="ss"&gt;input_field_formatter:&lt;/span&gt; &lt;span class="ss"&gt;:camel_case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Field-name format on input&lt;/span&gt;
  &lt;span class="ss"&gt;output_field_formatter:&lt;/span&gt; &lt;span class="ss"&gt;:camel_case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Field-name format on output&lt;/span&gt;
  &lt;span class="ss"&gt;require_tenant_parameters:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# Multitenancy&lt;/span&gt;
  &lt;span class="ss"&gt;generate_zod_schemas:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# Generate Zod schemas&lt;/span&gt;
  &lt;span class="ss"&gt;generate_phx_channel_rpc_actions:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;generate_validation_functions:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;zod_import_path:&lt;/span&gt; &lt;span class="s2"&gt;"zod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;zod_schema_suffix:&lt;/span&gt; &lt;span class="s2"&gt;"ZodSchema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;phoenix_import_path:&lt;/span&gt; &lt;span class="s2"&gt;"phoenix"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Zod&lt;/strong&gt; is a library for describing and validating the structure of data. It lets you create schemas and automatically validate incoming data against them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key advantages of Zod:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript-oriented&lt;/strong&gt; — automatically infers types&lt;/li&gt;
&lt;li&gt;An intuitive and easy-to-read API&lt;/li&gt;
&lt;li&gt;Support for complex validation rules (&lt;em&gt;unions, intersections, refinements&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;High performance and a minimal footprint&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Starting with newer versions, AshTypescript generates not a single monolithic file but several: besides &lt;code&gt;ash_rpc.ts&lt;/code&gt;, you get shared types (&lt;code&gt;ash_types.ts&lt;/code&gt;) and &lt;strong&gt;&lt;em&gt;Zod&lt;/em&gt;&lt;/strong&gt; schemas (&lt;code&gt;ash_zod.ts&lt;/code&gt;), imported from a common place.&lt;/p&gt;

&lt;p&gt;In the end, all that's left is to hand the generated files off to the frontend. Client developers simply call methods, e.g. &lt;code&gt;listLessons()&lt;/code&gt;, without describing the &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt; by hand. This makes integration incredibly simple and convenient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sorting, filtering, and field projection mechanisms
&lt;/h3&gt;

&lt;p&gt;In standard backend development, you have to hand-construct &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt; queries for sorting, filtering, and selection in order to avoid transferring excessive data. &lt;strong&gt;&lt;em&gt;AshTypescript&lt;/em&gt;&lt;/strong&gt; implements the same capabilities as &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; — flexibly and type-safely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CREATE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createLessonResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createLesson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Elixir lesson about Ash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;very high!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;createLessonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createLessonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createLessonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// READ (single)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLesson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getLesson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;course&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// READ (list)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lessons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listLessons&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// UPDATE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateLessonResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateLessson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yoo chill im just a vessel!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// DELETE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteLessonResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deleteLesson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Advantages of using AshTypescript
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Guaranteed contract integrity.&lt;/strong&gt; No need to worry about interface drift, as can happen with "hand-rolled" &lt;strong&gt;&lt;em&gt;gRPC&lt;/em&gt;&lt;/strong&gt;. The single source of truth is the &lt;em&gt;.ts&lt;/em&gt;-file generator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated API creation.&lt;/strong&gt; No more manually writing methods or endpoints. It's enough to describe the resource and &lt;code&gt;typescript_rpc&lt;/code&gt; in the domain and give the methods unique names&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimized boilerplate.&lt;/strong&gt; Built-in tools for sorting, filtering, and field selection (&lt;em&gt;&lt;strong&gt;select&lt;/strong&gt;/projections&lt;/em&gt;) make the work seamless&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy integration with other teams.&lt;/strong&gt; You can hand the generated files (&lt;strong&gt;&lt;em&gt;ash_rpc.ts&lt;/em&gt;, &lt;em&gt;ash_types.ts&lt;/em&gt;, &lt;em&gt;ash_zod.ts&lt;/em&gt;&lt;/strong&gt;) to colleagues — they can start working immediately without spending time studying the documentation&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Impressions
&lt;/h3&gt;

&lt;p&gt;When I learned about this way of organizing server–client interaction, I felt a renewed enthusiasm for programming. Hand-writing endpoints is monotonous, and describing schemas for &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; takes a lot of time. Declaratively describing &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt; on the backend and simply calling methods on the frontend, on the other hand, is fast and convenient. This approach removes up to 80% of the excess code and speeds up development manyfold.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🧮 Comparing the three approaches
&lt;/h2&gt;

&lt;p&gt;All three approaches share one foundation — your &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt;. Only the "storefront" through which the logic faces the outside world changes. Hence the main takeaway: choosing an approach doesn't lock you into it forever — if needed, you can add a second contract on top of the same logic.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;AshJsonApi (REST / JSON:API)&lt;/th&gt;
&lt;th&gt;AshGraphQL&lt;/th&gt;
&lt;th&gt;AshTypescript&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Contract type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;REST per the JSON:API standard&lt;/td&gt;
&lt;td&gt;GraphQL schema (SDL)&lt;/td&gt;
&lt;td&gt;Generated &lt;code&gt;.ts&lt;/code&gt; files (RPC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Source of truth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;your &lt;em&gt;actions&lt;/em&gt; + OpenAPI autogen&lt;/td&gt;
&lt;td&gt;your &lt;em&gt;actions&lt;/em&gt; + SDL autogen&lt;/td&gt;
&lt;td&gt;your &lt;em&gt;actions&lt;/em&gt; + TypeScript autogen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;How the client specifies a request&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;query parameters (&lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;, &lt;code&gt;include&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;a GraphQL query with a field tree&lt;/td&gt;
&lt;td&gt;typed calls (&lt;code&gt;fields&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Selection flexibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;td&gt;maximal&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Industry standard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;yes, ubiquitous&lt;/td&gt;
&lt;td&gt;yes, but niche&lt;/td&gt;
&lt;td&gt;no, specific to the Ash + TS stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type safety on the frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;via codegen from OpenAPI&lt;/td&gt;
&lt;td&gt;via codegen from the schema&lt;/td&gt;
&lt;td&gt;native, out of the box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;not out of the box&lt;/td&gt;
&lt;td&gt;yes (subscriptions)&lt;/td&gt;
&lt;td&gt;yes (Phoenix channels)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;works (GET)&lt;/td&gt;
&lt;td&gt;difficult (POST)&lt;/td&gt;
&lt;td&gt;depends on the implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Entry barrier on the frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;td&gt;medium/high&lt;/td&gt;
&lt;td&gt;low (for TypeScript teams)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ideal for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;public and external APIs&lt;/td&gt;
&lt;td&gt;many diverse clients with complex queries&lt;/td&gt;
&lt;td&gt;full-stack Elixir + TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;How to choose in practice:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AshJsonApi&lt;/strong&gt; — when you need familiar &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt;, a public or external &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;, caching at the &lt;strong&gt;&lt;em&gt;HTTP&lt;/em&gt;&lt;/strong&gt; level, and maximum compatibility with any client. The most "neutral" choice&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AshGraphQL&lt;/strong&gt; — when there are many clients with different, often nested data requirements, and under-/over-fetching (&lt;strong&gt;&lt;em&gt;over/under-fetching&lt;/em&gt;&lt;/strong&gt;) is a real hindrance. Be prepared to pay for it with batching against N+1, query-complexity limiting, and a client-side cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AshTypescript&lt;/strong&gt; — when both the backend and the frontend are yours, and the frontend is in &lt;strong&gt;&lt;em&gt;TypeScript&lt;/em&gt;&lt;/strong&gt;. It gives you end-to-end type safety without a hand-written contract: rename a field in &lt;strong&gt;&lt;em&gt;Elixir&lt;/em&gt;&lt;/strong&gt; and you immediately get a compile error in &lt;strong&gt;&lt;em&gt;TS&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're unsure and the client is single and relatively simple — start with &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt;. If you later need &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; or type-safe &lt;strong&gt;&lt;em&gt;RPC&lt;/em&gt;&lt;/strong&gt;, you'll simply add another contract on top of the same &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt; without rewriting the logic.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😴 Conclusion
&lt;/h2&gt;

&lt;p&gt;The world of &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; gives you not just a convenient way of working with the database and freedom from boilerplate — it lets you build applications quickly, with quality, and with enjoyment. Fully mastering these methods will take a little more time, but it will pay off more than once and will genuinely help you in your projects. Good luck!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🥴 From the author
&lt;/h2&gt;

&lt;p&gt;Thank you so much for your interest in this article! I hope it helped you understand what AshJsonApi, AshGraphQL, and AshTypescript are and why they're used!&lt;/p&gt;

&lt;p&gt;If you enjoyed this article and want more material like it, join me on my &lt;a href="https://bsky.app/profile/adamancry.bsky.social" rel="noopener noreferrer"&gt;Bsky&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>ash</category>
      <category>phoenix</category>
      <category>backend</category>
    </item>
    <item>
      <title>Ash Framework: способы взаимодействия — AshJsonApi, AshGraphQL и AshTypescript</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Fri, 03 Jul 2026 07:50:45 +0000</pubDate>
      <link>https://dev.to/adamanq/ash-sposoby-vzaimodieistviia-ashjsonapi-ashgraphql-i-ashtypescript-1h9p</link>
      <guid>https://dev.to/adamanq/ash-sposoby-vzaimodieistviia-ashjsonapi-ashgraphql-i-ashtypescript-1h9p</guid>
      <description>&lt;p&gt;Разработка стабильного продукта, способного держать высокие нагрузки на разных серверах, — задача сложная и ресурсозатратная. Программист не может сфокусироваться только на бизнес-логике: сначала нужно настроить базу данных, спроектировать серверные &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt; и многое другое.&lt;/p&gt;

&lt;p&gt;Сложность многократно возрастает при работе в одиночку. Приходится самому реализовывать интерфейс на выбранном фреймворке, обеспечивать совместимость слоёв, собирать проект в образы &lt;strong&gt;&lt;em&gt;Docker&lt;/em&gt;&lt;/strong&gt; и разворачивать его в &lt;strong&gt;&lt;em&gt;Kubernetes&lt;/em&gt;&lt;/strong&gt;. Добавьте сюда настройку маршрутизации через &lt;strong&gt;&lt;em&gt;Nginx&lt;/em&gt;&lt;/strong&gt; и проектирование системы по принципам чистой архитектуры — и получится крайне трудоёмкий процесс.&lt;/p&gt;

&lt;p&gt;Поэтому во многих языках активно внедряют инструменты для автоматизации рутины (&lt;em&gt;бойлерплейта&lt;/em&gt;), позволяя сосредоточиться на уникальном коде. Например, в &lt;strong&gt;&lt;em&gt;Go&lt;/em&gt;&lt;/strong&gt; есть библиотека &lt;strong&gt;&lt;em&gt;oapi-codegen&lt;/em&gt;&lt;/strong&gt;: вы описываете контракт в формате &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt;, а по нему автоматически генерируется код сервера. Такой формат упрощает интеграцию фронтенда и бэкенда и делает изменения логики менее болезненными — достаточно обновить описание в &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt;-файле и перегенерировать сервис.&lt;/p&gt;

&lt;p&gt;Подобные подходы к систематизации повторяющихся задач появляются повсеместно. Главная цель — сделать интеграцию предсказуемой и исключить ситуацию, когда разработчику приходится десятки раз переписывать и тестировать одно и то же ради идеальной совместимости. И в этой сфере есть свой лидер — &lt;strong&gt;Ash&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 Благодарности
&lt;/h2&gt;

&lt;p&gt;Хочу выразить глубокую признательность Заку Даниэлю и всем, кто отреагировал на мою предыдущую статью об &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; (&lt;a href="https://dev.to/adamanq/ash-framework-znakomstvo-g0g"&gt;&lt;em&gt;Ash Framework: Знакомство&lt;/em&gt;&lt;/a&gt;) и поделился ею. Ваша поддержка дала огромный заряд энергии и мотивации и подтвердила, что сообщество &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; — потрясающее!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Предисловие
&lt;/h2&gt;

&lt;p&gt;В этом материале намеренно не будет практическое руководство в полном объёме по указанным темам, так как полагаемся на вашу любознательность. Несмотря на желание охватить всё, я не смогу в одной статье описать всю практическую сторону данных фреймворков и научить вас всему и сразу. Вместо этого я поделюсь с вами информацией и задам направление для размышлений, что и является основной целью данной публикации. Для более глубокого погружения в работу этих библиотек я рекомендую вам начать создавать на их основе собственные проекты — именно такой подход когда-то помог мне лучше всего разобраться в их устройстве. Помните, что главным наставником для вас станет практика, поэтому успехов в её освоении!&lt;/p&gt;

&lt;p&gt;Важно: цель статьи — вовсе не уговорить вас использовать Ash повсеместно и постоянно!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🧐 Что предлагает экосистема Ash
&lt;/h2&gt;

&lt;p&gt;Если вы знакомы с &lt;strong&gt;&lt;em&gt;Elixir&lt;/em&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;em&gt;Phoenix&lt;/em&gt;&lt;/strong&gt;, то знаете, что фреймворк уже содержит встроенные механизмы для упрощения разработки. Один из них — &lt;strong&gt;&lt;em&gt;LiveView&lt;/em&gt;&lt;/strong&gt;. Он позволяет отказаться от классического разделения на &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;-бэкенд и отдельный &lt;strong&gt;&lt;em&gt;JavaScript&lt;/em&gt;&lt;/strong&gt;-фронтенд: интерфейс пишется прямо внутри приложения. Это ускоряет разработку и удобно бэкенд-программистам, работающим без фронтенд-команды. Для простых и средних проектов путь с &lt;strong&gt;&lt;em&gt;LiveView&lt;/em&gt;&lt;/strong&gt; отлично подходит, но при создании масштабных систем код рискует превратиться в «спагетти», где трудно отслеживать зависимости между компонентами и находить обработчики вроде &lt;code&gt;def handle_params/3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;А как быть, если нужен качественный обособленный фронтенд, но вы устали каждый раз заново писать шаблонный &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt;-интерфейс и настраивать связь? Ответ — освоить возможности &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt;. О них и поговорим.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😮 REST с &lt;a href="https://ash-json-api.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;AshJsonApi&lt;/a&gt;: новый подход
&lt;/h2&gt;

&lt;p&gt;Стандартная практика подразумевает фиксацию контракта между фронтендом и бэкендом не только на словах, но и документально — обычно через &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt;. В нём описывают структуры данных и методы API, после чего документация передаётся команде интерфейса. Любые изменения требуют осторожности, чтобы не сломать совместимость. Даже базовая настройка такого подхода стоит немалых усилий: нужно интегрировать библиотеку генерации, создать конфигурации по спецификации OpenAPI (&lt;em&gt;Swagger&lt;/em&gt;) и вручную описать конечные точки (&lt;em&gt;endpoints&lt;/em&gt;). Если речь о переписывании существующего проекта, процесс легко занимает больше рабочего дня.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ash-json-api.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;&lt;strong&gt;AshJsonApi&lt;/strong&gt;&lt;/a&gt; предлагает альтернативу. Главное преимущество — вам больше не нужно предварительно писать спецификацию в &lt;strong&gt;&lt;em&gt;YAML/JSON&lt;/em&gt;&lt;/strong&gt; и генерировать из неё код. Для старта достаточно готового домена ресурсов и подключённого расширения &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Создать новый проект с поддержкой этого инструмента можно прямо на главной странице портала &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; через конструктор. Если проект на &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; уже есть, добавить функциональность можно одной командой:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix igniter.install ash_json_api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В процессе установки система задаёт уточняющие вопросы (&lt;em&gt;Y/N&lt;/em&gt;). Можно соглашаться со всеми предложениями или изучить каждый пункт и решить самостоятельно. После установки останется выполнить несколько простых шагов.&lt;/p&gt;

&lt;h3&gt;
  
  
  Настройка ресурса для работы с JSON:API
&lt;/h3&gt;

&lt;p&gt;Чтобы ресурсы стали доступны через &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;, подключите расширение &lt;strong&gt;&lt;em&gt;AshJsonApi.Resource&lt;/em&gt;&lt;/strong&gt;. Это позволит автоматически сгенерировать эндпоинты на основе определений домена. Добавьте &lt;code&gt;AshJsonApi.Resource&lt;/code&gt; в список &lt;code&gt;extensions&lt;/code&gt; и задайте имя ресурса в параметре &lt;code&gt;type&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshJsonApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Активация генерации эндпоинтов&lt;/span&gt;
  &lt;span class="n"&gt;json_api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="s2"&gt;"lesson"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons_allowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:map&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_ctx&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;allowed_courses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"go"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Golang"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"elixir"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Elixir"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"rust"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Rust"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;allowed_courses&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Параметр &lt;code&gt;type&lt;/code&gt; определяет имя ресурса в пространстве имён JSON:API. Он используется преимущественно для документации (&lt;em&gt;например, в &lt;strong&gt;Swagger&lt;/strong&gt; или &lt;strong&gt;Redoc&lt;/strong&gt;&lt;/em&gt;) и позволяет группировать методы по тегам.&lt;/p&gt;

&lt;p&gt;Далее опишите маршруты в домене, указав пути и связав их с конкретными действиями ресурса:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshJsonApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;json_api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;routes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;base_route&lt;/span&gt; &lt;span class="s2"&gt;"/lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Получение списка лекций"&lt;/span&gt;
        &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;primary?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Получение одной лекции"&lt;/span&gt;
        &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Создание лекции"&lt;/span&gt;
        &lt;span class="n"&gt;patch&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Изменение лекции"&lt;/span&gt;
        &lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Удаление лекции"&lt;/span&gt;

        &lt;span class="c1"&gt;# Путь относителен base_route, итоговый эндпоинт: /lessons/allowed&lt;/span&gt;
        &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/allowed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons_allowed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="ss"&gt;description:&lt;/span&gt; &lt;span class="s2"&gt;"Получение списка разрешённых лекций"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Выполнив эти шаги, вы получите шесть полностью функциональных эндпоинтов. Вся логика теперь связана с вашими &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt; внутри доменной модели. Отдельные &lt;strong&gt;&lt;em&gt;Swagger&lt;/em&gt;&lt;/strong&gt;-файлы или ручное определение маршрутов не нужны: изменение логики action в коде автоматически отражается на поведении соответствующего &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;-эндпоинта.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ключевые достоинства AshJsonApi
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Единый подход и предсказуемость
&lt;/h4&gt;

&lt;p&gt;Основное преимущество — строгая согласованность приложения. При переходе на другой тип контракта (&lt;em&gt;например, &lt;strong&gt;GraphQL&lt;/strong&gt;&lt;/em&gt;) не придётся переписывать бизнес-логику. Поскольку &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; работает поверх ваших &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt;, вы застрахованы от ситуации, когда изменения в коде не отразились в одном из интерфейсов: вся логика инкапсулирована в &lt;strong&gt;&lt;em&gt;action&lt;/em&gt;&lt;/strong&gt;, который вы «раздаёте» разным контрактам. Это заметно ускоряет разработку — по личным оценкам, готовый продукт получается примерно в пять раз быстрее. Для автоматизации можно воспользоваться командой:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix ash.patch.extend CourseHub.Courses.Lesson json_api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Указав адрес ресурса, вы позволите &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; автоматически сгенерировать всю обвязку для работы с &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Работа с данными: фильтрация, сортировка и связи
&lt;/h4&gt;

&lt;p&gt;Как бэкенд-разработчик, я раньше тратил много времени на рутинную реализацию фильтров и сортировок, вручную составляя &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt; с &lt;strong&gt;&lt;em&gt;LIMIT&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;OFFSET&lt;/em&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;em&gt;WHERE&lt;/em&gt;&lt;/strong&gt;. &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; избавляет от этого: фильтрация и упорядочивание выполняются автоматически. Фронтенду достаточно указать нужные поля и условия прямо в запросе. Это кардинально упрощает взаимодействие команд: фронтенд формирует запросы к данным самостоятельно, не отвлекая бэкенд. При этом все вычисления происходят на сервере, поэтому клиент не несёт лишней нагрузки — он лишь описывает желаемую схему данных. Полный список параметров есть в официальной документации. Пример запроса:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;GET /courses/1?include&lt;span class="o"&gt;=&lt;/span&gt;lessons&amp;amp;included_page[lessons][limit]&lt;span class="o"&gt;=&lt;/span&gt;10&amp;amp;included_page[lessons][offset]&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Запрос возвращает информацию о курсе и подгружает связанные лекции с заданной пагинацией. Ответ приходит в формате &lt;strong&gt;&lt;em&gt;application/vnd.api+json&lt;/em&gt;&lt;/strong&gt; — это стандартизированная спецификация (подробнее на &lt;a href="https://jsonapi.org/format/1.0/" rel="noopener noreferrer"&gt;jsonapi.org&lt;/a&gt;), которая структурирует JSON для предсказуемой работы.&lt;/p&gt;

&lt;p&gt;Пример ответа:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"course"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Course!"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"relationships"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"self"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1/relationships/lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"related"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1/lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"first"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1?include=lessons&amp;amp;included_page[lessons][limit]=10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1?include=lessons&amp;amp;included_page[lessons][limit]=10&amp;amp;included_page[lessons][offset]=10"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"prev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"last"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/courses/1?include=lessons&amp;amp;included_page[lessons][limit]=10&amp;amp;included_page[lessons][offset]=40"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"meta"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"included"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lessons about Elixir 1!"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lessons about Elixir 2!"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Заключение и личный опыт
&lt;/h4&gt;

&lt;p&gt;Подводя итог, поделюсь опытом. На текущем проекте создание &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt;-эндпоинтов заняло всего 20–30 минут. Основное время ушло на кастомизацию отображения документации в &lt;strong&gt;&lt;em&gt;Redoc&lt;/em&gt;&lt;/strong&gt; — вот это оказалось довольно трудоёмким (&lt;em&gt;в отличие от генерации самого &lt;strong&gt;API&lt;/strong&gt;&lt;/em&gt;). Если решите использовать &lt;strong&gt;&lt;em&gt;Redoc&lt;/em&gt;&lt;/strong&gt;, будьте готовы вручную переписывать его блоки ради красивого вывода ответов и примеров.&lt;/p&gt;

&lt;p&gt;В остальном инструмент показал себя отлично: великолепная гибкость, кастомизируемая обработка ошибок, тонкое управление доступом к эндпоинтам. Если цель — построить удобный и мощный &lt;strong&gt;&lt;em&gt;REST API&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; отличный выбор.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🙂‍↕️ AshGraphQL
&lt;/h2&gt;

&lt;p&gt;С этой библиотекой возможны сложности: не так много людей вообще работали с &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt;. Поэтому сначала дам вводную — освежить память или узнать, что это такое, — а потом посмотрим саму библиотеку.&lt;/p&gt;

&lt;h3&gt;
  
  
  Что такое GraphQL
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; — это язык запросов для &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt; и рантайм для их исполнения. Клиент сам описывает, какие данные ему нужны, а сервер возвращает ровно эту форму. Контрактом служит строго типизированная схема.&lt;/p&gt;

&lt;h4&gt;
  
  
  Фундаментальные принципы
&lt;/h4&gt;

&lt;p&gt;Взаимодействие идёт через единый &lt;strong&gt;&lt;em&gt;endpoint&lt;/em&gt;&lt;/strong&gt; (&lt;em&gt;обычно &lt;code&gt;POST /graphql&lt;/code&gt;&lt;/em&gt;). Логика строится вокруг графа типов. Запрос имеет иерархическую структуру, и ответ сервера в точности её повторяет. Это решает проблемы &lt;strong&gt;&lt;em&gt;over-fetching&lt;/em&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;em&gt;under-fetching&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Виды операций
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Query&lt;/strong&gt; — операции на чтение (идемпотентны).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutation&lt;/strong&gt; — операции на изменение данных (выполняются последовательно).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscription&lt;/strong&gt; — подписка на поток событий в реальном времени (через &lt;em&gt;WebSocket&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Схема и резолверы
&lt;/h4&gt;

&lt;p&gt;Центральный элемент — схема, описываемая на &lt;strong&gt;&lt;em&gt;SDL&lt;/em&gt;&lt;/strong&gt;. Она служит источником истины и позволяет проводить интроспекцию. Каждое поле сопоставляется с резолвером — функцией, возвращающей значение. Сервер обходит дерево запроса и вызывает резолверы для каждого поля.&lt;/p&gt;

&lt;h4&gt;
  
  
  Сложности
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Проблема N+1.&lt;/strong&gt; Резолверы работают независимо, и без оптимизации (батчинга) это приводит к множеству запросов к базе&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Кэширование.&lt;/strong&gt; Стандартные &lt;strong&gt;&lt;em&gt;HTTP&lt;/em&gt;&lt;/strong&gt;-механизмы (&lt;strong&gt;&lt;em&gt;CDN&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;ETag&lt;/em&gt;&lt;/strong&gt;) не работают, так как всё идёт через &lt;strong&gt;&lt;em&gt;POST&lt;/em&gt;&lt;/strong&gt;. Требуется нормализованный кэш на клиенте&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Безопасность.&lt;/strong&gt; Клиент может отправить глубоко вложенный запрос, перегружающий базу. Нужны ограничение глубины и анализ сложности&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ошибки.&lt;/strong&gt; Сервер почти всегда возвращает статус &lt;code&gt;200&lt;/code&gt;, а сами ошибки помещаются в массив &lt;code&gt;errors&lt;/code&gt; рядом с частичными данными. Это ломает мониторинг, основанный на кодах состояния &lt;strong&gt;&lt;em&gt;HTTP&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Инфраструктура.&lt;/strong&gt; Загрузка файлов, &lt;strong&gt;&lt;em&gt;rate limiting&lt;/em&gt;&lt;/strong&gt; и наблюдаемость требуют отдельного инфраструктурного слоя&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Где эффективен
&lt;/h4&gt;

&lt;p&gt;Технология оправдывает себя при множестве разнообразных клиентов со сложными требованиями к данным. Для одного клиента с простыми ресурсами проще взять &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt;. Для внутреннего межсервисного взаимодействия с жёсткими контрактами и минимальной задержкой лучше подходит &lt;strong&gt;&lt;em&gt;gRPC&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Что представляет собой &lt;a href="https://ash-graphql.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;AshGraphQL&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Несмотря на то что &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; пока уступает в популярности другим подходам, его интеграция с &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; сделана очень качественно. Технологию легко внедрить в проекты на базе &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Для начала добавьте библиотеку в проект:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix igniter.install ash_graphql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Затем в модуле схемы укажите используемые домены:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;domains:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Your&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Your&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Настройка ресурсов и доменов
&lt;/h3&gt;

&lt;p&gt;Чтобы ресурс стал доступен через &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt;, подключите расширение &lt;code&gt;AshGraphql.Resource&lt;/code&gt; и опишите базовый блок конфигурации:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="ss"&gt;:lesson&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ... остальная логика ресурса&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В модуле домена определите операции (ручки) через расширение &lt;code&gt;AshGraphql.Domain&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:get_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;
      &lt;span class="n"&gt;read_one&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:most_popular_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:most_popular&lt;/span&gt;
      &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;mutations&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;
      &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;
      &lt;span class="n"&gt;destroy&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Описывать эти ручки можно не только в модуле домена, но и прямо внутри ресурса.&lt;/p&gt;

&lt;p&gt;Кроме того, вы можете создавать собственные действия для кастомных ответов. Пример:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="ss"&gt;:lesson&lt;/span&gt;

  &lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:random_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:random_lesson&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:random_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:array&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:struct&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="ss"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;instance_of:&lt;/span&gt; &lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="c1"&gt;# возвращаем список структур, как объявлено в типе действия&lt;/span&gt;
      &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Генерация схемы SDL
&lt;/h4&gt;

&lt;p&gt;Как и в &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; (&lt;em&gt;где &lt;strong&gt;Swagger&lt;/strong&gt; генерируется автоматически после правок&lt;/em&gt;), схема &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; обновляется схожим образом. Пропишите путь для сохранения в &lt;code&gt;use AshGraphql&lt;/code&gt; через &lt;code&gt;generate_sdl_file:&lt;/code&gt;. Файл будет автоматически генерироваться при каждом запуске &lt;code&gt;mix ash.codegen&lt;/code&gt; (если включён &lt;code&gt;auto_generate_sdl_file?: true&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;domains:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;Domain1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Domain2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;generate_sdl_file:&lt;/span&gt; &lt;span class="s2"&gt;"priv/schema.graphql"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;auto_generate_sdl_file?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Создание подписок (Subscriptions)
&lt;/h4&gt;

&lt;p&gt;Можно настроить получение данных в реальном времени через механизм подписок. Для этого используется конфигурация &lt;strong&gt;&lt;em&gt;Absinthe&lt;/em&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# в файле схемы Absinthe&lt;/span&gt;
&lt;span class="n"&gt;subscription&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ss"&gt;:field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:type_name&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;
      &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;current_user:&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;topic:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;context_id:&lt;/span&gt; &lt;span class="s2"&gt;"user/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;_args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:unauthorized&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="no"&gt;AshGraphql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Subscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_for_subscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="no"&gt;YourResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="no"&gt;YourDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;resolution&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;actor:&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Мониторинг
&lt;/h4&gt;

&lt;p&gt;Для глубокого погружения рекомендую &lt;a href="https://hexdocs.pm/ash/monitoring.html" rel="noopener noreferrer"&gt;официальное руководство по мониторингу Ash&lt;/a&gt;. Здесь кратко опишем механизмы трассировки и события телеметрии, которые становятся доступны с этим расширением.&lt;/p&gt;

&lt;p&gt;Можно определить собственный трассировщик на уровне домена — его настройки будут иметь приоритет над глобальным параметром &lt;code&gt;config :ash, :tracer&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;graphql&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tracer&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Трассировка (Traces)
&lt;/h4&gt;

&lt;p&gt;Операции резолверов &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; и базовых пакетных загрузчиков данных оборачиваются в диапазоны (&lt;strong&gt;&lt;em&gt;spans&lt;/em&gt;&lt;/strong&gt;) с соответствующими именами. Для удобства к ним добавляется метаданное &lt;code&gt;source: :graphql&lt;/code&gt;, что позволяет фильтровать или комментировать эти данные в системе мониторинга.&lt;/p&gt;

&lt;h4&gt;
  
  
  Телеметрия (Telemetry)
&lt;/h4&gt;

&lt;p&gt;Модуль &lt;strong&gt;&lt;em&gt;AshGraphql&lt;/em&gt;&lt;/strong&gt; публикует события телеметрии, каждое из которых сопровождается суффиксами &lt;code&gt;:start&lt;/code&gt; и &lt;code&gt;:stop&lt;/code&gt;. События начала (&lt;code&gt;:start&lt;/code&gt;) содержат измерение &lt;code&gt;system_time&lt;/code&gt;, а события завершения (&lt;code&gt;:stop&lt;/code&gt;) — &lt;code&gt;system_time&lt;/code&gt; и длительность выполнения &lt;code&gt;duration&lt;/code&gt;. Все временные показатели представлены в нативных единицах времени.&lt;/p&gt;

&lt;p&gt;Список генерируемых событий:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;имя_домена&amp;gt;, :gql_mutation]&lt;/code&gt;&lt;/strong&gt; — выполнение мутации. Для детализации используйте метаданные &lt;code&gt;resource_short_name&lt;/code&gt; и &lt;code&gt;mutation&lt;/code&gt; (или &lt;code&gt;action&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;имя_домена&amp;gt;, :gql_query]&lt;/code&gt;&lt;/strong&gt; — выполнение запроса. Для детализации используйте метаданные &lt;code&gt;resource_short_name&lt;/code&gt; и &lt;code&gt;query&lt;/code&gt; (или &lt;code&gt;action&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;имя_домена&amp;gt;, :gql_relationship]&lt;/code&gt;&lt;/strong&gt; — разрешение связи. Для детализации используйте метаданные &lt;code&gt;resource_short_name&lt;/code&gt; и &lt;code&gt;relationship&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;имя_домена&amp;gt;, :gql_calculation]&lt;/code&gt;&lt;/strong&gt; — разрешение вычисления. Для детализации используйте метаданные &lt;code&gt;resource_short_name&lt;/code&gt; и &lt;code&gt;calculation&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;имя_домена&amp;gt;, :gql_relationship_batch]&lt;/code&gt;&lt;/strong&gt; — пакетное разрешение связей загрузчиком данных. Для детализации используйте метаданные &lt;code&gt;resource_short_name&lt;/code&gt; и &lt;code&gt;relationship&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;[:ash, &amp;lt;имя_домена&amp;gt;, :gql_calculation_batch]&lt;/code&gt;&lt;/strong&gt; — пакетное разрешение вычислений загрузчиком данных. Для детализации используйте метаданные &lt;code&gt;resource_short_name&lt;/code&gt; и &lt;code&gt;calculation&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Отзыв
&lt;/h3&gt;

&lt;p&gt;Честно говоря, я нечасто прибегал к такому формату взаимодействия, но из того немногого опыта, что у меня есть, скажу одно — это действительно удобно.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😳 &lt;a href="https://ash-typescript.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;AshTypescript&lt;/a&gt; — скрытая жемчужина
&lt;/h2&gt;

&lt;p&gt;Мы уже познакомились с &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;em&gt;AshGraphQL&lt;/em&gt;&lt;/strong&gt;, и это был полезный опыт. Эти два подхода — своего рода индустриальный стандарт веб-разработки, и особых сюрпризов для опытных специалистов в них нет. Но у &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; есть «козырная карта» — &lt;a href="https://ash-typescript.hexdocs.pm/readme.html" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;AshTypescript&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Представьте, что вы хотите взаимодействовать с фронтендом с типобезопасностью уровня &lt;strong&gt;&lt;em&gt;gRPC&lt;/em&gt;&lt;/strong&gt;, сохраняя структуру эндпоинтов как в &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt; и гибкость работы с данными как в &lt;strong&gt;&lt;em&gt;AshGraphQL&lt;/em&gt;&lt;/strong&gt;. Именно это и решает &lt;strong&gt;&lt;em&gt;AshTypescript&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Принцип работы
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Определение структуры.&lt;/strong&gt; Вы описываете домен и ресурсы, указывая нужные действия (&lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt;) и атрибуты&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Подключение расширений.&lt;/strong&gt; В ресурс добавляется &lt;code&gt;AshTypescript.Resource&lt;/code&gt;, а в домен — &lt;code&gt;AshTypescript.Rpc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Настройка ресурса.&lt;/strong&gt; В блоке &lt;code&gt;typescript&lt;/code&gt; внутри ресурса задаётся его имя для генерации
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;domain:&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshTypescript&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# Подключаем расширение&lt;/span&gt;

  &lt;span class="c1"&gt;# Задаём имя типа для сгенерированного RPC-файла&lt;/span&gt;
  &lt;span class="n"&gt;typescript&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;type_name&lt;/span&gt; &lt;span class="s2"&gt;"Lesson"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Стандартные CRUD-действия&lt;/span&gt;
  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Описываем атрибуты ресурса&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;uuid_primary_key&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default:&lt;/span&gt; &lt;span class="s2"&gt;"Какой-то текст!"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Описываем связь с Course&lt;/span&gt;
  &lt;span class="n"&gt;relationships&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:course&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Course&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; 
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Привязка RPC-методов.&lt;/strong&gt; В модуле домена вы определяете, какие методы будут доступны фронтенду, и связываете их с действиями ресурса.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;extensions:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;AshTypescript&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Rpc&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;# Публичные RPC-методы для фронтенда&lt;/span&gt;
  &lt;span class="n"&gt;typescript_rpc&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:list_lessons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:create_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:get_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:delete_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;
      &lt;span class="n"&gt;rpc_action&lt;/span&gt; &lt;span class="ss"&gt;:update_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Генерация кода.&lt;/strong&gt; Запустив &lt;code&gt;mix ash_typescript.codegen&lt;/code&gt; (&lt;em&gt;или общую &lt;code&gt;mix ash.codegen&lt;/code&gt;&lt;/em&gt;), вы получите сгенерированные файлы по пути из конфигурации. Настройки лежат в &lt;code&gt;config/config.exs&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="ss"&gt;:ash_typescript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;ash_domains:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="no"&gt;Backend&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="ss"&gt;output_file:&lt;/span&gt; &lt;span class="s2"&gt;"assets/js/ash_rpc.ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Путь для сохранения сгенерированного кода&lt;/span&gt;
  &lt;span class="ss"&gt;run_endpoint:&lt;/span&gt; &lt;span class="s2"&gt;"/rpc/run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# Эндпоинт для отправки запросов&lt;/span&gt;
  &lt;span class="ss"&gt;validate_endpoint:&lt;/span&gt; &lt;span class="s2"&gt;"/rpc/validate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Эндпоинт для валидации данных по схеме&lt;/span&gt;
  &lt;span class="ss"&gt;input_field_formatter:&lt;/span&gt; &lt;span class="ss"&gt;:camel_case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Формат имён полей на входе&lt;/span&gt;
  &lt;span class="ss"&gt;output_field_formatter:&lt;/span&gt; &lt;span class="ss"&gt;:camel_case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Формат имён полей на выходе&lt;/span&gt;
  &lt;span class="ss"&gt;require_tenant_parameters:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# Мультитенантность&lt;/span&gt;
  &lt;span class="ss"&gt;generate_zod_schemas:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# Генерация схем для Zod&lt;/span&gt;
  &lt;span class="ss"&gt;generate_phx_channel_rpc_actions:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;generate_validation_functions:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;zod_import_path:&lt;/span&gt; &lt;span class="s2"&gt;"zod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;zod_schema_suffix:&lt;/span&gt; &lt;span class="s2"&gt;"ZodSchema"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;phoenix_import_path:&lt;/span&gt; &lt;span class="s2"&gt;"phoenix"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Zod&lt;/strong&gt; — библиотека для описания и проверки структуры данных. Она позволяет создавать схемы и автоматически валидировать по ним входящие данные.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ключевые преимущества Zod:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ориентированность на TypeScript&lt;/strong&gt; — автоматически выводит типы&lt;/li&gt;
&lt;li&gt;Интуитивно понятный и легко читаемый API&lt;/li&gt;
&lt;li&gt;Поддержка сложных правил валидации (&lt;em&gt;объединения типов, пересечения, уточнения&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Высокая производительность и минимальный вес&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Начиная с новых версий, AshTypescript генерирует не один монолитный файл, а несколько: помимо &lt;code&gt;ash_rpc.ts&lt;/code&gt; появляются общие типы (&lt;code&gt;ash_types.ts&lt;/code&gt;) и схемы &lt;strong&gt;&lt;em&gt;Zod&lt;/em&gt;&lt;/strong&gt; (&lt;code&gt;ash_zod.ts&lt;/code&gt;), которые импортируются из общего места.&lt;/p&gt;

&lt;p&gt;В итоге вам остаётся лишь передать сгенерированные файлы на фронтенд. Разработчики клиента просто вызывают методы, например &lt;code&gt;listLessons()&lt;/code&gt;, без ручного описания &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;. Это делает интеграцию невероятно простой и удобной.&lt;/p&gt;

&lt;h3&gt;
  
  
  Механизмы сортировки, фильтрации и проекции полей
&lt;/h3&gt;

&lt;p&gt;В стандартной серверной разработке приходится вручную конструировать &lt;strong&gt;&lt;em&gt;SQL&lt;/em&gt;&lt;/strong&gt;-запросы для сортировки, фильтрации и выборки, чтобы избежать избыточной передачи данных. В &lt;strong&gt;&lt;em&gt;AshTypescript&lt;/em&gt;&lt;/strong&gt; реализованы те же возможности, что и в &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt;, — гибко и типобезопасно:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CREATE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createLessonResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createLesson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Elixir lesson about Ash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;very high!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;createLessonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create failed:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createLessonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createLessonResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// READ (single)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getLesson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getLesson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;course&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// READ (list)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lessons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;listLessons&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// UPDATE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateLessonResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateLessson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yoo chill im just a vessel!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// DELETE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteLessonResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;deleteLesson&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lessonId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;headers&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Преимущества использования AshTypescript
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Гарантия целостности контракта.&lt;/strong&gt; Не нужно беспокоиться о рассинхроне интерфейса, как это бывает с «ручным» &lt;strong&gt;&lt;em&gt;gRPC&lt;/em&gt;&lt;/strong&gt;. Единственный источник истины — генератор &lt;em&gt;.ts&lt;/em&gt;-файлов&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Автоматизация создания API.&lt;/strong&gt; Отпадает ручное написание методов и эндпоинтов. Достаточно описать ресурс и &lt;code&gt;typescript_rpc&lt;/code&gt; в домене и дать методам уникальные имена&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Минимизация шаблонного кода.&lt;/strong&gt; Встроенные инструменты для сортировки, фильтрации и выборки полей (&lt;em&gt;&lt;strong&gt;select&lt;/strong&gt;/проекции&lt;/em&gt;) делают работу бесшовной&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Простая интеграция с другими командами.&lt;/strong&gt; Сгенерированные файлы (&lt;strong&gt;&lt;em&gt;ash_rpc.ts&lt;/em&gt;, &lt;em&gt;ash_types.ts&lt;/em&gt;, &lt;em&gt;ash_zod.ts&lt;/em&gt;&lt;/strong&gt;) можно передать коллегам — они начнут работу немедленно, не тратя время на изучение документации&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Впечатления
&lt;/h3&gt;

&lt;p&gt;Узнав об этом способе организации взаимодействия между сервером и клиентом, я вновь испытал энтузиазм к программированию. Ручное написание эндпоинтов монотонно, а описание схем для &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; отнимает много времени. Декларативное же описание &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt; на бэкенде и простой вызов методов на фронтенде — это быстро и удобно. Такой подход убирает до 80% лишнего кода и многократно ускоряет разработку.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🧮 Сравнение трёх подходов
&lt;/h2&gt;

&lt;p&gt;У всех трёх способов один фундамент — ваши &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt;. Меняется только «витрина», через которую логика выходит наружу. Отсюда и главный вывод: выбор способа не запирает вас в нём навсегда — при необходимости можно добавить второй контракт поверх той же логики.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Критерий&lt;/th&gt;
&lt;th&gt;AshJsonApi (REST / JSON:API)&lt;/th&gt;
&lt;th&gt;AshGraphQL&lt;/th&gt;
&lt;th&gt;AshTypescript&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Тип контракта&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;REST по стандарту JSON:API&lt;/td&gt;
&lt;td&gt;GraphQL-схема (SDL)&lt;/td&gt;
&lt;td&gt;Сгенерированные &lt;code&gt;.ts&lt;/code&gt;-файлы (RPC)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Источник истины&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ваши &lt;em&gt;actions&lt;/em&gt; + автоген OpenAPI&lt;/td&gt;
&lt;td&gt;ваши &lt;em&gt;actions&lt;/em&gt; + автоген SDL&lt;/td&gt;
&lt;td&gt;ваши &lt;em&gt;actions&lt;/em&gt; + автоген TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Как клиент задаёт запрос&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;query-параметры (&lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;, &lt;code&gt;include&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;GraphQL-запрос с деревом полей&lt;/td&gt;
&lt;td&gt;типизированные вызовы (&lt;code&gt;fields&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Гибкость выборки&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;высокая&lt;/td&gt;
&lt;td&gt;максимальная&lt;/td&gt;
&lt;td&gt;высокая&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Индустриальный стандарт&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;да, повсеместно&lt;/td&gt;
&lt;td&gt;да, но нишевый&lt;/td&gt;
&lt;td&gt;нет, специфичен для стека Ash + TS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Типобезопасность на фронте&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;через кодоген из OpenAPI&lt;/td&gt;
&lt;td&gt;через кодоген из схемы&lt;/td&gt;
&lt;td&gt;нативная, из коробки&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Реалтайм&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;нет из коробки&lt;/td&gt;
&lt;td&gt;да (subscriptions)&lt;/td&gt;
&lt;td&gt;да (Phoenix channels)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HTTP-кэширование&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;работает (GET)&lt;/td&gt;
&lt;td&gt;затруднено (POST)&lt;/td&gt;
&lt;td&gt;зависит от реализации&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Порог входа на фронте&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;низкий&lt;/td&gt;
&lt;td&gt;средний/высокий&lt;/td&gt;
&lt;td&gt;низкий (для TypeScript-команд)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Идеально для&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;публичные и внешние API&lt;/td&gt;
&lt;td&gt;много разных клиентов со сложными запросами&lt;/td&gt;
&lt;td&gt;full-stack Elixir + TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Как выбирать на практике:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AshJsonApi&lt;/strong&gt; — когда нужен привычный &lt;strong&gt;&lt;em&gt;REST&lt;/em&gt;&lt;/strong&gt;, публичный или внешний &lt;strong&gt;&lt;em&gt;API&lt;/em&gt;&lt;/strong&gt;, кэширование на уровне &lt;strong&gt;&lt;em&gt;HTTP&lt;/em&gt;&lt;/strong&gt; и максимальная совместимость с любыми клиентами. Самый «нейтральный» выбор&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AshGraphQL&lt;/strong&gt; — когда клиентов много и у них разные, часто вложенные требования к данным, а под-/пере-выборка (&lt;strong&gt;&lt;em&gt;over/under-fetching&lt;/em&gt;&lt;/strong&gt;) реально мешает. Готовьтесь платить за это батчингом против N+1, ограничением сложности запросов и клиентским кэшем&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AshTypescript&lt;/strong&gt; — когда и бэкенд, и фронтенд ваши, и фронтенд на &lt;strong&gt;&lt;em&gt;TypeScript&lt;/em&gt;&lt;/strong&gt;. Даёт сквозную типобезопасность без ручного контракта: переименовали поле в &lt;strong&gt;&lt;em&gt;Elixir&lt;/em&gt;&lt;/strong&gt; — сразу получили ошибку компиляции в &lt;strong&gt;&lt;em&gt;TS&lt;/em&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Если сомневаетесь и клиент один и относительно простой — начните с &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt;. Если позже понадобится &lt;strong&gt;&lt;em&gt;GraphQL&lt;/em&gt;&lt;/strong&gt; или типобезопасный &lt;strong&gt;&lt;em&gt;RPC&lt;/em&gt;&lt;/strong&gt;, вы просто добавите ещё один контракт поверх тех же &lt;strong&gt;&lt;em&gt;actions&lt;/em&gt;&lt;/strong&gt;, не переписывая логику.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😴 Заключение
&lt;/h2&gt;

&lt;p&gt;Мир &lt;strong&gt;&lt;em&gt;Ash&lt;/em&gt;&lt;/strong&gt; даёт не просто удобный формат работы с БД и отсутствие бойлерплейта — он позволяет делать приложения быстро, качественно и с удовольствием. На полное освоение этих методов уйдёт чуть больше времени, но оно окупится не раз и здорово поможет в ваших проектах. Успехов!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🥴 От автора
&lt;/h2&gt;

&lt;p&gt;Спасибо большое за интерес к этой статье! Надеюсь, она помогла разобраться, что представляет собой &lt;strong&gt;&lt;em&gt;AshJsonApi&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;AshGraphQL&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;AshTypescript&lt;/em&gt;&lt;/strong&gt; и зачем они используются!&lt;/p&gt;

&lt;p&gt;Если эта статья пришлась вам по душе и хочется ещё материалов такого плана, присоединяйтесь ко мне в моём &lt;a href="https://t.me/devs_inc" rel="noopener noreferrer"&gt;телеграм-канале&lt;/a&gt;, где выкладываю обзоры книг, публикации по &lt;strong&gt;&lt;em&gt;Elixir&lt;/em&gt;&lt;/strong&gt;, переводы технической литературы и интересные новости!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>ash</category>
      <category>phoenix</category>
      <category>backend</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Tue, 09 Jun 2026 13:27:15 +0000</pubDate>
      <link>https://dev.to/adamanq/-41i3</link>
      <guid>https://dev.to/adamanq/-41i3</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ua3mqj/forth-made-neural-networks-suffer-4p7h" class="crayons-story__hidden-navigation-link"&gt;Forth made neural networks suffer&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ua3mqj" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" alt="ua3mqj profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ua3mqj" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alexey Bolshakov
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alexey Bolshakov
                
              
              &lt;div id="story-author-preview-content-3856476" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ua3mqj" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alexey Bolshakov&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ua3mqj/forth-made-neural-networks-suffer-4p7h" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 9&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ua3mqj/forth-made-neural-networks-suffer-4p7h" id="article-link-3856476"&gt;
          Forth made neural networks suffer
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/forth"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;forth&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/toolchain"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;toolchain&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ua3mqj/forth-made-neural-networks-suffer-4p7h" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ua3mqj/forth-made-neural-networks-suffer-4p7h#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            18 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>deeplearning</category>
      <category>machinelearning</category>
      <category>programming</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Tue, 09 Jun 2026 09:05:14 +0000</pubDate>
      <link>https://dev.to/adamanq/-nlp</link>
      <guid>https://dev.to/adamanq/-nlp</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih" class="crayons-story__hidden-navigation-link"&gt;Forth заставил страдать нейросети&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ua3mqj" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" alt="ua3mqj profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ua3mqj" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alexey Bolshakov
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alexey Bolshakov
                
              
              &lt;div id="story-author-preview-content-3853851" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ua3mqj" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alexey Bolshakov&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 9&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih" id="article-link-3853851"&gt;
          Forth заставил страдать нейросети
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/forth"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;forth&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/toochain"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;toochain&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ua3mqj/forth-zastavil-stradat-nieirosieti-2aih#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              1&lt;span class="hidden s:inline"&gt;&amp;nbsp;comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Tue, 19 May 2026 09:17:52 +0000</pubDate>
      <link>https://dev.to/adamanq/-51k</link>
      <guid>https://dev.to/adamanq/-51k</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p" class="crayons-story__hidden-navigation-link"&gt;FMix: пакетный менеджер для Forth&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ua3mqj" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" alt="ua3mqj profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ua3mqj" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alexey Bolshakov
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alexey Bolshakov
                
              
              &lt;div id="story-author-preview-content-3695970" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ua3mqj" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alexey Bolshakov&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p" id="article-link-3695970"&gt;
          FMix: пакетный менеджер для Forth
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/forth"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;forth&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tooling"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tooling&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ua3mqj/fmix-pakietnyi-mieniedzhier-dlia-forth-o3p#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Ash Framework: Introduction</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Sat, 04 Apr 2026 08:18:54 +0000</pubDate>
      <link>https://dev.to/adamanq/ash-framework-introduction-3lm3</link>
      <guid>https://dev.to/adamanq/ash-framework-introduction-3lm3</guid>
      <description>&lt;p&gt;When a newcomer starts with Elixir and Phoenix, they are almost always introduced to &lt;strong&gt;Ecto&lt;/strong&gt; first. You create schemas, write changesets, and move queries into contexts. This approach is flexible, but as the project grows, a serious problem begins to emerge 😟&lt;/p&gt;

&lt;p&gt;Business logic starts to spread across dozens of files. Eventually, confusion arises about where to place request handling code and how to make sense of the existing mess. After a year or two, it becomes difficult to understand where the “right” place for code is, and new developers spend weeks just getting up to speed with the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ash Framework&lt;/strong&gt; was created precisely to solve this pain 💪&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important clarification&lt;/strong&gt;: Ash is &lt;strong&gt;not a replacement for Phoenix&lt;/strong&gt; and not a new web framework. It is a powerful declarative layer that organizes business logic and data on top of the existing ecosystem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Preface
&lt;/h2&gt;

&lt;p&gt;It’s important to understand that the goal of this article is not to convince you to use Ash everywhere and all the time!&lt;/p&gt;

&lt;p&gt;Personally, I love Ash because it allows you to gather all necessary logic in a single space, where it belongs. This tool significantly simplifies work, reducing boilerplate code and freeing you from the tedious implementation of standard CRUD.&lt;/p&gt;

&lt;p&gt;If you’re ready to get acquainted with such a tool but plan to use it only when truly justified—read on without hesitation. Ash is not a universal cure-all, but rather a convenient set of useful tools.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 What is Ash?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.ash-hq.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/a&gt; is a framework for fast and efficient application development in Elixir. Its goal is to simplify launching new projects and make maintaining existing solutions convenient, ensuring excellent performance and stability even after long periods of active development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key idea of Ash is:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Precisely define the structure of your business logic, and let Ash handle all the rest (database migrations, API interface creation, data validation, access control, form generation).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The framework integrates beautifully with popular tools like Phoenix, PostgreSQL, GraphQL, and LiveView.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🔑 Key Abstractions in Ash
&lt;/h2&gt;

&lt;p&gt;Everything in Ash is built around three main concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/ash/dsl-ash-resource.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Resource&lt;/strong&gt;&lt;/a&gt; — description of a single business entity&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/ash/actions.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Action&lt;/strong&gt;&lt;/a&gt; — an operation that can be performed on a resource&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/ash/domains.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Domain&lt;/strong&gt;&lt;/a&gt; — logical grouping of resources within one business area&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s break down each concept in detail!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 What is a Resource?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Resource&lt;/strong&gt; is &lt;strong&gt;one module&lt;/strong&gt; where a &lt;strong&gt;single business entity&lt;/strong&gt; of your application (User, Lesson, Order, Post, Payment, etc.) is fully described.&lt;/p&gt;

&lt;p&gt;Everything related to this entity is collected in one place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data structure (fields)&lt;/li&gt;
&lt;li&gt;Relationships with other entities&lt;/li&gt;
&lt;li&gt;Operations that can be performed (CRUD + custom)&lt;/li&gt;
&lt;li&gt;Validation rules&lt;/li&gt;
&lt;li&gt;Authorization (who can do what)&lt;/li&gt;
&lt;li&gt;Computed fields&lt;/li&gt;
&lt;li&gt;Indexes and constraints&lt;/li&gt;
&lt;li&gt;Storage settings in the database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Resource in Ash unifies in one module what in classic Elixir/Phoenix is usually spread across several parts: Ecto Schema, Changesets, Context functions, as well as authorization logic and computed fields.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Resource — The Heart of Ash
&lt;/h3&gt;

&lt;p&gt;Ash is built around the idea of &lt;strong&gt;Resource-Oriented Design&lt;/strong&gt;. You describe &lt;strong&gt;what&lt;/strong&gt; an entity is and &lt;strong&gt;what&lt;/strong&gt; can be done with it—and Ash generates the rest: SQL queries, APIs, forms, migrations, validation, etc.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Complete Example of a Modern Resource
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:course_hub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;domain:&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;data_layer:&lt;/span&gt; &lt;span class="no"&gt;AshPostgres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DataLayer&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== STORAGE ====================&lt;/span&gt;
  &lt;span class="n"&gt;postgres&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="s2"&gt;"lessons"&lt;/span&gt;
    &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== FIELDS (Attributes) ====================&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;uuid_primary_key&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;

    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;allow_nil?&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="ss"&gt;min_length:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max_length:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;public?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;public?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:atom&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;default?&lt;/span&gt; &lt;span class="ss"&gt;:draft&lt;/span&gt;
      &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="ss"&gt;one_of:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;create_timestamp&lt;/span&gt; &lt;span class="ss"&gt;:inserted_at&lt;/span&gt;
    &lt;span class="n"&gt;update_timestamp&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== RELATIONSHIPS ====================&lt;/span&gt;
  &lt;span class="n"&gt;relationships&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;allow_nil?&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Comment&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;many_to_many&lt;/span&gt; &lt;span class="ss"&gt;:tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tag&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;through&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LessonTag&lt;/span&gt;
      &lt;span class="n"&gt;source_attribute_on_join_resource&lt;/span&gt; &lt;span class="ss"&gt;:lesson_id&lt;/span&gt;
      &lt;span class="n"&gt;destination_attribute_on_join_resource&lt;/span&gt; &lt;span class="ss"&gt;:tag_id&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== ACTIONS ====================&lt;/span&gt;
  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Custom read&lt;/span&gt;
    &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;pagination&lt;/span&gt; &lt;span class="ss"&gt;offset?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;countable:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sort:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;inserted_at:&lt;/span&gt; &lt;span class="ss"&gt;:desc&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Custom create with logic&lt;/span&gt;
    &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Fully custom action&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:calculate_reading_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== COMPUTED FIELDS ====================&lt;/span&gt;
  &lt;span class="n"&gt;calculations&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;calculate&lt;/span&gt; &lt;span class="ss"&gt;:reading_time_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;calculation&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Float&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== INDEXES ====================&lt;/span&gt;
  &lt;span class="n"&gt;identities&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;identity&lt;/span&gt; &lt;span class="ss"&gt;:unique_title_per_author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:author_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== AUTHORIZATION ====================&lt;/span&gt;
  &lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;action_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;authorize_if&lt;/span&gt; &lt;span class="n"&gt;always&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;authorize_if&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Main Sections of Resource
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Section&lt;/th&gt;
&lt;th&gt;Describes&lt;/th&gt;
&lt;th&gt;Analog in Classic Elixir/Phoenix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;attributes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fields and their types + validation&lt;/td&gt;
&lt;td&gt;Ecto Schema + fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;relationships&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relationships with other resources&lt;/td&gt;
&lt;td&gt;Ecto associations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;actions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All possible operations (CRUD + custom)&lt;/td&gt;
&lt;td&gt;Context functions + Changesets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;calculations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Computed fields (on the fly)&lt;/td&gt;
&lt;td&gt;Virtual fields / functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aggregates&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aggregates (count, sum, avg, etc.)&lt;/td&gt;
&lt;td&gt;Queries with group_by&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;identities&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique indexes&lt;/td&gt;
&lt;td&gt;Ecto unique constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;policies&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authorization rules&lt;/td&gt;
&lt;td&gt;Manual permission checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;postgres&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL storage settings&lt;/td&gt;
&lt;td&gt;Migrations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features of Resource
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Everything in one file&lt;/strong&gt; — a huge plus for understanding the entity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introspection&lt;/strong&gt; — Ash can read all information about a resource at runtime (fields, actions, policies). This is used for API generation, forms, admin panels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarativeness&lt;/strong&gt; — you describe &lt;strong&gt;what&lt;/strong&gt; should be there, not &lt;strong&gt;how&lt;/strong&gt; to implement it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensibility&lt;/strong&gt; — you can add your own &lt;code&gt;changes&lt;/code&gt;, &lt;code&gt;preparations&lt;/code&gt;, &lt;code&gt;validations&lt;/code&gt;, &lt;code&gt;notifiers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded Resources&lt;/strong&gt; — you can create resources stored not in a separate table but as JSON/Map within another resource.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  When to Create a New Resource?
&lt;/h3&gt;

&lt;p&gt;Almost always when a new business entity appears (a table or even a complex JSON object).&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ More About Actions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Action&lt;/strong&gt; is an &lt;strong&gt;explicitly declared operation&lt;/strong&gt; that can be performed on a resource.&lt;br&gt;
All data interaction in Ash goes &lt;strong&gt;only through Actions&lt;/strong&gt;. You cannot simply take a record and change it manually—you always call an action.&lt;/p&gt;

&lt;p&gt;This replaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Regular functions in Phoenix Contexts&lt;/li&gt;
&lt;li&gt;Changesets in Ecto&lt;/li&gt;
&lt;li&gt;Controllers and service layers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Five Types of Actions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Action Type&lt;/th&gt;
&lt;th&gt;What It Does?&lt;/th&gt;
&lt;th&gt;In Transaction?&lt;/th&gt;
&lt;th&gt;Returns?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✨&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;create&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Creates a new record&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;New resource record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔍&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;read&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reads one record or a list&lt;/td&gt;
&lt;td&gt;No (can be enabled)&lt;/td&gt;
&lt;td&gt;Record or list of records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✏️&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;update&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Updates an existing record&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Updated record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗑️&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;destroy&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deletes a record&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Deleted record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;action&lt;/code&gt;&lt;/strong&gt; (Generic)&lt;/td&gt;
&lt;td&gt;Any custom business logic&lt;/td&gt;
&lt;td&gt;No (can be enabled)&lt;/td&gt;
&lt;td&gt;Any value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Complete Example of All Action Types
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Standard CRUD (defaults)&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Custom Read&lt;/span&gt;
    &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;pagination&lt;/span&gt; &lt;span class="ss"&gt;offset?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;countable:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default_limit:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
      &lt;span class="n"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sort:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;inserted_at:&lt;/span&gt; &lt;span class="ss"&gt;:desc&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;load:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comment_count&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:for_user&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Custom Create&lt;/span&gt;
    &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

      &lt;span class="c1"&gt;# Changes (changes)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;NotifySubscribers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;topic:&lt;/span&gt; &lt;span class="s2"&gt;"new_lessons"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;# Hooks&lt;/span&gt;
      &lt;span class="n"&gt;after_action&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Custom Update&lt;/span&gt;
    &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="ss"&gt;:archive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:archive_reason&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:archived_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 5. Generic Action (fully custom)&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:calculate_reading_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:words_per_minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default?:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

      &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;words_per_minute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  How to Call Actions
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Through Domain (recommended way)&lt;/span&gt;
&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;reason:&lt;/span&gt; &lt;span class="s2"&gt;"Готово"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Or with Ash.Query / Ash.Changeset for complex cases&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;for_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Main Capabilities of Actions
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. &lt;code&gt;argument&lt;/code&gt; — Input Parameters
&lt;/h4&gt;

&lt;p&gt;Allows passing data into an action and validating it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;constraints:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;min_length:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default?:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. &lt;code&gt;accept&lt;/code&gt; — Which Attributes Can Be Changed
&lt;/h4&gt;

&lt;p&gt;Limits the list of fields that can be modified during &lt;code&gt;create&lt;/code&gt; or &lt;code&gt;update&lt;/code&gt;. Very useful for security.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:register&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="ss"&gt;:update_profile&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:bio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:avatar_url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. &lt;code&gt;change&lt;/code&gt; — Data Transformations
&lt;/h4&gt;

&lt;p&gt;The most powerful and frequently used part of Actions. Here the main business logic happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;

  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;NotifySubscribers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;topic:&lt;/span&gt; &lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;change_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate_slug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. &lt;code&gt;prepare&lt;/code&gt; — Query Preparation (mainly for &lt;code&gt;read&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;Allows pre-configuring the query: sorting, preloading relations, additional filters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;sort:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;inserted_at:&lt;/span&gt; &lt;span class="ss"&gt;:desc&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;load:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;limit:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Hooks (before/after)
&lt;/h4&gt;

&lt;p&gt;Allows executing code before or after an action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;validate_business_rules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;after_action&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;LessonNotifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;after_transaction&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;broadcast_new_lesson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; 
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. &lt;code&gt;transaction?&lt;/code&gt; — Transaction Management
&lt;/h4&gt;

&lt;p&gt;Determines whether the action should be wrapped in a database transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:transfer_money&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:from_account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:to_account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:decimal&lt;/span&gt;

  &lt;span class="n"&gt;transaction?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

  &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;# complex operation with multiple records&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  7. &lt;code&gt;pipe_through&lt;/code&gt; — Reusing Logic
&lt;/h4&gt;

&lt;p&gt;Allows creating common pipelines and attaching them to different actions (very useful for DRY).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In Resource&lt;/span&gt;
&lt;span class="n"&gt;pipelines&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:ensure_admin&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;ensure_actor_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;actor_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:create_admin_content&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:ensure_admin&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="ss"&gt;:update_admin_content&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:ensure_admin&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Why Actions Are Awesome?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full typing and introspection&lt;/strong&gt; — Ash knows everything about an action at runtime (used for GraphQL, forms, admin panels).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarativeness&lt;/strong&gt; — code reads like business documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt; — one action can be used in LiveView, API, Background Job, Tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; — authorization policies are tied to specific actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability&lt;/strong&gt; — easy to test individual actions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison with Classic Elixir/Phoenix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Where is logic?&lt;/th&gt;
&lt;th&gt;Number of files&lt;/th&gt;
&lt;th&gt;Clarity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ecto + Context&lt;/td&gt;
&lt;td&gt;Context + Changeset&lt;/td&gt;
&lt;td&gt;4–8&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ash Actions&lt;/td&gt;
&lt;td&gt;In one Resource&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Actions are where all your business logic lives.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🏢 What is a Domain?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Domain&lt;/strong&gt; is a &lt;strong&gt;logical container&lt;/strong&gt; that groups related &lt;strong&gt;Resources&lt;/strong&gt;.&lt;br&gt;
It is similar to &lt;strong&gt;Phoenix Context&lt;/strong&gt;, but much more powerful. If Resource describes one entity (like &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Lesson&lt;/code&gt;, &lt;code&gt;Order&lt;/code&gt;), then Domain is a "module" or "bounded context" (in DDD terms) that brings together several entities of one business area.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Accounts&lt;/code&gt; — domain for users, profiles, authorization.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Courses&lt;/code&gt; — domain for courses, lessons, tests, enrollments.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Billing&lt;/code&gt; — domain for payments, subscriptions, invoices.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Therapy&lt;/code&gt; — domain for therapy sessions, conversations, messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Three Main Goals of Domain
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Grouping Resources&lt;/strong&gt;
Organizing the project. Instead of all resources being scattered, they are explicitly grouped into logical units.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized Code Interface&lt;/strong&gt;
Domain allows defining convenient wrapper functions for calling resource actions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Settings and Cross-Cutting Concerns&lt;/strong&gt;
Here you can set behavior that applies to all resources in the domain (timeouts, default pagination, authorization, etc.).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  How Domain Looks in Practice
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:course_hub&lt;/span&gt;

  &lt;span class="c1"&gt;# 1. Grouping resources&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Course&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Quiz&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Enrollment&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# 2. Code Interface — convenient functions&lt;/span&gt;
  &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="ss"&gt;:get_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action:&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;args:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;get?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="ss"&gt;:list_published_courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action:&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;
  &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="ss"&gt;:enroll_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action:&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;resource:&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Enrollment&lt;/span&gt;

  &lt;span class="c1"&gt;# 3. Shared settings&lt;/span&gt;
  &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="ss"&gt;:timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;default_page_size&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Enabling AshAdmin (admin panel)&lt;/span&gt;
  &lt;span class="n"&gt;admin&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;show?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Why Do You Need Domain?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Organization and boundaries.&lt;/strong&gt; Clearly separates different parts of the application (like bounded contexts in DDD). &lt;code&gt;Accounts&lt;/code&gt; doesn't know about &lt;code&gt;Billing&lt;/code&gt; without explicit relations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convenient public API.&lt;/strong&gt; Instead of &lt;code&gt;Ash.read!(CourseHub.Courses.Lesson, ...)&lt;/code&gt;, you write beautifully:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_lesson!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_published_courses!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralized management.&lt;/strong&gt; You can disable authorization for the entire domain in dev mode, set common policies, notifiers, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introspection and extensions.&lt;/strong&gt; Many Ash extensions (GraphQL, JsonApi, Phoenix) work at the domain level.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing and isolation.&lt;/strong&gt; Easier to test one business area as a whole.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices When Working with Domains
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One resource — one domain.&lt;/strong&gt; A resource cannot belong to multiple domains simultaneously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How many domains to make?&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Small project → 1–3 domains.&lt;/li&gt;
&lt;li&gt;Medium/large → one per major business area (Accounts, Core, Billing, Notifications, etc.).
This helps with scaling and understanding architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Interface&lt;/strong&gt; — a very powerful feature. You can define not only simple wrappers but also those with preset filters, loads, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison: Domain vs Resource
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it describes&lt;/td&gt;
&lt;td&gt;One entity&lt;/td&gt;
&lt;td&gt;Group of entities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where logic lies&lt;/td&gt;
&lt;td&gt;Attributes, actions, policies&lt;/td&gt;
&lt;td&gt;Grouping + shared settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code call&lt;/td&gt;
&lt;td&gt;Through domain&lt;/td&gt;
&lt;td&gt;Public app interface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analog in Phoenix&lt;/td&gt;
&lt;td&gt;Schema + Context (partially)&lt;/td&gt;
&lt;td&gt;Phoenix Context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantity per project&lt;/td&gt;
&lt;td&gt;Many (one per table)&lt;/td&gt;
&lt;td&gt;Few (per business area)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Why Domain Is Especially Useful?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Project grows &amp;gt;10–15 resources.&lt;/li&gt;
&lt;li&gt;Complex authorization (policies) needed.&lt;/li&gt;
&lt;li&gt;Doing GraphQL / JSON:API / Admin panel.&lt;/li&gt;
&lt;li&gt;Team development — everyone needs to understand boundaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  ⚔️ Ash + Phoenix vs Ecto + Phoenix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Phoenix + Ecto (classic) 😐&lt;/th&gt;
&lt;th&gt;Phoenix + Ash 🔥&lt;/th&gt;
&lt;th&gt;Who wins?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Business logic organization&lt;/td&gt;
&lt;td&gt;Scattered across Context, Changeset, separate modules&lt;/td&gt;
&lt;td&gt;Everything in one Resource&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CRUD operations&lt;/td&gt;
&lt;td&gt;Write manually: create, read, update, delete + boilerplate&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;defaults [:create, :read, :update, :destroy]&lt;/code&gt; — done ✨&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;Manual checks / Bodyguard / scopes&lt;/td&gt;
&lt;td&gt;Declarative Policies (secure by default) ⚡&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Computed fields&lt;/td&gt;
&lt;td&gt;Virtual fields + functions in Context&lt;/td&gt;
&lt;td&gt;Calculations — auto-loaded and cached&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Number of files per entity&lt;/td&gt;
&lt;td&gt;3–6+ files (Schema + Context + Changeset + Policy)&lt;/td&gt;
&lt;td&gt;One file — Resource&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API generation&lt;/td&gt;
&lt;td&gt;Absinthe / GraphQL manually + lots of code&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AshGraphql&lt;/code&gt; or &lt;code&gt;AshJsonApi&lt;/code&gt; — add extension and done&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LiveView forms&lt;/td&gt;
&lt;td&gt;Phoenix.Component + manual validation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AshPhoenix.Form&lt;/code&gt; — forms know everything themselves&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-tenancy&lt;/td&gt;
&lt;td&gt;Write yourself (or use schemas)&lt;/td&gt;
&lt;td&gt;Built into Policies (attribute-based or context-based)&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintainability after 2–3 years&lt;/td&gt;
&lt;td&gt;Medium (logic spreads)&lt;/td&gt;
&lt;td&gt;High (everything declarative and in one place)&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development speed&lt;/td&gt;
&lt;td&gt;Normal at start&lt;/td&gt;
&lt;td&gt;Much faster after learning curve 🚀&lt;/td&gt;
&lt;td&gt;Ash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flexibility&lt;/td&gt;
&lt;td&gt;Maximum (full control)&lt;/td&gt;
&lt;td&gt;Very high + can escape to pure Ecto when needed&lt;/td&gt;
&lt;td&gt;Draw&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Learning curve&lt;/td&gt;
&lt;td&gt;Easier for beginners&lt;/td&gt;
&lt;td&gt;Steeper but pays off in large projects&lt;/td&gt;
&lt;td&gt;Ecto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  What to Choose in 2026?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros of Ash&lt;/strong&gt; 🔥:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Declarativeness → "Describe the domain — Ash does the rest."&lt;/li&gt;
&lt;li&gt;Boilerplate reduction by orders of magnitude ("I write code 3–5 times faster").&lt;/li&gt;
&lt;li&gt;Automatic generation of GraphQL, JSON:API, Admin panel, authentication.&lt;/li&gt;
&lt;li&gt;Authorization policies and multi-tenancy out of the box.&lt;/li&gt;
&lt;li&gt;Excellent maintainability in the long run (exactly what production dreams of).
&lt;strong&gt;Pros of classic Phoenix + Ecto&lt;/strong&gt; 😐:&lt;/li&gt;
&lt;li&gt;Full control over every byte of code.&lt;/li&gt;
&lt;li&gt;Simpler for very small projects and pet projects.&lt;/li&gt;
&lt;li&gt;No extra abstraction (if you love "bare" Elixir).&lt;/li&gt;
&lt;li&gt;Easier for Elixir beginners.
&lt;strong&gt;When to choose what?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Small pet project or experiment → pure Phoenix + Ecto.&lt;/li&gt;
&lt;li&gt;Medium/large project, SaaS, long-lived app → Phoenix + Ash (huge time and nerve savings).&lt;/li&gt;
&lt;li&gt;Want both → you can safely mix in one project (Ash and regular Ecto coexist perfectly).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😴 Conclusion
&lt;/h2&gt;

&lt;p&gt;Ash is not just a library but a shift in approach to Elixir development. It especially shines in medium and large projects where clean architecture, development speed, and long-term maintainability are important.&lt;br&gt;
Of course, it has a learning curve and may be excessive for very simple pet projects. But if you're tired of spreading logic and want a clear declarative structure—Ash gives a very pleasant sense of control over your code.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🙏 From the Author
&lt;/h2&gt;

&lt;p&gt;Thank you very much for your interest in this article! I hope it helped you understand what Ash is and why it's used.&lt;br&gt;
If you liked this article and want more materials like this—join me on my &lt;a href="https://t.me/devs_inc_en" rel="noopener noreferrer"&gt;Telegram channel&lt;/a&gt;, where I post book reviews, Elixir publications, technical literature, and interesting news!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>ash</category>
      <category>phoenix</category>
      <category>ecto</category>
    </item>
    <item>
      <title>Ash Framework: Знакомство</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Sat, 04 Apr 2026 08:05:54 +0000</pubDate>
      <link>https://dev.to/adamanq/ash-framework-znakomstvo-g0g</link>
      <guid>https://dev.to/adamanq/ash-framework-znakomstvo-g0g</guid>
      <description>&lt;p&gt;Когда новичок приходит в Elixir и Phoenix, ему почти всегда первым делом показывают &lt;strong&gt;Ecto&lt;/strong&gt;. Создаём схемы, пишем changeset’ы, выносим запросы в контексты. Подход гибкий, но с ростом проекта начинает проявляться серьёзная проблема 😟&lt;/p&gt;

&lt;p&gt;Бизнес-логика расползается по десяткам файлов. В итоге возникает путаница относительно того, куда именно поместить код обработки запросов и как разобраться в существующем беспорядке, а через год-два сложно понять, где находится «правильное» место для кода, а новые разработчики тратят недели на погружение в проект&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ash Framework&lt;/strong&gt; создан именно для решения этой боли 💪&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Важное уточнение&lt;/strong&gt;: Ash — это &lt;strong&gt;не замена Phoenix&lt;/strong&gt; и не новый веб-фреймворк. Это мощный декларативный слой, который организует бизнес-логику и данные поверх существующей экосистемы.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  📖 Предисловие
&lt;/h2&gt;

&lt;p&gt;Важно понимать, что цель этой статьи вовсе не состоит в том, чтобы уговорить вас использовать Ash повсеместно и постоянно!&lt;/p&gt;

&lt;p&gt;Сам я люблю Ash именно из-за того, что он даёт возможность собрать всю необходимую логику в едином пространстве, где ей самое место. Этот инструмент значительно упрощает работу, уменьшая количество шаблонного кода и освобождая от утомительной реализации стандартных CRUD.&lt;/p&gt;

&lt;p&gt;Если готовы ознакомиться с таким инструментом, но собираетесь прибегать к нему лишь тогда, когда это действительно оправдано — смело читайте дальше. Ash — это не универсальное лекарство от всех проблем, а всего лишь удобный набор полезных инструментов.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Что такое Ash?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.ash-hq.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/a&gt; — это фреймворк для быстрой и эффективной разработки приложений на Elixir. Его цель заключается в облегчении запуска новых проектов и обеспечении удобства поддержки существующих решений, гарантируя отличную производительность и стабильность даже после длительного периода активной разработки.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ключевая идея Ash звучит так:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Точно сформулируй структуру своей бизнес-логики, а всю остальную работу (миграция базы данных, создание API-интерфейсов, валидация данных, управление доступом, оформление форм) возьмет на себя Ash&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Фреймворк прекрасно взаимодействует с такими популярными инструментами, как Phoenix, PostgreSQL, GraphQL и LiveView.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🔑 Ключевые абстракции Ash
&lt;/h2&gt;

&lt;p&gt;Всё в Ash строится вокруг трёх основных понятий:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/ash/dsl-ash-resource.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Resource (Ресурс)&lt;/strong&gt;&lt;/a&gt; — описание одной бизнес-сущности&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/ash/actions.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Action (Действие)&lt;/strong&gt;&lt;/a&gt; — операция, которую можно выполнить над ресурсом&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/ash/domains.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Domain (Домен)&lt;/strong&gt;&lt;/a&gt; — логическая группа ресурсов одной бизнес-области&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Давайте разберём каждую концепцию подробно!&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🧩 Что такое Ресурс?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Resource&lt;/strong&gt; — это &lt;strong&gt;один модуль&lt;/strong&gt;, в котором полностью описана &lt;strong&gt;одна бизнес-сущность&lt;/strong&gt; вашего приложения (Пользователь, Урок, Заказ, Пост, Платёж и т.д.).&lt;/p&gt;

&lt;p&gt;В нём в одном месте собрано &lt;strong&gt;всё&lt;/strong&gt;, что касается этой сущности:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Структура данных (поля)&lt;/li&gt;
&lt;li&gt;Связи с другими сущностями&lt;/li&gt;
&lt;li&gt;Операции, которые можно выполнять (CRUD + кастомные)&lt;/li&gt;
&lt;li&gt;Правила валидации&lt;/li&gt;
&lt;li&gt;Авторизация (кто и что может делать)&lt;/li&gt;
&lt;li&gt;Вычисляемые поля&lt;/li&gt;
&lt;li&gt;Индексы и ограничения&lt;/li&gt;
&lt;li&gt;Настройки хранения в базе&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resource в Ash объединяет в одном модуле то, что в классическом Elixir/Phoenix обычно разносится по нескольким частям: Ecto Schema, Changesets, Context-функции, а также логика авторизации и вычисляемые поля.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Resource — сердце Ash
&lt;/h3&gt;

&lt;p&gt;Ash построен вокруг идеи &lt;strong&gt;Resource-Oriented Design&lt;/strong&gt;. Ты описываешь &lt;strong&gt;что&lt;/strong&gt; представляет собой сущность и &lt;strong&gt;что&lt;/strong&gt; с ней можно делать — а Ash сам генерирует остальное: SQL-запросы, API, формы, миграции, валидацию и т.д.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Полный пример современного Resource
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:course_hub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;              &lt;span class="c1"&gt;# Привязка к приложению&lt;/span&gt;
    &lt;span class="ss"&gt;domain:&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;# В каком домене живёт&lt;/span&gt;
    &lt;span class="ss"&gt;data_layer:&lt;/span&gt; &lt;span class="no"&gt;AshPostgres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DataLayer&lt;/span&gt;  &lt;span class="c1"&gt;# Где храним данные&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== ХРАНЕНИЕ ====================&lt;/span&gt;
  &lt;span class="n"&gt;postgres&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="s2"&gt;"lessons"&lt;/span&gt;
    &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== ПОЛЯ (Attributes) ====================&lt;/span&gt;
  &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;uuid_primary_key&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;

    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;               &lt;span class="c1"&gt;# видно в API и формах&lt;/span&gt;
      &lt;span class="n"&gt;allow_nil?&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="ss"&gt;min_length:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;max_length:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;public?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;public?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

    &lt;span class="n"&gt;attribute&lt;/span&gt; &lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:atom&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;default?&lt;/span&gt; &lt;span class="ss"&gt;:draft&lt;/span&gt;
      &lt;span class="n"&gt;constraints&lt;/span&gt; &lt;span class="ss"&gt;one_of:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;create_timestamp&lt;/span&gt; &lt;span class="ss"&gt;:inserted_at&lt;/span&gt;
    &lt;span class="n"&gt;update_timestamp&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== СВЯЗИ (Relationships) ====================&lt;/span&gt;
  &lt;span class="n"&gt;relationships&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;belongs_to&lt;/span&gt; &lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Accounts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;allow_nil?&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Comment&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;many_to_many&lt;/span&gt; &lt;span class="ss"&gt;:tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Tag&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;public?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;through&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;LessonTag&lt;/span&gt;
      &lt;span class="n"&gt;source_attribute_on_join_resource&lt;/span&gt; &lt;span class="ss"&gt;:lesson_id&lt;/span&gt;
      &lt;span class="n"&gt;destination_attribute_on_join_resource&lt;/span&gt; &lt;span class="ss"&gt;:tag_id&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== ДЕЙСТВИЯ (Actions) ====================&lt;/span&gt;
  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Кастомное чтение&lt;/span&gt;
    &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;pagination&lt;/span&gt; &lt;span class="ss"&gt;offset?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;countable:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="n"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sort:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;inserted_at:&lt;/span&gt; &lt;span class="ss"&gt;:desc&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Кастомное создание с логикой&lt;/span&gt;
    &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# Полностью кастомное действие&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:calculate_reading_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== ВЫЧИСЛЯЕМЫЕ ПОЛЯ ====================&lt;/span&gt;
  &lt;span class="n"&gt;calculations&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;calculate&lt;/span&gt; &lt;span class="ss"&gt;:reading_time_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;calculation&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Float&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== ИНДЕКСЫ ====================&lt;/span&gt;
  &lt;span class="n"&gt;identities&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;identity&lt;/span&gt; &lt;span class="ss"&gt;:unique_title_per_author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:author_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# ==================== АВТОРИЗАЦИЯ ====================&lt;/span&gt;
  &lt;span class="n"&gt;policies&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;action_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;authorize_if&lt;/span&gt; &lt;span class="n"&gt;always&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;authorize_if&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Основные разделы Resource
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Раздел&lt;/th&gt;
&lt;th&gt;Что описывает&lt;/th&gt;
&lt;th&gt;Аналог в обычном Elixir/Phoenix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;attributes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Поля и их типы + валидация&lt;/td&gt;
&lt;td&gt;Ecto Schema + поля&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;relationships&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Связи с другими ресурсами&lt;/td&gt;
&lt;td&gt;Ecto associations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;actions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Все возможные операции (CRUD + кастомные)&lt;/td&gt;
&lt;td&gt;Context-функции + Changesets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;calculations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Вычисляемые поля (на лету)&lt;/td&gt;
&lt;td&gt;Virtual fields / функции&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aggregates&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Агрегаты (count, sum, avg и т.д.)&lt;/td&gt;
&lt;td&gt;Запросы с group_by&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;identities&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Уникальные индексы&lt;/td&gt;
&lt;td&gt;Ecto unique constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;policies&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Правила авторизации&lt;/td&gt;
&lt;td&gt;Ручная проверка прав&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;postgres&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Настройки хранения в PostgreSQL&lt;/td&gt;
&lt;td&gt;Миграции&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Ключевые особенности Resource
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Всё в одном файле&lt;/strong&gt; — огромный плюс для понимания сущности.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introspection&lt;/strong&gt; — Ash может в runtime читать всю информацию о ресурсе (поля, действия, политики). Это используется для генерации API, форм, админок.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Декларативность&lt;/strong&gt; — ты описываешь &lt;strong&gt;что&lt;/strong&gt; должно быть, а не &lt;strong&gt;как&lt;/strong&gt; это реализовать.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Расширяемость&lt;/strong&gt; — можно добавлять свои &lt;code&gt;changes&lt;/code&gt;, &lt;code&gt;preparations&lt;/code&gt;, &lt;code&gt;validations&lt;/code&gt;, &lt;code&gt;notifiers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded Resources&lt;/strong&gt; — можно создавать ресурсы, которые хранятся не в отдельной таблице, а как JSON/Map внутри другого ресурса.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Когда создавать новый Resource?
&lt;/h3&gt;

&lt;p&gt;Практически всегда, когда появляется новая бизнес-сущность (таблица или даже сложный JSON-объект).&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🛠️ Подробнее об Actions (Действиях)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Action (Действие)&lt;/strong&gt; — это &lt;strong&gt;явно объявленная операция&lt;/strong&gt;, которую можно выполнить над ресурсом.  &lt;/p&gt;

&lt;p&gt;Всё взаимодействие с данными в Ash идёт &lt;strong&gt;только через Actions&lt;/strong&gt;. Нельзя просто взять запись и изменить её вручную — всегда вызываешь действие.&lt;/p&gt;

&lt;p&gt;Это заменяет:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Обычные функции в Context’ах Phoenix&lt;/li&gt;
&lt;li&gt;Changesets в Ecto&lt;/li&gt;
&lt;li&gt;Контроллеры и сервисные слои&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Пять типов Actions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Тип действия&lt;/th&gt;
&lt;th&gt;Что делает?&lt;/th&gt;
&lt;th&gt;В транзакции?&lt;/th&gt;
&lt;th&gt;Что возвращает?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✨&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;create&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Создаёт новую запись&lt;/td&gt;
&lt;td&gt;Да&lt;/td&gt;
&lt;td&gt;Новую запись ресурса&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔍&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;read&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Читает одну запись или целый список&lt;/td&gt;
&lt;td&gt;Нет (можно включить)&lt;/td&gt;
&lt;td&gt;Запись или список записей&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✏️&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;update&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Обновляет существующую запись&lt;/td&gt;
&lt;td&gt;Да&lt;/td&gt;
&lt;td&gt;Обновлённую запись&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗑️&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;destroy&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Удаляет запись&lt;/td&gt;
&lt;td&gt;Да&lt;/td&gt;
&lt;td&gt;Удалённую запись&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;&lt;code&gt;action&lt;/code&gt;&lt;/strong&gt; (Generic)&lt;/td&gt;
&lt;td&gt;Любая кастомная бизнес-логика&lt;/td&gt;
&lt;td&gt;Нет (можно включить)&lt;/td&gt;
&lt;td&gt;Любое значение (что угодно)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Полный пример всех типов Actions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;

  &lt;span class="n"&gt;actions&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Стандартные CRUD (defaults)&lt;/span&gt;
    &lt;span class="n"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:destroy&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Кастомное Read&lt;/span&gt;
    &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;pagination&lt;/span&gt; &lt;span class="ss"&gt;offset?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;countable:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default_limit:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
      &lt;span class="n"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sort:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;inserted_at:&lt;/span&gt; &lt;span class="ss"&gt;:desc&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;load:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comment_count&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:for_user&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author_id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Кастомное Create&lt;/span&gt;
    &lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

      &lt;span class="c1"&gt;# Изменения (changes)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;NotifySubscribers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;topic:&lt;/span&gt; &lt;span class="s2"&gt;"new_lessons"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="c1"&gt;# Хуки&lt;/span&gt;
      &lt;span class="n"&gt;after_action&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="c1"&gt;# side-effect после успешного создания&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 4. Кастомное Update&lt;/span&gt;
    &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="ss"&gt;:archive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:archive_reason&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;                    &lt;span class="c1"&gt;# какие поля можно менять&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:archived&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:archived_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="c1"&gt;# 5. Generic Action (полностью кастомное)&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:calculate_reading_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
      &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:words_per_minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default?:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

      &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;words_per_minute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                  &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Как вызывать Actions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Через домен (рекомендуемый способ)&lt;/span&gt;
&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;reason:&lt;/span&gt; &lt;span class="s2"&gt;"Готово"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Или с Ash.Query / Ash.Changeset для сложных случаев&lt;/span&gt;
&lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;for_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Основные возможности Actions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. &lt;code&gt;argument&lt;/code&gt; — входные параметры
&lt;/h4&gt;

&lt;p&gt;Позволяет передавать данные в действие и валидировать их.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;constraints:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;min_length:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default?:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allow_nil?:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. &lt;code&gt;accept&lt;/code&gt; — какие атрибуты можно менять
&lt;/h4&gt;

&lt;p&gt;Ограничивает список полей, которые разрешено изменять при &lt;code&gt;create&lt;/code&gt; или &lt;code&gt;update&lt;/code&gt;. Очень полезно для безопасности.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:register&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# только эти поля можно передать&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="ss"&gt;:update_profile&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:bio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:avatar_url&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. &lt;code&gt;change&lt;/code&gt; — трансформации данных
&lt;/h4&gt;

&lt;p&gt;Самая мощная и часто используемая часть Actions. Здесь происходит основная бизнес-логика.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:string&lt;/span&gt;

  &lt;span class="c1"&gt;# Встроенные изменения&lt;/span&gt;
  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:published_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc_now&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;# Кастомный change-модуль&lt;/span&gt;
  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;NotifySubscribers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;topic:&lt;/span&gt; &lt;span class="s2"&gt;"lessons"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Анонимная функция&lt;/span&gt;
  &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Changeset&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;change_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;generate_slug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. &lt;code&gt;prepare&lt;/code&gt; — подготовка запроса (в основном для &lt;code&gt;read&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;Позволяет заранее настроить запрос: сортировку, предзагрузку связей, дополнительные фильтры и т.д.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;prepare&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="ss"&gt;sort:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;inserted_at:&lt;/span&gt; &lt;span class="ss"&gt;:desc&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;load:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:comments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:reading_time_minutes&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="ss"&gt;limit:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. Хуки (before/after)
&lt;/h4&gt;

&lt;p&gt;Позволяют выполнить код до или после выполнения действия.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:publish&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before_action&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;# проверка перед выполнением&lt;/span&gt;
    &lt;span class="n"&gt;validate_business_rules&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changeset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;after_action&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;_changeset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;# side-effect после успешного создания&lt;/span&gt;
    &lt;span class="no"&gt;LessonNotifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;after_transaction&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;broadcast_new_lesson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:ok&lt;/span&gt; 
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6. &lt;code&gt;transaction?&lt;/code&gt; — управление транзакцией
&lt;/h4&gt;

&lt;p&gt;Определяет, нужно ли оборачивать действие в базу данных транзакцию.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="ss"&gt;:transfer_money&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:boolean&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:from_account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:to_account_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;
  &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="ss"&gt;:amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:decimal&lt;/span&gt;

  &lt;span class="n"&gt;transaction?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;   &lt;span class="c1"&gt;# будет выполнено в одной транзакции&lt;/span&gt;

  &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;# сложная операция с несколькими записями&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  7. &lt;code&gt;pipe_through&lt;/code&gt; — переиспользование логики
&lt;/h4&gt;

&lt;p&gt;Позволяет создавать общие пайплайны и подключать их к разным действиям (очень полезно для DRY).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# В Resource&lt;/span&gt;
&lt;span class="n"&gt;pipelines&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:ensure_admin&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="n"&gt;ensure_actor_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;actor_role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:admin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;create&lt;/span&gt; &lt;span class="ss"&gt;:create_admin_content&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:ensure_admin&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="ss"&gt;:update_admin_content&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:ensure_admin&lt;/span&gt;
  &lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:content&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Почему Actions — это круто?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Полная типизация&lt;/strong&gt; и &lt;strong&gt;introspection&lt;/strong&gt; — Ash знает всё о действии в runtime (используется для GraphQL, форм, админок)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Декларативность&lt;/strong&gt; — код читается как документация бизнеса&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Повторное использование&lt;/strong&gt; — одно действие можно использовать в LiveView, API, Background Job, Tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Безопасность&lt;/strong&gt; — политики авторизации привязываются к конкретным действиям&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Тестируемость&lt;/strong&gt; — легко тестировать отдельные действия&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Сравнение с классическим Elixir/Phoenix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Подход&lt;/th&gt;
&lt;th&gt;Где логика?&lt;/th&gt;
&lt;th&gt;Кол-во файлов&lt;/th&gt;
&lt;th&gt;Понятность&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ecto + Context&lt;/td&gt;
&lt;td&gt;Context + Changeset&lt;/td&gt;
&lt;td&gt;4–8&lt;/td&gt;
&lt;td&gt;Средняя&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ash Actions&lt;/td&gt;
&lt;td&gt;В одном Resource&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Высокая&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Actions — это то место, где живёт вся бизнес-логика&lt;/strong&gt; твоего ресурса&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🏢 Что такое Domain (Домены)?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Domain (Домен)&lt;/strong&gt; — это &lt;strong&gt;логический контейнер&lt;/strong&gt;, который объединяет группу связанных &lt;strong&gt;Resources&lt;/strong&gt; (ресурсов).  &lt;/p&gt;

&lt;p&gt;Он похож на &lt;strong&gt;Phoenix Context&lt;/strong&gt;, но гораздо мощнее. Если Resource — это описание одной сущности (как &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Lesson&lt;/code&gt;, &lt;code&gt;Order&lt;/code&gt;), то Domain — это «модуль» или «bounded context» (в терминах DDD), который собирает вместе несколько сущностей одной бизнес-области.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Пример&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Accounts&lt;/code&gt; — домен для пользователей, профилей, авторизации&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Courses&lt;/code&gt; — домен для курсов, уроков, тестов, enrollments&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Billing&lt;/code&gt; — домен для платежей, подписок, инвойсов&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Therapy&lt;/code&gt; — домен для терапии, разговоров, сообщений (из реальных примеров)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Три главные цели Domain
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Группировка ресурсов&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Организация проекта. Вместо того чтобы все ресурсы были разбросаны, они явно лежат в логических группах.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Централизованный Code Interface&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Domain позволяет определять удобные функции-обёртки для вызова действий ресурсов.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Общие настройки и cross-cutting concerns&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Здесь можно задать поведение, которое применяется ко всем ресурсам домена (таймауты, пагинация по умолчанию, авторизация и т.д.).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Как выглядит Domain на практике
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Ash&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:course_hub&lt;/span&gt;

  &lt;span class="c1"&gt;# 1. Группировка ресурсов&lt;/span&gt;
  &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Course&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Lesson&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Quiz&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Enrollment&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# 2. Code Interface — удобные функции&lt;/span&gt;
  &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="ss"&gt;:get_lesson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action:&lt;/span&gt; &lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;args:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;get?:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="ss"&gt;:list_published_courses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action:&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;
  &lt;span class="n"&gt;define&lt;/span&gt; &lt;span class="ss"&gt;:enroll_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;action:&lt;/span&gt; &lt;span class="ss"&gt;:create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;resource:&lt;/span&gt; &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Enrollment&lt;/span&gt;

  &lt;span class="c1"&gt;# 3. Общие настройки&lt;/span&gt;
  &lt;span class="n"&gt;execution&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="ss"&gt;:timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# глобальный таймаут&lt;/span&gt;
    &lt;span class="n"&gt;default_page_size&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Включение AshAdmin (админка)&lt;/span&gt;
  &lt;span class="n"&gt;admin&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;show?&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Зачем нужен Domain?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Организация и границы.&lt;/strong&gt; Чётко разделяет разные части приложения (как bounded contexts в DDD). &lt;code&gt;Accounts&lt;/code&gt; не знает про &lt;code&gt;Billing&lt;/code&gt; без явного отношения&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Удобный публичный API.&lt;/strong&gt; Вместо &lt;code&gt;Ash.read!(CourseHub.Courses.Lesson, ...)&lt;/code&gt; ты пишешь красиво:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_lesson!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lesson_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;CourseHub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_published_courses!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Централизованное управление.&lt;/strong&gt; Можно отключить авторизацию для всего домена в dev-режиме, задать общие политики, notifiers и т.д.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Introspection и расширения.&lt;/strong&gt; Многие расширения Ash (GraphQL, JsonApi, Phoenix и т.д.) работают на уровне домена&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Тестирование и изоляция.&lt;/strong&gt; Легче тестировать одну бизнес-область целиком&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Лучшие практики при работе с доменом
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Один ресурс — один домен.&lt;/strong&gt; Ресурс не может принадлежать нескольким доменам одновременно.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Сколько доменов делать?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Маленький проект → 1–3 домена.
&lt;/li&gt;
&lt;li&gt;Средний/большой → по одному на крупную бизнес-область (Accounts, Core, Billing, Notifications и т.д.).
Это помогает в масштабировании и понимании архитектуры.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code Interface&lt;/strong&gt; — очень мощная фича. Ты можешь определять не только простые обёртки, но и с предустановленными фильтрами, load'ами и т.д.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Сравнение: Domain vs Resource
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Аспект&lt;/th&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Что описывает&lt;/td&gt;
&lt;td&gt;Одну сущность&lt;/td&gt;
&lt;td&gt;Группу сущностей&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Где лежит логика&lt;/td&gt;
&lt;td&gt;Атрибуты, действия, политики&lt;/td&gt;
&lt;td&gt;Группировка + общие настройки&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Вызов кода&lt;/td&gt;
&lt;td&gt;Через домен&lt;/td&gt;
&lt;td&gt;Публичный интерфейс приложения&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Аналог в Phoenix&lt;/td&gt;
&lt;td&gt;Schema + Context (частично)&lt;/td&gt;
&lt;td&gt;Phoenix Context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Количество на проект&lt;/td&gt;
&lt;td&gt;Много (по одной на таблицу)&lt;/td&gt;
&lt;td&gt;Мало (по бизнес-областям)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Почему Domain особенно полезен?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Проект растёт &amp;gt; 10–15 ресурсов&lt;/li&gt;
&lt;li&gt;Нужна сложная авторизация (policies)&lt;/li&gt;
&lt;li&gt;Делаешь GraphQL / JSON:API / Admin-панель&lt;/li&gt;
&lt;li&gt;Командная разработка — всем нужно понимать границы&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  ⚔️ Ash + Phoenix vs Ecto + Phoenix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Аспект&lt;/th&gt;
&lt;th&gt;Phoenix + Ecto (классика) 😐&lt;/th&gt;
&lt;th&gt;Phoenix + Ash 🔥&lt;/th&gt;
&lt;th&gt;Кто выигрывает?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Организация бизнес-логики&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Разбросана по Context, Changeset, отдельным модулям&lt;/td&gt;
&lt;td&gt;Всё в &lt;strong&gt;одном Resource&lt;/strong&gt; — атрибуты, действия, политики, связи&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ash&lt;/strong&gt; (всё на виду)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CRUD-операции&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Пишешь вручную: create, read, update, delete + boilerplate&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;defaults [:create, :read, :update, :destroy]&lt;/code&gt; — и готово ✨&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ash&lt;/strong&gt; (1 строка)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Авторизация&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ручные проверки / Bodyguard / scopes&lt;/td&gt;
&lt;td&gt;Декларативные &lt;strong&gt;Policies&lt;/strong&gt; (secure by default) ⚡&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ash&lt;/strong&gt; (автоматика)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Вычисляемые поля&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Virtual fields + функции в Context&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Calculations&lt;/strong&gt; — автоматически подгружаются и кэшируются&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Количество файлов на сущность&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3–6+ файлов (Schema + Context + Changeset + Policy)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;1 файл&lt;/strong&gt; — Resource&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Генерация API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Absinthe / GraphQL вручную + много кода&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AshGraphql&lt;/code&gt; или &lt;code&gt;AshJsonApi&lt;/code&gt; — добавил расширение и готово&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LiveView формы&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Phoenix.Component + ручная валидация&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;AshPhoenix.Form&lt;/strong&gt; — формы сами знают всё&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-tenancy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Пишешь сам (или используешь схемы)&lt;/td&gt;
&lt;td&gt;Встроено в Policies (attribute-based или context-based)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Поддерживаемость через 2–3 года&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Средняя (логика расползается)&lt;/td&gt;
&lt;td&gt;Высокая (всё декларативно и в одном месте)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ash&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Скорость разработки&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Нормальная на старте&lt;/td&gt;
&lt;td&gt;Значительно быстрее после кривой обучения 🚀&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ash&lt;/strong&gt; (после обучения)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Гибкость&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Максимальная (полный контроль)&lt;/td&gt;
&lt;td&gt;Очень высокая + можно escape в чистый Ecto когда нужно&lt;/td&gt;
&lt;td&gt;Ничья&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Кривая обучения&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Легче для новичков&lt;/td&gt;
&lt;td&gt;Круче, но окупается в большом проекте&lt;/td&gt;
&lt;td&gt;Ecto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Что стоит выбрать в 2026 году?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Плюсы Ash&lt;/strong&gt; 🔥&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Декларативность&lt;/strong&gt; → «Опиши домен — остальное Ash выведет сам»&lt;/li&gt;
&lt;li&gt;Сокращение шаблонного кода (часто встречаются отзывы о Ash вроде: «я создаю программы, используя в 3–5 раз меньше строк»)&lt;/li&gt;
&lt;li&gt;Автоматическая генерация GraphQL, JSON:API, Admin-панели, аутентификации&lt;/li&gt;
&lt;li&gt;Политики авторизации и multi-tenancy из коробки&lt;/li&gt;
&lt;li&gt;Отличная поддерживаемость на длинной дистанции (именно то, о чём мечтают в продакшене)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Плюсы классического Phoenix + Ecto&lt;/strong&gt; 😐&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Полный контроль над каждым байтом кода&lt;/li&gt;
&lt;li&gt;Проще для совсем маленьких проектов и пет-проектов&lt;/li&gt;
&lt;li&gt;Нет дополнительной абстракции (если ты любишь «голый» Elixir)&lt;/li&gt;
&lt;li&gt;Легче для новичков в Elixir&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Когда выбирать что?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Маленький пет-проект или эксперимент&lt;/strong&gt; → бери чистый Phoenix + Ecto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Средний/крупный проект, SaaS, долгоживущее приложение&lt;/strong&gt; → Phoenix + Ash (экономия времени и нервов огромная)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Хочешь и то, и другое&lt;/strong&gt; → можно спокойно миксовать в одном проекте (Ash и обычный Ecto отлично уживаются)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  😴 В заключение
&lt;/h2&gt;

&lt;p&gt;Ash — это не просто библиотека, а изменение подхода к разработке на Elixir. Он особенно хорошо заходит в проектах среднего и крупного размера, где важны чистота архитектуры, скорость разработки и долгосрочная поддерживаемость.&lt;/p&gt;

&lt;p&gt;Конечно, у него есть кривая обучения, и в совсем простых пет-проектах он может быть избыточен. Но если вы устали от расползающейся логики и хотите чёткой, декларативной структуры — Ash дарит очень приятное ощущение контроля над кодом.&lt;/p&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;




&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  🥴 От автора
&lt;/h2&gt;

&lt;p&gt;Спасибо большое за интерес к этой статье! Надеюсь, она помогла разобраться, что представляет собой Ash и зачем он используется.&lt;/p&gt;

&lt;p&gt;Если эта статья пришлась вам по душе и хочется ещё материалов такого плана, присоединяйтесь ко мне в моём &lt;a href="https://t.me/devs_inc" rel="noopener noreferrer"&gt;телеграм-канале&lt;/a&gt;, где выкладываю обзоры книг, публикации по Elixir, переводы технической литературы и интересные новости!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>ash</category>
      <category>phoenix</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Thu, 19 Mar 2026 17:05:36 +0000</pubDate>
      <link>https://dev.to/adamanq/-igb</link>
      <guid>https://dev.to/adamanq/-igb</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/ua3mqj/ad9833-dds-generator-board-10p7" class="crayons-story__hidden-navigation-link"&gt;AD9833 DDS Generator Board&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/ua3mqj" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" alt="ua3mqj profile" class="crayons-avatar__image" width="96" height="96"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/ua3mqj" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alexey Bolshakov
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alexey Bolshakov
                
              
              &lt;div id="story-author-preview-content-3372554" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/ua3mqj" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3831248%2F7b0c6671-cae6-45c2-97a8-0fa75341b7a2.jpg" class="crayons-avatar__image" alt="" width="96" height="96"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alexey Bolshakov&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/ua3mqj/ad9833-dds-generator-board-10p7" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 19&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/ua3mqj/ad9833-dds-generator-board-10p7" id="article-link-3372554"&gt;
          AD9833 DDS Generator Board
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/dds"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;dds&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/microchips"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;microchips&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/stm8"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;stm8&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/microcontroller"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;microcontroller&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/ua3mqj/ad9833-dds-generator-board-10p7" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;3&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/ua3mqj/ad9833-dds-generator-board-10p7#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>dds</category>
      <category>microchips</category>
      <category>stm8</category>
      <category>microcontroller</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Sun, 15 Mar 2026 13:11:26 +0000</pubDate>
      <link>https://dev.to/adamanq/-ogk</link>
      <guid>https://dev.to/adamanq/-ogk</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/sag1v/the-internet-is-getting-quieter-who-will-feed-the-next-generation-of-ai-4bl1" class="crayons-story__hidden-navigation-link"&gt;The Internet Is Getting Quieter - Who Will Feed the Next Generation of AI?&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/sag1v" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F139634%2F490a5b18-ebbd-414e-b10e-188f793b539b.webp" alt="sag1v profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/sag1v" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Sagiv ben giat
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Sagiv ben giat
                
              
              &lt;div id="story-author-preview-content-3345524" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/sag1v" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F139634%2F490a5b18-ebbd-414e-b10e-188f793b539b.webp" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Sagiv ben giat&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/sag1v/the-internet-is-getting-quieter-who-will-feed-the-next-generation-of-ai-4bl1" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 12&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/sag1v/the-internet-is-getting-quieter-who-will-feed-the-next-generation-of-ai-4bl1" id="article-link-3345524"&gt;
          The Internet Is Getting Quieter - Who Will Feed the Next Generation of AI?
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/knowledgesharing"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;knowledgesharing&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/stackoverflow"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;stackoverflow&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opinion"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opinion&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/sag1v/the-internet-is-getting-quieter-who-will-feed-the-next-generation-of-ai-4bl1" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;57&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/sag1v/the-internet-is-getting-quieter-who-will-feed-the-next-generation-of-ai-4bl1#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              30&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>knowledgesharing</category>
      <category>stackoverflow</category>
      <category>opinion</category>
    </item>
    <item>
      <title>BEAM: In Plain English</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Thu, 25 Sep 2025 07:00:03 +0000</pubDate>
      <link>https://dev.to/adamanq/beam-in-plain-english-2n1o</link>
      <guid>https://dev.to/adamanq/beam-in-plain-english-2n1o</guid>
      <description>&lt;p&gt;Imagine a universe where billions of messages, calls, and financial transactions happen every second. Each message reaches its recipient flawlessly, phone calls remain uninterrupted, and funds are transferred instantly. What ensures such impeccable performance? No, it's not magic—it's virtual machines—the unsung heroes of digital infrastructure.&lt;/p&gt;

&lt;p&gt;Among them stands out one remarkable machine — &lt;strong&gt;BEAM&lt;/strong&gt;. Created decades ago, it looks like an invention from a distant future. It powers stable operations for giants like WhatsApp, Discord, financial services, and IoT systems. Even after all these years since its inception, BEAM continues to impress with its performance and reliability.&lt;/p&gt;

&lt;p&gt;This article is not intended as an in-depth exploration into the inner workings of BEAM or its implementation details. We won't delve into byte code flow or provide specific internal code snippets here. Instead, this piece aims to give you a general understanding of what BEAM does, why it's important, and how it can benefit your business under heavy loads. For those who want to dive deeper into BEAM’s internals, I recommend reading the book — &lt;em&gt;&lt;a href="https://blog.stenmans.org/theBeamBook/#CH-BEAM" rel="noopener noreferrer"&gt;The BEAM Book: Understanding the Erlang Runtime System&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In today's era characterized by rapid development of real-time technologies, artificial intelligence, and mass parallel user connections, BEAM excels at providing high response speed, reliability even during failures, and quick adaptation to increasing load, making it an optimal solution for complex challenges. But let's start with the basics!&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Virtual Machine? 🤔
&lt;/h2&gt;

&lt;p&gt;Imagine writing a program that needs to run everywhere—on laptops, smartphones, servers, smart speakers, etc. How do you ensure compatibility across different operating systems and hardware configurations? The answer lies in using a virtual machine (VM). It's a special execution environment that interprets your code, acting as a bridge between your application and the underlying device. The VM takes intermediate code (like Java bytecode) and translates it into instructions suitable for each platform, freeing you from worrying about memory management or multithreading intricacies. In essence, a virtual machine serves as a universal adapter enabling your code to run anywhere!&lt;/p&gt;

&lt;p&gt;Additionally, many virtual machines come equipped with useful tools such as automatic garbage collection, built-in performance optimizations, and data protection mechanisms. These features make app development significantly easier and more efficient. Without VMs, creating modern projects ranging from mobile games to large-scale cloud services would be much harder.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Are Virtual Machines Needed? 🌟
&lt;/h2&gt;

&lt;p&gt;Virtual machines aren't just developer conveniences but powerful tools for building versatile, reliable, and scalable solutions. Let's explore their key advantages through practical examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cross-platform Compatibility&lt;/strong&gt; 📱💻&lt;br&gt;&lt;br&gt;
Write once, run anywhere. An example is a banking app written in Java running seamlessly both on bank servers and client Android phones thanks to JVM. This saves time and resources, especially valuable in enterprise-level development involving diverse platforms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Management&lt;/strong&gt; 🧹&lt;br&gt;&lt;br&gt;
Virtual machines handle resource allocation, including memory control (via garbage collection), threading, and hardware interaction. For instance, in the Erlang VM (BEAM), each process gets isolated memory areas, preventing issues caused by individual process crashes. Such architecture minimizes bugs and simplifies coding tasks, relieving developers from manually managing memory allocation and deallocation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt; 🔒&lt;br&gt;&lt;br&gt;
By isolating executable code from the OS, virtual machines create secure sandboxes protecting against potential threats posed by harmful programs. This feature is crucial in environments like AWS Lambda or Google Cloud Functions, where thousands of code fragments from various users execute simultaneously on shared hardware.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Optimization&lt;/strong&gt; ⚡&lt;br&gt;&lt;br&gt;
Many VMs dynamically optimize code execution while applications are running. One prominent example is Just-In-Time compilation (JIT), which makes browser engines like V8 incredibly fast. Starting with version OTP 24, BEAM introduced its own JIT mechanism, further enhancing performance. These techniques prove invaluable when dealing with repetitive computations common in highly loaded web apps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Support for Complex Systems&lt;/strong&gt; 🌐&lt;br&gt;&lt;br&gt;
Some VMs are specifically designed to support concurrency, distributed computing, and fault tolerance. For example, popular game server infrastructures, like those powering &lt;a href="https://highscalability.com/how-league-of-legends-scaled-chat-to-70-million-players-it-t/" rel="noopener noreferrer"&gt;&lt;em&gt;League of Legends&lt;/em&gt;&lt;/a&gt;, rely heavily on BEAM, efficiently handling millions of concurrent player connections with minimal latency and high reliability.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thus, virtual machines allow developers to focus directly on product functionality rather than low-level technicalities, accelerating development processes and reducing costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Popular Virtual Machines: Who's Who? 🦸‍♂️
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JVM (Java Virtual Machine):&lt;/strong&gt; Used for Java, Kotlin, Scala. Ideal for large business systems (banking solutions), web servers (Spring Framework), and Android platforms.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Extensive ecosystem (huge library availability), Just-In-Time compilation technology.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; High initial RAM usage, comparatively slow startup times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fact:&lt;/strong&gt; Adopted by nearly 90% of Fortune 500 companies worldwide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;V8 (JavaScript Engine):&lt;/strong&gt; Powering Node.js and modern browsers. Optimized for web environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Exceptional performance for client-side websites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con:&lt;/strong&gt; Not well suited for long-term server-based solutions, where BEAM shines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CLR (.NET Common Language Runtime):&lt;/strong&gt; Supports languages within the .NET family, notably C#. Strongly integrated with Microsoft technologies (Azure).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Seamless integration with Windows platforms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con:&lt;/strong&gt; Limited cross-platform capabilities compared to JVM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;BEAM (Erlang VM):&lt;/strong&gt; Designed for Erlang, Elixir, and other compatible languages (LFE, Alpaca). Best suited for highly scalable, resilient, and concurrent systems, excelling in messaging, telecommunications, and fintech.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; Capable of handling millions of simultaneous processes with minimal delays.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Con:&lt;/strong&gt; Smaller toolkit and framework libraries, though actively growing (Phoenix, Nerves frameworks).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Comparison Table:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VM&lt;/th&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;th&gt;Weaknesses&lt;/th&gt;
&lt;th&gt;Use Cases&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JVM&lt;/td&gt;
&lt;td&gt;Versatility, extensive ecosystem&lt;/td&gt;
&lt;td&gt;Memory consumption, startup lag&lt;/td&gt;
&lt;td&gt;Enterprise, Android&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;V8&lt;/td&gt;
&lt;td&gt;Speed for web clients&lt;/td&gt;
&lt;td&gt;Unsuitable for backend servers&lt;/td&gt;
&lt;td&gt;Node.js, Browsers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLR&lt;/td&gt;
&lt;td&gt;Integration with Microsoft tech&lt;/td&gt;
&lt;td&gt;Dependence on MS ecosystem&lt;/td&gt;
&lt;td&gt;.NET, Azure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BEAM&lt;/td&gt;
&lt;td&gt;Concurrency, resilience&lt;/td&gt;
&lt;td&gt;Fewer libraries &amp;amp; frameworks&lt;/td&gt;
&lt;td&gt;Real-time, Telecom&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;By 2025, with the rise of IoT and streaming data, BEAM has become particularly relevant for real-time applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deeper Look Into BEAM 🌌
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BEAM&lt;/strong&gt; is a virtual machine engineered for programming languages like &lt;strong&gt;Erlang&lt;/strong&gt;, &lt;strong&gt;Elixir&lt;/strong&gt;, and others (such as LFE). Initially developed in the 1990s by Ericsson for telecommunication purposes, where reliability and capacity to manage massive traffic were critical, today BEAM forms the backbone of numerous high-performance services. It handles billions of daily messages (as seen in WhatsApp), supports millions of online gamers simultaneously (products like League of Legends), and keeps real-time chat services (Discord) functioning smoothly. As the need for high throughput, low latency, and robustness grows, BEAM proves indispensable in 2025 more than ever before.&lt;/p&gt;

&lt;h3&gt;
  
  
  History of BEAM: From Telco to Global Platforms 📜
&lt;/h3&gt;

&lt;p&gt;Originally, BEAM stood for "&lt;em&gt;Bogdan's Erlang Abstract Machine&lt;/em&gt;" named after Bogumil Hausman ("Bogdan"), who created its first iteration. However, over time, it became associated with another author—"&lt;em&gt;Björn's Erlang Abstract Machine&lt;/em&gt;", reflecting contributions made by Björn Gustavsson, who maintains the current implementation. Both contributors played significant roles in shaping the system while working at Ericsson.&lt;/p&gt;

&lt;p&gt;Before BEAM emerged, there was another virtual machine called JAM (&lt;em&gt;Joe's Abstract Machine&lt;/em&gt;)—the first interpreter for the Erlang language. Developed by Joe Armstrong alongside Mike Williams in C, detailed insights are found in the &lt;a href="https://www.erlang.org/blog/beam-compiler-history/" rel="noopener noreferrer"&gt;developers' blog&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Therefore Bogumil (“Bogdan”) Hausman created TEAM (Turbo Erlang Abstract Machine). It compiled the Erlang code to C code, which was then compiled to native code using GCC.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Today, BEAM finds widespread use in finance (Klarna), advertising (AdRoll), notification systems (Pinterest), and Internet of Things (IoT) deployments (Nerves). With advancements such as Just-In-Time compiler introduction in OTP 24 and type improvement enhancements in Elixir v1.15+, BEAM remains a viable choice. Future plans include closer alignment with WebAssembly for supporting web applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's So Great About BEAM? 🚀
&lt;/h2&gt;

&lt;p&gt;BEAM distinguishes itself among other virtual machines due to unique characteristics essential for scalable and demanding applications. Below are its main advantages, supported by concrete figures and comparisons with similar solutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fast and Lightweight Processes&lt;/strong&gt; ⚡&lt;br&gt;&lt;br&gt;
Process creation in BEAM takes only microseconds, consuming approximately 2.5 KB per process, allowing millions of instances to coexist on a single machine. Compared to Java threads requiring megabytes of memory, BEAM offers superior efficiency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 While asynchronous tasks in Rust are lightweight too, BEAM surpasses in building distributed solutions due to its actor model, isolated processes with negligible overhead, auto-failure recovery via supervisors, and hot code swapping without downtime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actor Model&lt;/strong&gt; 🎭&lt;br&gt;&lt;br&gt;
Processes behave independently, exchanging messages without sharing resources or causing blocking. Unlike Java threads, this greatly simplifies concurrent processing. Imagine a chat messenger like Discord where each room acts as an independent process freely accepting new messages regardless of other activities. Performance is impressive: BEAM can process roughly 100k messages per second on a single core.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Let It Crash Philosophy&lt;/strong&gt; 💥&lt;br&gt;&lt;br&gt;
BEAM embraces the principle of “let it crash”: if a process fails, it simply stops executing. Yet, thanks to careful design, failure goes largely unnoticed by end-users. Consider managing a busy restaurant where someone suddenly leaves mid-shift. Typically, chaos ensues, disrupting service. In BEAM, however, there's a supervisor-like manager immediately spawning a replacement process. Customers continue receiving orders promptly, unaware of any interruption.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;☎️ This philosophy proved effective back in the '90s when Ericsson used BEAM to maintain large telecommunication stations. If a component crashed, the overall system remained operational without disruption.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hot Code Reloading&lt;/strong&gt; 🔄&lt;br&gt;&lt;br&gt;
Applications can update themselves without halting ongoing operations. BEAM holds two versions of software modules simultaneously, switching almost instantaneously. In contrast, Java transitions require additional effort and may cause temporary delays. Successful implementations include payment gateway Klarna, updating continuously without a single second of downtime, ensuring continuous shopping experiences for customers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Distributed Systems&lt;/strong&gt; 🌐&lt;br&gt;&lt;br&gt;
Individual nodes connect into unified clusters through network links. This setup enhances fault tolerance and horizontal scaling. Take Pinterest notifications: thousands of new servers easily join existing clusters, boosting total output without compromising individual node performance. Scalability extends to tens of thousands of nodes with minimal request latencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low Latency&lt;/strong&gt; ⏱&lt;br&gt;&lt;br&gt;
Incremental garbage collection minimizes delays. The Phoenix framework, built atop BEAM, handles thousands of requests with millisecond-level responses. Other languages like Python occasionally experience longer pauses due to their garbage collection models, whereas BEAM consistently performs under heavy loads with low latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Just-In-Time Compilation&lt;/strong&gt; 🚀&lt;br&gt;&lt;br&gt;
Starting with OTP 24, BEAM introduces Just-In-Time compilation, improving computational-intensive task speeds by up to 20–30%. Although BEAM primarily focuses on concurrency, this optimization dramatically increases execution speed for computation-heavy tasks. Parsing JSON files now happens faster thanks to enhanced compiler optimizations.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Inside BEAM: How Does It Work? 🛠
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Scheduler: Maestro of Multitasking 🎻
&lt;/h3&gt;

&lt;p&gt;The scheduler in BEAM operates like a conductor leading an orchestra. Thousands of musicians (processes) play together, each needing their turn to perform. Nobody plays for too long—otherwise, music turns chaotic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Real-world Example:&lt;/strong&gt; A single WhatsApp server handled millions of active connections simultaneously. Implementing this feat using conventional OS threads would have been impossible, yet BEAM manages it gracefully with its unique scheduling scheme.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;How It Works:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is one scheduler per CPU core. On an eight-core server, eight schedulers operate.&lt;/li&gt;
&lt;li&gt;Every process receives a fixed number of steps ("reductions") before yielding control to another process.&lt;/li&gt;
&lt;li&gt;Processes fall into two queues: ready-for-execution and waiting-for-resources.&lt;/li&gt;
&lt;li&gt;Load balancing employs task stealing mechanics to redistribute workload evenly.&lt;/li&gt;
&lt;li&gt;Time-wheel structure stores active timers for efficient timing operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Common Issue:&lt;/strong&gt; Long-running Built-In Functions (BIFs) executed without yielding control can block scheduler activity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Garbage Collection: Keeping Memory Clean 🧹
&lt;/h3&gt;

&lt;p&gt;The garbage collector (GC) in BEAM works similarly to a diligent janitor silently cleaning tables in a bustling city full of restaurants. Each process has its dedicated storage area ("heap"). When unused objects fill a heap, cleanup occurs locally, leaving other processes unaffected.&lt;/p&gt;

&lt;p&gt;BEAM implements generational garbage collection: newer elements ("young generation") frequently cleaned because they tend to die quickly, while older generations are checked less often.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Common Issue:&lt;/strong&gt; Accumulation of long-lived objects can lead to increased resource consumption. Monitor parameters like high_water carefully.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Large binary data (files, images) resides separately outside the GC zone, avoiding unnecessary bloat.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Real-world Example:&lt;/strong&gt; Discord manages millions of active channels on BEAM. Even if one particular chat accumulates transient data, the cleanup mechanism swiftly removes it without impacting other chats’ performance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Algorithm Overview:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heap and stack expand towards each other until meeting, triggering cleanup.&lt;/li&gt;
&lt;li&gt;Two types of cycles exist: minor (frequent young-generation cleanups) and major (infrequent old-generation cleanups).&lt;/li&gt;
&lt;li&gt;Data copying technique moves values between two memory zones ("from space" → "to space").&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✨ Carefully monitor memory growth dynamics in your processes. Rapidly expanding sizes might indicate presence of long-living objects requiring architectural adjustments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Register-Based Architecture: Faster and More Efficient 🧠
&lt;/h3&gt;

&lt;p&gt;BEAM utilizes register-based architecture. Registers act as small, fast-access memory regions storing intermediate results of calculations. Each register holds specific values participating in computational operations. By keeping data close to the processor, registers enable swift access and high-performance operation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Simple addition (&lt;code&gt;add(A, B)&lt;/code&gt; resulting value stored in registers X0 and X1).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Operational Principles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registers X (up to 1024): Store function arguments.&lt;/li&gt;
&lt;li&gt;Registers Y: Local variables.&lt;/li&gt;
&lt;li&gt;Instructions like &lt;code&gt;move&lt;/code&gt; and &lt;code&gt;call&lt;/code&gt;, direct memory addressing allowed. JIT compilation substantially improves performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Common Issue:&lt;/strong&gt; Overflowing registers — pay attention to function size and optimization strategies.&lt;/p&gt;

&lt;p&gt;✨ &lt;strong&gt;Performance Tips:&lt;/strong&gt; Minimize stack usage, utilize registers effectively for intermediate result storage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Supervisors and Fault-Tolerance: Let It Crash! 💥
&lt;/h3&gt;

&lt;p&gt;In typical systems, a failing process means disaster. In BEAM, however, this event aligns with the development philosophy.&lt;/p&gt;

&lt;p&gt;Consider electrical networks: A vast grid where one substation breaks down. Normally, entire neighborhoods lose power. In BEAM, automated switches disconnect the faulty element and reconnect reserve units instantly, preserving normalcy throughout the city.&lt;/p&gt;

&lt;p&gt;Supervisors form hierarchical structures. When child processes crash, parent supervisors decide whether to restart affected components individually or collectively.&lt;/p&gt;

&lt;p&gt;Such architecture guarantees automatic restoration of system integrity.&lt;/p&gt;

&lt;p&gt;Mechanisms involved: tree-structured supervision, one-for-one strategy, state monitoring (running, garbage collection).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Common Issue:&lt;/strong&gt; Ignoring supervisor role, opting for custom solutions instead of proven OTP libraries.&lt;/p&gt;

&lt;p&gt;✨ Developer Advice: Design applications tolerant of errors, leave failure-handling logic to specialized supervisor mechanisms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5. Dynamic Code Loading and Distributed Systems 🌍
&lt;/h3&gt;

&lt;p&gt;BEAM allows live updates to running applications. Similar to a musician changing instruments mid-performance invisibly to listeners.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Two module versions reside simultaneously:&lt;/strong&gt; Old handles ongoing requests, new accepts fresh ones. Updates occur transparently without stopping the application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Replacement:&lt;/strong&gt; Achieved via command &lt;code&gt;:code.load_file(Module)&lt;/code&gt;. Follow proper update rules to avoid disruptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution:&lt;/strong&gt; Check remote node availability using &lt;code&gt;net_adm:ping(Node)&lt;/code&gt;, leverage Mnesia database. Clustering examples exist in Kubernetes environments utilizing BEAM-powered virtual machines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scaling:&lt;/strong&gt; Utilize OTP tools for cluster-wide node monitoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Scalability: BEAM Network Across Thousands of Nodes 🌐
&lt;/h3&gt;

&lt;p&gt;BEAM isn't merely a standalone node but a connected network behaving as a unified whole. Individual network members interact seamlessly, forming a coherent distributed infrastructure.&lt;/p&gt;

&lt;p&gt;Key benefits of this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy clusters spanning multiple servers, enabling transparent communication between processes.&lt;/li&gt;
&lt;li&gt;Particularly beneficial in IoT systems and microservices architectures, where message delivery automatically distributes across appropriate servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Real-world Case Study:&lt;/strong&gt; Pinterest applied this technology to deliver notifications. As traffic grew, adding extra servers scaled the system effortlessly.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  BEAM in 2025: Why Should You Pay Attention? 🚀
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Handling Massive Traffic: WhatsApp manages millions of simultaneous connections.&lt;/li&gt;
&lt;li&gt;Scalable Microservices: Effortless deployment and management in Kubernetes.&lt;/li&gt;
&lt;li&gt;Real-time Operations: Phoenix provides lightning-fast chat communications.&lt;/li&gt;
&lt;li&gt;Reliability and Resilience: Proven in big players like Klarna.&lt;/li&gt;
&lt;li&gt;IoT and AI: Nerves for IoT devices, TensorFlow compatibility via port mechanisms.&lt;/li&gt;
&lt;li&gt;Future Development: Enhanced performance through JIT compilation, WebAssembly support for web apps.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Success Story:&lt;/strong&gt; Pinterest reduced notification delay by 50%, leveraging BEAM.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  BEAM — The Future That's Already Here! 🌌
&lt;/h2&gt;

&lt;p&gt;As we stand in 2025, Elixir and Erlang occupy niche positions among commonly used development tools, drawing little public attention. Personally, I believe this situation is unfair given the truly amazing and innovative possibilities offered by these technologies. Despite the learning curve and project complexity, the rewards promise to be substantial!&lt;/p&gt;

&lt;p&gt;Embrace the world of Elixir/Erlang, launch a new project, and experience the true power of the BEAM virtual machine! ✨&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Note from the Author
&lt;/h2&gt;

&lt;p&gt;Thank you very much for taking interest in this article! Hopefully, it helped clarify what BEAM is and why it's so widely adopted. Initially, I planned to write something more advanced and filled with technical details, but realized I'd struggle to top the comprehensive guide provided in the book &lt;a href="https://blog.stenmans.org/theBeamBook/#CH-BEAM" rel="noopener noreferrer"&gt;&lt;em&gt;The BEAM Book: Understanding the Erlang Runtime System&lt;/em&gt;&lt;/a&gt;. Thus, I've opted for a simpler explanation.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>beam</category>
      <category>erlang</category>
      <category>virtualmachine</category>
    </item>
    <item>
      <title>BEAM: Простыми словами</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Thu, 25 Sep 2025 06:58:34 +0000</pubDate>
      <link>https://dev.to/adamanq/beam-prostymi-slovami-3104</link>
      <guid>https://dev.to/adamanq/beam-prostymi-slovami-3104</guid>
      <description>&lt;p&gt;Представьте вселенную, где ежесекундно происходят миллиарды сообщений, звонков и финансовых операций. Каждое послание достигает адресата, звонки продолжаются бесперебойно, а средства поступают моментально. Что обеспечивает такую безупречную работу? Нет, это не волшебство, а именно &lt;strong&gt;виртуальные машины&lt;/strong&gt; — незаметные герои цифровой инфраструктуры.&lt;/p&gt;

&lt;p&gt;Среди них есть одна выдающаяся машина — &lt;strong&gt;BEAM&lt;/strong&gt;. Созданная еще в прошлом веке, она выглядит как разработка из далекого будущего. Именно она поддерживает стабильную работу таких гигантов, как WhatsApp, Discord, финансовые сервисы и IoT системы. Даже спустя десятилетия после своего появления, BEAM продолжает поражать своей производительностью и надежностью.&lt;/p&gt;

&lt;p&gt;Цель этой статьи вовсе не в подробном погружении в механизмы работы BEAM и внутренние детали реализации. Здесь мы не будем разбираться, куда уходят байты или приводить конкретные внутренние фрагменты кода. Эта статья предназначена лишь для общего понимания сути технологии, её назначения и возможностей, полезных для вас и вашего бизнеса в условиях больших нагрузок. Для тех, кто желает глубже изучить внутреннее устройство BEAM, рекомендую книгу — &lt;a href="https://blog.stenmans.org/theBeamBook/#CH-BEAM" rel="noopener noreferrer"&gt;The BEAM Book: Understanding the Erlang Runtime System&lt;/a&gt; на английском языке. (А вот русскоязычная версия вскоре появится в &lt;a href="//t.me/digital_tent"&gt;моём телеграм-канале&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Сегодня, когда эпоха характеризуется бурным развитием технологий реального времени, искусственного интеллекта и массового параллельного подключения пользователей, BEAM выделяется своей способностью обеспечивать высокую скорость отклика, надежность даже при отказах и быструю адаптацию к росту нагрузки, превращаясь в оптимальное решение для сложных задач. Но, начнём с основ!&lt;/p&gt;




&lt;h2&gt;
  
  
  Что такое виртуальная машина? 🤔
&lt;/h2&gt;

&lt;p&gt;Представь себе написание программы, которую ты хочешь запустить абсолютно на всём — будь то ноутбук, смартфон, сервер или даже умная колонка. Как добиться совместимости с разными операционными системами и железом? Решение простое — использовать виртуальную машину (ВМ). Она представляет собой специальную среду выполнения, которая интерпретирует твой код, действуя как мостик между твоим приложением и конкретным устройством. ВМ берет промежуточный код (например, байт-код Java) и преобразует его в инструкции, подходящие именно этой платформе, освобождая тебя от забот о деталях работы памяти или многопоточности. Проще говоря, виртуальная машина — это своего рода универсальный переходник, позволяющий твоему коду запускаться повсеместно!&lt;/p&gt;

&lt;p&gt;Кроме того, виртуальные машины часто имеют полезные инструменты, такие как автоматический сборщик мусора, встроенные оптимизации производительности и средства защиты данных. Благодаря этому разработка приложений становится значительно легче и эффективнее. Без ВМ создание современных проектов, начиная от мобильных игр и заканчивая крупными облачными сервисами, было бы намного сложнее.&lt;/p&gt;




&lt;h2&gt;
  
  
  Зачем нужны виртуальные машины? 🌟
&lt;/h2&gt;

&lt;p&gt;Виртуальные машины — это не просто комфорт разработчиков, а мощный инструмент для создания универсальных, надежных и легко расширяемых решений. Рассмотрим основные преимущества виртуальных машин на понятных примерах:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Кроссплатформенность&lt;/strong&gt; 📱💻&lt;br&gt;&lt;br&gt;
Написал код один раз — работает везде. Например, банковское приложение на Java запускается на сервере банка и на Android-смартфоне клиента благодаря JVM. Это экономит время и ресурсы, особенно в enterprise-разработке, где команды работают с разными платформами.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Управление ресурсами&lt;/strong&gt; 🧹&lt;br&gt;&lt;br&gt;
Виртуальная машина берет на себя контроль над оперативной памятью (например, посредством сборки мусора), многопоточностью и взаимодействием с техникой. Так, в VM Erlang (BEAM) каждому процессу выделяется отдельная область памяти, что позволяет избежать проблем с утечками даже при падении отдельных процессов. Такая архитектура снижает риск возникновения багов и существенно облегчает написание кода, поскольку разработчикам больше не приходится самостоятельно заботиться о выделении и освобождении памяти.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Безопасность&lt;/strong&gt; 🔒&lt;br&gt;&lt;br&gt;
За счет изоляции исполняемого кода от операционной системы, виртуальные машины создают своего рода песочницу, защищающую систему от потенциально опасных действий программы. Эта функция незаменима в облаках вроде AWS Lambda или Google Cloud Functions, где на одной машине одновременно выполняются тысячи фрагментов кода от разных пользователей.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Оптимизация&lt;/strong&gt; ⚡&lt;br&gt;&lt;br&gt;
Многие виртуальные машины способны динамически оптимизировать исполнение программного кода прямо во время работы приложения. Яркий пример — механизм Just-in-time компиляции (JIT). Именно благодаря ему браузерные движки типа V8 делают JavaScript невероятно быстрым, а среда BEAM начиная с версии OTP 24 заметно повышает производительность при помощи собственного механизма JIT. Такие технологии полезны там, где программа многократно повторяется, как, например, в высоконагруженных веб-приложениях.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Поддержка сложных систем&lt;/strong&gt; 🌐&lt;br&gt;&lt;br&gt;
Некоторые виртуальные машины специально спроектированы для обеспечения конкурентности, распределения нагрузки и устойчивости к сбоям. Так, сервера популярной игры &lt;a href="https://highscalability.com/how-league-of-legends-scaled-chat-to-70-million-players-it-t/" rel="noopener noreferrer"&gt;League of Legends основаны именно на BEAM&lt;/a&gt;, что позволяет эффективно обрабатывать миллионы одновременных подключений игроков с минимальными задержками и высоким уровнем надежности.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Таким образом, виртуальные машины помогают разработчикам сосредоточиться непосредственно на создании функциональности продукта, а не на низкоуровневых технических аспектах, ускоряя процессы разработки и снижая расходы.&lt;/p&gt;




&lt;h2&gt;
  
  
  Популярные виртуальные машины: Кто есть? 🦸‍♂️
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;JVM (Java Virtual Machine):&lt;/strong&gt; Используется для Java, Kotlin, Scala. Отлично подходит для крупных бизнес-систем (например, банковские решения), веб-серверов (Spring Framework) и платформ Android. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Преимущества:&lt;/strong&gt; обширная инфраструктура (огромное количество готовых библиотек), технология Just-In-Time компиляции. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Недостатки:&lt;/strong&gt; большие начальные затраты оперативной памяти и сравнительно долгий запуск приложений.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Факт:&lt;/strong&gt; Решение применяется в большинстве (около 90%) крупнейших мировых корпораций (Fortune 500).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;V8 (JavaScript Engine):&lt;/strong&gt; Основана в Node.js и современных браузерах. Оптимизировано для работы в веб-пространстве. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Преимущества:&lt;/strong&gt; высокая производительность для клиентской части сайта. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Недостаток:&lt;/strong&gt; плохо приспособлена для долговременных серверных решений, где преимущество на стороне BEAM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CLR (.NET Common Language Runtime):&lt;/strong&gt; Поддерживает языки семейства .NET, включая C#. Хорошо интегрируется с технологиями от Microsoft (например Azure). &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Преимущества:&lt;/strong&gt; тесная совместимость с Windows-платформой.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Недостаток:&lt;/strong&gt; ограниченная кросс-платформенность по сравнению с JVM.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;BEAM (Erlang VM):&lt;/strong&gt; Работает с языками Erlang, Elixir, LFE. Лучше всего справляется с высоко нагруженными системами, ориентированными на масштабируемость, устойчивость и параллельность обработки запросов. Прекрасно подходит для мессенджеров, телекоммуникационных сервисов и финансовых технологий. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Преимущества:&lt;/strong&gt; поддерживает миллионы параллельных процессов одновременно, минимизирует задержки выполнения операций. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Недостаток:&lt;/strong&gt; меньший объем библиотеки инструментов и фреймворков, хотя активно развивается (например Phoenix, Nerves).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Сравнение в таблице&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;ВМ&lt;/th&gt;
&lt;th&gt;Сильные стороны&lt;/th&gt;
&lt;th&gt;Слабые стороны&lt;/th&gt;
&lt;th&gt;Применение&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JVM&lt;/td&gt;
&lt;td&gt;Универсальность, экосистема&lt;/td&gt;
&lt;td&gt;Память, запуск&lt;/td&gt;
&lt;td&gt;Enterprise, Android&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;V8&lt;/td&gt;
&lt;td&gt;Скорость для веб&lt;/td&gt;
&lt;td&gt;Не для серверов&lt;/td&gt;
&lt;td&gt;Node.js, браузеры&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLR&lt;/td&gt;
&lt;td&gt;Интеграция с Microsoft&lt;/td&gt;
&lt;td&gt;Привязка к экосистеме&lt;/td&gt;
&lt;td&gt;.NET, Azure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BEAM&lt;/td&gt;
&lt;td&gt;Конкурентность, отказоустойчивость&lt;/td&gt;
&lt;td&gt;Экосистема, численные расчёты&lt;/td&gt;
&lt;td&gt;Реальное время, телеком&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;В 2025 году, с ростом IoT и стриминга данных, BEAM особенно актуальна для реального времени.&lt;/p&gt;




&lt;h2&gt;
  
  
  Подробнее о BEAM  🌌
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BEAM&lt;/strong&gt; представляет собой виртуальную машину, предназначенную для работы с языками программирования &lt;strong&gt;Erlang&lt;/strong&gt;, &lt;strong&gt;Elixir&lt;/strong&gt; и рядом других (например, LFE, Alpaca). Изначально она была создана в 1990-е годы в компании Ericsson специально для телекоммуникаций, где крайне важны устойчивость к сбоям и способность выдерживать огромные нагрузки. Сейчас BEAM лежит в основе многих высоконагруженных сервисов: она обслуживает миллиарды сообщений ежедневно (как WhatsApp), поддерживает миллионы пользователей онлайн-игр одновременно (например, продукты Riot Games) и обеспечивает бесперебойную работу чат-сервисов реального времени (таких как Discord). В условиях современного мира, где критически важными становятся высокая производительность, минимальное время отклика и надежность, в 2025 году BEAM демонстрирует свою незаменимость сильнее, чем когда бы то ни было ранее.&lt;/p&gt;

&lt;h3&gt;
  
  
  История BEAM: От телекома к глобальным платформам 📜
&lt;/h3&gt;

&lt;p&gt;Изначально аббревиатура BEAM расшифровывалась как &lt;em&gt;«Bogdan’s Erlang Abstract Machine»&lt;/em&gt; в честь Богумила "Богдана" Хаусмана, который создал первую её версию. Однако сейчас название часто связывают с именем другого автора — &lt;em&gt;«Björn’s Erlang Abstract Machine»&lt;/em&gt;, поскольку именно Бьёрн Густавссон разработал и поддерживает актуальную реализацию. Оба специалиста внесли значительный вклад в развитие системы, трудясь в компании Ericsson.&lt;/p&gt;

&lt;p&gt;До появления BEAM существовала другая виртуальная машина — JAM (&lt;em&gt;Joe’s Abstract Machine&lt;/em&gt;), ставшая первым интерпретатором языка программирования Erlang. Она была создана Джо Армстронгом совместно с Майком Уильямсом на языке C (подробности можно найти в &lt;a href="https://www.erlang.org/blog/beam-compiler-history/" rel="noopener noreferrer"&gt;блоге разработчиков&lt;/a&gt;).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Поэтому Богумил («Богдан») Хаусман создал TEAM (Турбо-Эрланг-Абстрактную-машину). Она компилировала код Эрланга в Си-код, который затем компилировался в нативный код с помощью GCC.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;На сегодняшний день BEAM активно применяется в финансовом секторе (например, Klarna), рекламной индустрии (AdRoll), системах уведомлений (Pinterest) и сфере Интернета вещей (Nerves). Благодаря внедрению технологии Just-In-Time компилятора в версии OTP 24 и развитию языка Elixir до уровня 1.15+, включающего улучшение работы с типизацией, BEAM продолжает оставаться актуальным решением. В будущем планируется ещё большее сближение с технологией WebAssembly для поддержки веб-приложений.&lt;/p&gt;




&lt;h2&gt;
  
  
  Почему BEAM — это круто? 🚀
&lt;/h2&gt;

&lt;p&gt;BEAM отличается от прочих виртуальных машин своими уникальными характеристиками, особенно полезными для масштабируемых и нагруженных приложений. Рассмотрим основные преимущества вместе с конкретными примерами, цифрами и сравнением с аналогичными решениями:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Быстрые и лёгкие процессы&lt;/strong&gt; ⚡&lt;br&gt;
Создание процессов в BEAM занимает всего несколько микросекунд, занимая около ~2.5 КБ оперативной памяти каждый процесс, что позволяет одновременно запустить миллионы экземпляров на одной машине. В отличие от потоков Java Virtual Machine, потребляющих целые мегабайты памяти. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧠 Асинхронные задачи в Rust также легковесны, однако BEAM значительно удобнее для построения распределённых решений благодаря встроенной модели акторов, изолированные процессы с минимальными накладными расходами, автоматическую отказоустойчивость через супервизоры и горячую замену кода без остановки системы.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Модель акторов&lt;/strong&gt; 🎭&lt;br&gt;
Процессы здесь ведут себя как самостоятельные акторы, обменивающиеся сообщениями друг с другом без общих ресурсов и блокировки. Это значительно упрощает работу по сравнению с потоками Java Virtual Machine. Например, представим себе мессенджер вроде Discord, где каждая отдельная комната чата функционирует как независимый процесс, свободно принимающий новые сообщения независимо от других процессов. Производительность впечатляет: виртуальная машина BEAM способна обработать около ста тысяч сообщений всего за одну секунду на одном ядре.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Философия «пусть падает»&lt;/strong&gt; 💥&lt;br&gt;
BEAM основывается на принципе «let it crash»: когда какой-либо процесс ломается, он просто прекращает свою работу. Однако, благодаря продуманному подходу, такое событие практически незаметно для пользователей. Допустим, вы управляете большим рестораном, и вдруг кто-то из сотрудников уходит посреди смены. Обычно такая ситуация привела бы к хаосу среди клиентов, но в BEAM действует своего рода супервайзер («менеджер»), который моментально запускает замену сбойного процесса новым экземпляром. И клиент продолжает получать заказ вовремя, даже не заметив заминки.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;☎️ История показывает, насколько эффективна эта философия: ещё в девяностые годы прошлого века компания Ericsson использовала BEAM для поддержания работоспособности крупных телекоммуникационных станций. Если один из компонентов падал, система продолжала функционировать бесперебойно.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Горячая замена кода&lt;/strong&gt; 🔄&lt;br&gt;
Система позволяет обновлять программы, не останавливая работающую систему. BEAM держит одновременно две версии программного модуля, переключаясь между ними почти мгновенно. Для сравнения, в Java такой переход требует больше усилий и зачастую вызывает кратковременные задержки. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Пример из практики:&lt;/strong&gt; Торговая площадка Klarna, которая обновляется без единой секунды простоя, позволяя клиентам совершать покупки непрерывно.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Распределённые системы&lt;/strong&gt; 🌐&lt;br&gt;
Отдельные узлы BEAM соединяются в единую распределённую систему через сетевые соединения. Такой подход обеспечивает высокую отказоустойчивость и лёгкость горизонтального масштабирования. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Пример из практики:&lt;/strong&gt; в Pinterest тысячи новых серверов легко подключаются к существующему кластеру, увеличивая общую производительность без потери производительности отдельных частей. Масштабируемость достигает десятков тысяч узлов с минимальной задержкой обработки запросов.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Низкая задержка&lt;/strong&gt; ⏱&lt;br&gt;
За счёт инкрементальной сборки мусора BEAM сводит задержки к минимуму. Фреймворк Phoenix, построенный поверх BEAM, способен обрабатывать тысячи запросов с минимальным временем отклика, измеряемым в миллисекундах. Другие языки программирования, такие как Python, иногда испытывают длительные паузы из-за своей модели сбора мусора, тогда как BEAM уверенно справляется с большими нагрузками, сохраняя низкую задержку.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;JIT-компиляция&lt;/strong&gt; 🚀&lt;br&gt;
Начиная с версии OTP 24, BEAM поддерживает технологию Just-In-Time компиляции, ускоряя выполнение интенсивных вычислительных операций примерно на 20–30%. Хотя основной фокус BEAM всё равно остаётся на параллельной обработке, новая оптимизация существенно повышает скорость выполнения ресурсоёмких задач. Например, парсинг JSON-файлов теперь выполняется заметно быстрее благодаря улучшенной оптимизации компилятором.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Внутренности BEAM: Как это работает? 🛠
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Планировщик: Дирижёр многозадачности 🎻
&lt;/h3&gt;

&lt;p&gt;Планировщик BEAM — это как дирижёр в оркестре. У него тысячи музыкантов (процессов), и каждому нужно время, чтобы сыграть свою партию. Никто не играет слишком долго — иначе музыка превратится в хаос.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Пример из практики:&lt;/strong&gt; Один сервер WhatsApp с использованием BEAM одновременно обслуживал миллионы подключений. Такое было бы невозможно реализовать через обычные потоки операционной системы, однако BEAM справляется с этой задачей благодаря своей уникальной схеме планирования процессов.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Механизм работы:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Количество планировщиков соответствует числу ядер процессора. Например, на восьмиядерном сервере действует восемь планировщиков.&lt;/li&gt;
&lt;li&gt;Каждый процесс получает определённое количество шагов выполнения («редукций»), после чего управление передаётся другому процессу.&lt;/li&gt;
&lt;li&gt;Процессы распределяются по двум очередям: готовых к выполнению (ready) и ожидающих ресурсов (waiting).&lt;/li&gt;
&lt;li&gt;Реализована технология перераспределения нагрузок («краже задач») для балансировки активности.&lt;/li&gt;
&lt;li&gt;Используется структура типа «колеса времени»: массив временных интервалов для хранения активных таймеров с целью снижения накладных расходов.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Типичная ошибка:&lt;/strong&gt; Долгие встроенные функции (BIFs), выполняемые без передачи управления, способны заблокировать работу планировщика.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Сборка мусора: Чистота в памяти 🧹
&lt;/h3&gt;

&lt;p&gt;Сборщик мусора (GC) в среде выполнения BEAM действует подобно аккуратному дворнику, незаметно обходящему рестораны большого города BEAM и аккуратно собирающему грязную посуду, освобождая пространство на столах.&lt;/p&gt;

&lt;p&gt;Каждый отдельный процесс имеет свою собственную зону хранения («кучу»). Когда память конкретного процесса заполняется ненужными объектами, уборка проводится именно там, не затрагивая работу остальных процессов.&lt;/p&gt;

&lt;p&gt;BEAM применяет методику поколений (generational garbage collection): часто очищаются недавно созданные элементы («молодое поколение»), поскольку они имеют короткий срок жизни, тогда как старшие поколения проверяются гораздо реже.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Типичная ошибка:&lt;/strong&gt; длительное накопление долгоживущих объектов может привести к повышенному потреблению ресурсов, отслеживайте параметр high_water.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Объёмные двоичные данные (файлы, изображения) располагаются обособленно от основной зоны сборки мусора, предотвращая её чрезмерное заполнение.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Пример из практики:&lt;/strong&gt; Discord обслуживает миллионы активных каналов на платформе BEAM. Даже если один конкретный чат накапливает временные данные, система очистки мгновенно избавляется от них, абсолютно не влияя на производительность других чатов.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Алгоритм работы:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Стек и куча расширяются навстречу друг другу до тех пор, пока не встретятся. Тогда запускается процедура уборки.&lt;/li&gt;
&lt;li&gt;Существуют два типа циклов сбора мусора: молодой (частый, минорный) и старый (редкий, полный).&lt;/li&gt;
&lt;li&gt;Используется техника копирования данных между двумя зонами памяти ("from space" → "to space"), обеспечивая эффективную обработку совместно используемых элементов. Ключевые параметры мониторинга: gen_gcs, high_water. &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✨ Внимательно наблюдайте за динамикой роста областей памяти ваших процессов. Быстро увеличивающиеся размеры могут свидетельствовать о наличии объектов с длительным жизненным циклом, что требует пересмотра архитектуры вашего приложения.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. Архитектура на основе регистров: быстрее и эффективнее 🧠
&lt;/h3&gt;

&lt;p&gt;Машина BEAM основана на регистровой модели работы. Регистровая машина работает через использование набора регистров — специальных быстродействующих областей памяти, предназначенных для хранения промежуточных результатов вычислений. Каждый регистр хранит определённое значение, которое участвует в операции обработки данных. Регистры позволяют быстрее обращаться к данным и обеспечивают высокую производительность выполнения операций, поскольку данные уже находятся в быстрой памяти процессора.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Примеры:&lt;/strong&gt; Простое вычисление суммы двух чисел (add(A, B) → результат в регистрах X0 и X1).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Принцип функционирования:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Регистры X (до 1024 штук): Хранят аргументы функций.&lt;/li&gt;
&lt;li&gt;Регистры Y: Для локальных переменных.&lt;/li&gt;
&lt;li&gt;Используются инструкции типа move и call, поддерживается прямая адресация памяти. Технология JIT существенно повышает производительность.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Типичная ошибка:&lt;/strong&gt; Переполнение регистров — важно следить за размером функций и корректностью оптимизации.&lt;/p&gt;

&lt;p&gt;✨ &lt;strong&gt;Советы по улучшению производительности:&lt;/strong&gt; Уменьшайте использование стека, активно задействуйте регистры для хранения промежуточных значений.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Супервизоры и отказоустойчивость: Пусть падает! 💥
&lt;/h3&gt;

&lt;p&gt;В обычной среде крах процесса означает бедствие. Но в виртуальной машине BEAM это заложено в философию разработки.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Возьмём аналогию электрической сети:&lt;/strong&gt; Есть большая электрическая сеть. В ней одна подстанция «вырубилась». В обычном случае — весь район без света. В BEAM же стоит автоматика: неисправная подстанция отключается, а система мгновенно подключает резервную. Город даже не моргнёт лампочкой.&lt;/p&gt;

&lt;p&gt;Супервизор формируют древовидную структуру. Когда дочерний процесс выходит из строя, узел супервизора определяет стратегию действий: восстановить лишь пострадавший элемент либо целую группу процессов разом.&lt;/p&gt;

&lt;p&gt;Подобная архитектура обеспечивает автоматическое восстановление работоспособности всей системы.&lt;/p&gt;

&lt;p&gt;Механизм работы: дерево супервизоров, стратегии типа one_for_one, состояния выполнения (running, сбор мусора).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Типичная ошибка:&lt;/strong&gt; игнорирование роли супервизора, использование неподходящих инструментов вроде самодельных решений вместо проверенных библиотек OTP.&lt;/p&gt;

&lt;p&gt;✨ Советы разработчикам: пишите приложения так, чтобы ошибки были простительны, а логика обработки сбоев оставляйте специализированным механизмам супервизора.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5. Динамическая загрузка кода и распределенные системы 🌍
&lt;/h3&gt;

&lt;p&gt;Платформа BEAM позволяет обновлять код прямо на лету, когда приложение активно функционирует. Подобно тому, как музыкант переключается на другой инструмент посреди выступления, абсолютно незаметно для аудитории.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;В BEAM одновременно хранятся две версии каждого модуля:&lt;/strong&gt; одна старая продолжает обрабатывать существующие запросы, другая новая принимает новые задачи. Таким образом исправляются ошибки и добавляются функции без остановок работы приложения.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Замена кода:&lt;/strong&gt; выполняется командой :code.load_file(Module). Важно соблюдать правила обновления, иначе возможны сбои.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Распределенность:&lt;/strong&gt; Проверяется доступность удаленных нод через команду net_adm:ping(Node), база данных поддерживается системой Mnesia. Пример реализации — кластеры в Kubernetes с использованием виртуальных машин на основе BEAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Масштабирование:&lt;/strong&gt; Использование инструментов из набора OTP для мониторинга состояния узлов сети.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Масштабируемость: BEAM-сеть на тысячи узлов 🌐
&lt;/h3&gt;

&lt;p&gt;BEAM представляет собой не единый узел, а целую взаимосвязанную сеть, функционирующую как единое целое. Каждый отдельный элемент сети способен взаимодействовать с остальными узлами так, словно они являются частью единой распределённой инфраструктуры.&lt;/p&gt;

&lt;p&gt;Вот несколько преимуществ такой архитектуры:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Вы можете развернуть кластер из множества серверов, позволяя процессам обмениваться сообщениями между собой прозрачно и эффективно.&lt;/li&gt;
&lt;li&gt;Особенно полезно это в системах Internet of Things (IoT) и микросервисных архитектурах, когда доставка сообщений автоматически распределяется BEAM'ом среди нужных серверов.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Пример из практики:&lt;/strong&gt; компания Pinterest применила эту технологию для отправки уведомлений своим пользователям. По мере роста нагрузки всё, что потребовалось сделать, — добавить дополнительные сервера в кластер.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  BEAM в 2025 году: почему стоит обратить внимание? 🚀
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Большие объёмы трафика: WhatsApp обслуживает миллионы подключений одновременно.&lt;/li&gt;
&lt;li&gt;Масштабируемые микросервисы: простое развертывание и управление в Kubernetes.&lt;/li&gt;
&lt;li&gt;Работа в режиме реального времени: Phoenix обеспечивает мгновенные чат-коммуникации.&lt;/li&gt;
&lt;li&gt;Надежность и отказоустойчивость: проверена крупными финансовыми компаниями вроде Klarna.&lt;/li&gt;
&lt;li&gt;Интернет вещей и машинное обучение: Nerves для устройств IoT и совместимость с TensorFlow через механизм портов.&lt;/li&gt;
&lt;li&gt;Перспективы развития: оптимизация производительности благодаря JIT-компиляции и поддержка WebAssembly для веб-приложений.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Пример из практики:&lt;/strong&gt; Pinterest снизил задержку доставки уведомлений на 50% именно благодаря платформе BEAM.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  BEAM — будущее, которое уже здесь! 🌌
&lt;/h2&gt;

&lt;p&gt;На сегодняшний день, в 2025 году, технологии Elixir и Erlang занимают скромное место среди используемых инструментов разработки и не получают должного внимания общественности. Но лично я считаю, что такая ситуация весьма незаслуженна, потому что предлагаемые ими возможности действительно удивительны и оригинальны. Хотя путь освоения и развития проектов на этой платформе может показаться непростым, результат обещает быть потрясающим!&lt;/p&gt;

&lt;p&gt;Откройте для себя мир Elixir/Erlang, окунитесь в разработку нового проекта и ощутите всю мощь виртуальной машины BEAM! ✨&lt;/p&gt;




&lt;h2&gt;
  
  
  От автора
&lt;/h2&gt;

&lt;p&gt;Спасибо большое за интерес к этой статье! Надеюсь, она помогла разобраться, что представляет собой BEAM и зачем она используется. Изначально планировал сделать материал сложнее и добавить больше технических подробностей, однако понял, что вряд ли смогу превзойти книгу &lt;a href="https://blog.stenmans.org/theBeamBook/#CH-BEAM" rel="noopener noreferrer"&gt;The BEAM Book: Understanding the Erlang Runtime System&lt;/a&gt;, поэтому решил изложить всё простыми словами.&lt;/p&gt;

&lt;p&gt;Если эта статья пришлась вам по душе и хочется ещё материалов такого плана, присоединяйтесь ко мне в &lt;a href="//t.me/devs_inc"&gt;моём телеграм-канале&lt;/a&gt;, где выкладываю обзоры книг, публикации по Elixir, переводы технической литературы и интересные новости!&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>erlang</category>
      <category>beam</category>
      <category>virtualmachine</category>
    </item>
    <item>
      <title>Speeding up Elixir: integration with native code (NIF, Ports, etc.)</title>
      <dc:creator>Artyom Molchanov </dc:creator>
      <pubDate>Wed, 21 May 2025 08:59:32 +0000</pubDate>
      <link>https://dev.to/adamanq/speeding-up-elixir-integration-with-native-code-nif-ports-etc-5ajd</link>
      <guid>https://dev.to/adamanq/speeding-up-elixir-integration-with-native-code-nif-ports-etc-5ajd</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt; Before introducing new languages into your project, ensure that your existing Elixir code has been optimized first. In many cases, proper optimization can significantly improve application performance and sometimes yield better results than adding another programming language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Elixir&lt;/strong&gt; and &lt;strong&gt;Erlang&lt;/strong&gt; when are excellent languages for developing scalable and fault-tolerant systems. However, there may come times when you need to extract maximum performance or utilize libraries available only in other languages. If you're facing such situations or simply want to explore how to combine two favorite programming languages, this article is for you! &lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 Search When You Need Native Code Integration
&lt;/h2&gt;

&lt;p&gt;Before diving into technical details, it's important to understand when native code integration should be considered:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Compute-intensive Tasks
&lt;/h3&gt;

&lt;p&gt;The BEAM virtual machine excels at concurrency but isn't designed for heavy computations. Consider integrating native code if your bottleneck involves CPU-bound operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧮 Mathematical Calculations—Linear algebra or matrix operations&lt;/li&gt;
&lt;li&gt;🔐 Cryptography—Encryption/decryption of large datasets&lt;/li&gt;
&lt;li&gt;🎨 Media Processing—Images, videos, audio&lt;/li&gt;
&lt;li&gt;🧠 Machine Learning—Model inference, vector operations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Hardware Interaction
&lt;/h3&gt;

&lt;p&gt;When low-level access to hardware is required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📟 Embedded Systems—Raspberry Pi, microcontrollers (Note: While Nerves exists in Elixir, it might fall short)&lt;/li&gt;
&lt;li&gt;🎮 Specific Drivers—Unconventional devices&lt;/li&gt;
&lt;li&gt;📊 GPU Computing—CUDA, OpenCL&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Reusing Existing Codebases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🏛️ Proven Libraries on C/C++&lt;/li&gt;
&lt;li&gt;📚 Ecosystem Advantages from other languages (e.g., Python for ML)&lt;/li&gt;
&lt;li&gt;🔧 Avoid Reinventing the Wheel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Profiling and Bottlenecks
&lt;/h3&gt;

&lt;p&gt;Typical signs indicating it's time to consider native code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before Optimization - 1000ms&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;SlowModule&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;process_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Potential bottleneck candidate for native optimization&lt;/span&gt;
    &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;complex_calculation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;acc&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;complex_calculation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Imagine CPU-heavy calculations here&lt;/span&gt;
    &lt;span class="c1"&gt;# That don't scale well within BEAM&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Exhausted all possible optimizations in Elixir and still slow?&lt;/span&gt;
&lt;span class="c1"&gt;# Perhaps it's time to introduce native code!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Native extensions transform Elixir into an all-purpose tool where BEAM handles everything except critical computations. This opens doors to ML, hardware, video processing, and more speed-critical tasks.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Balance Comparison of Integration Methods
&lt;/h2&gt;

&lt;p&gt;Choosing the right method of integration is crucial for project success. Here's a comparative table of methods with their pros and cons:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;BEAM Safety&lt;/th&gt;
&lt;th&gt;Implementation Complexity&lt;/th&gt;
&lt;th&gt;Language Support&lt;/th&gt;
&lt;th&gt;Communication Overhead&lt;/th&gt;
&lt;th&gt;Asynchronicity&lt;/th&gt;
&lt;th&gt;Ideal Use Cases использования&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NIF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡⚡⚡⚡⚡&lt;/td&gt;
&lt;td&gt;❌ Dangerous&lt;/td&gt;
&lt;td&gt;🔧🔧🔧 Medium&lt;/td&gt;
&lt;td&gt;C, C++&lt;/td&gt;
&lt;td&gt;None (direct call)&lt;/td&gt;
&lt;td&gt;❌ Blocks scheduler&lt;/td&gt;
&lt;td&gt;Microservices, lengthy CPU-bound tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dirty NIF&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡⚡⚡⚡&lt;/td&gt;
&lt;td&gt;⚠️ Conditionally Safe&lt;/td&gt;
&lt;td&gt;🔧🔧🔧 Medium&lt;/td&gt;
&lt;td&gt;C, C++&lt;/td&gt;
&lt;td&gt;None (direct call)&lt;/td&gt;
&lt;td&gt;✅ Doesn't block primary scheduler&lt;/td&gt;
&lt;td&gt;Long computational tasks (&amp;gt;1ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡⚡&lt;/td&gt;
&lt;td&gt;✅✅ Completely Safe&lt;/td&gt;
&lt;td&gt;🔧 Simple&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;High (IPC)&lt;/td&gt;
&lt;td&gt;✅ Process isolation&lt;/td&gt;
&lt;td&gt;Python, Go, Bash-scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port Driver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡⚡⚡⚡&lt;/td&gt;
&lt;td&gt;✅ Safe&lt;/td&gt;
&lt;td&gt;🔧🔧🔧🔧 Complex&lt;/td&gt;
&lt;td&gt;C, C++&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;✅ Dedicated thread&lt;/td&gt;
&lt;td&gt;Video/audio processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;gRPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡⚡&lt;/td&gt;
&lt;td&gt;✅✅ Completely Safe&lt;/td&gt;
&lt;td&gt;🔧🔧 Medium&lt;/td&gt;
&lt;td&gt;Any with gRPC support&lt;/td&gt;
&lt;td&gt;Moderate (network)&lt;/td&gt;
&lt;td&gt;✅ Separate service&lt;/td&gt;
&lt;td&gt;Microservice architecture&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rustler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡⚡⚡⚡&lt;/td&gt;
&lt;td&gt;✅ Safe&lt;/td&gt;
&lt;td&gt;🔧🔧 Medium&lt;/td&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;None (direct call)&lt;/td&gt;
&lt;td&gt;✅ Supports asynchronous APIs&lt;/td&gt;
&lt;td&gt;Alternative to C NIF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zigler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚡⚡⚡⚡⚡&lt;/td&gt;
&lt;td&gt;✅ Safe&lt;/td&gt;
&lt;td&gt;🔧🔧 Medium&lt;/td&gt;
&lt;td&gt;Zig&lt;/td&gt;
&lt;td&gt;None (direct call)&lt;/td&gt;
&lt;td&gt;✅ Safer than standard NIFs&lt;/td&gt;
&lt;td&gt;Alternative to C in NIF&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Visual Comparison by Key Metrics
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Speed:               Safety:               Simplicity:
NIF         █████    Port        █████     Port        █████
Dirty NIF   ████     Dirty NIF   ███       gRPC        ████
Rustler     ████     Rustler     ████      Rustler     ████
Zigler      █████    Zigler      ████      Zigler      ████
Port Driver ████     Port Driver ████      NIF         ███
Port        ██       gRPC        █████     Port Driver █
gRPC        ██       NIF         █         Dirty NIF   ███
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mechanisms Behind Integration
&lt;/h2&gt;

&lt;p&gt;ㅤ &lt;br&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 NIFs — Maximal Performance
&lt;/h3&gt;

&lt;p&gt;Native Implemented Functions (NIFs) offer the fastest way to integrate native code, albeit being the riskiest option. They execute directly in the BEAM scheduler's thread, ensuring lightning-fast execution due to zero overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How Do NIFs Work?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compilation:&lt;/strong&gt; Native code (C/Rust/Zig) compiles into a dynamic library (.so, .dll)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loading:&lt;/strong&gt; BEAM loads the library during module startup via :erlang.load_nif/2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct Execution:&lt;/strong&gt; Functions run in the same thread as the calling Elixir code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ &lt;strong&gt;Benefits&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blazing Speed:&lt;/strong&gt; Calls take approximately 0.1–1 µs &lt;em&gt;(~100–1000× faster than Ports)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access to BEAM API:&lt;/strong&gt; Direct interaction with Erlang terms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Serialization:&lt;/strong&gt; No encoding/decoding costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution:&lt;/strong&gt; Library ships alongside the OTP app&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Drawbacks&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Risk of VM Crash: Errors in NIF terminate the entire BEAM instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduler Blocking:&lt;/strong&gt; Long-running NIFs freeze multithreading&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging Difficulty:&lt;/strong&gt; Memory leaks hard to detect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform Dependency:&lt;/strong&gt; Requires compilation per target architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Dirty NIFs — Safer Alternative&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With Erlang/OTP 20+, we have Dirty NIFs, which execute in dedicated thread pools, allowing longer computations without blocking the BEAM scheduler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Defining a Dirty NIF&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ErlNifFunc&lt;/span&gt; &lt;span class="n"&gt;nif_funcs&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"long_computation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;long_computation_nif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ERL_NIF_DIRTY_CPU&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use ERL_NIF_DIRTY_CPU for CPU-bound ops and ERL_NIF_DIRTY_IO for I/O-bound ones&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔌 &lt;strong&gt;Ports — Complete Isolation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Ports provide a safe mechanism for interacting with external programs through standard I/O streams (stdin/stdout). It's the safest form of integration since the external program runs in its own OS process.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How Do Ports Work?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launch:&lt;/strong&gt; Elixir spawns an external program as a separate OS process&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communication:&lt;/strong&gt; Data exchange occurs over stdin/stdout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; Failure of the external program doesn't affect BEAM&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ &lt;strong&gt;Benefits&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full Safety:&lt;/strong&gt; Isolation ensures BEAM stability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language Agnosticism:&lt;/strong&gt; Works with any programming language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Debugging:&lt;/strong&gt; External program can be tested independently&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency-Free:&lt;/strong&gt; No specific BEAM-compatible libraries needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Downsides&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High Overheads:&lt;/strong&gt; Approximately 100–500 µs per invocation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serialization Required:&lt;/strong&gt; Needs conversion of data (usually to JSON)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blocking Calls:&lt;/strong&gt; By default blocks the caller process&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🚗 &lt;strong&gt;Port Drivers — Goldilocks Zone&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Port Drivers are high-performance alternatives to Ports, though they require greater complexity. These drivers are written in C and embedded directly into BEAM’s address space, running in separate threads.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How Do Port Drivers Work?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Load:&lt;/strong&gt; BEAM loads the C library into its memory space&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thread Allocation:&lt;/strong&gt; Each driver operates in its own thread&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous Operation:&lt;/strong&gt; Data exchanges occur via message queues&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✅ &lt;strong&gt;Benefits&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Significantly faster than regular ports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety:&lt;/strong&gt; Lower risk compared to NIFs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async Capability:&lt;/strong&gt; Non-blocking operation supported&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convenience:&lt;/strong&gt; No need to spawn additional processes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Disadvantages&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; Requires knowledge of Erlang C API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Scope:&lt;/strong&gt; Only works with C/C++&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less Documentation:&lt;/strong&gt; Few examples and guides available&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🐊 &lt;strong&gt;&lt;a href="https://github.com/E-xyza/zigler" rel="noopener noreferrer"&gt;Zigler&lt;/a&gt; — Wrapper Around NIFs for Zig&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Zigler is a library enabling writing native extensions using the Zig programming language. It integrates the Zig compiler directly into the Elixir compilation cycle, allowing direct coding in Zig inside Elixir modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ &lt;strong&gt;Benefits&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Minimal overhead for native calls&lt;/li&gt;
&lt;li&gt;Memory safety without garbage collection&lt;/li&gt;
&lt;li&gt;Direct access to low-level operations&lt;/li&gt;
&lt;li&gt;Simpler compilation compared to other native extensions&lt;/li&gt;
&lt;li&gt;Hot reloading capability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Disadvantages&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Zig is relatively young with a smaller community&lt;/li&gt;
&lt;li&gt;Higher entry barrier for Elixir developers&lt;/li&gt;
&lt;li&gt;Limited ecosystem of libraries compared to Rust&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🦀 &lt;strong&gt;&lt;a href="https://github.com/rusterlium/rustler" rel="noopener noreferrer"&gt;Rustler&lt;/a&gt; — Wrapping NIFs for Rust&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Rustler is a library for creating native extensions in Erlang/Elixir using Rust. It provides a safe binding between Rust and Erlang/Elixir, facilitating writing NIFs in Rust.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ &lt;strong&gt;Benefits&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Memory safety enforced at compile-time&lt;/li&gt;
&lt;li&gt;High performance for computationally intensive tasks&lt;/li&gt;
&lt;li&gt;Rich ecosystem of Rust packages (crates)&lt;/li&gt;
&lt;li&gt;Protection against crashes in native code (protects BEAM from crashing)&lt;/li&gt;
&lt;li&gt;Parallel execution without blocking the BEAM scheduler&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ❌ &lt;strong&gt;Disadvantages&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Complicated build process and dependency management&lt;/li&gt;
&lt;li&gt;Requires familiarity with two different programming paradigms&lt;/li&gt;
&lt;li&gt;Possible compatibility issues across versions&lt;/li&gt;
&lt;li&gt;Potential bottlenecks at runtime boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🌐 &lt;strong&gt;Global gRPC and Similar Protocols&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For more advanced scenarios involving external services, especially in microservice architectures, protocols like gRPC provide structured and scalable ways of integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ &lt;strong&gt;gRPC Benefits for Elixir&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Contract Schema:&lt;/strong&gt; Strong typing via Protocol Buffers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bidirectional Streaming:&lt;/strong&gt; Supports streaming both ways&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform:&lt;/strong&gt; Supports multiple languages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; More efficient than REST/JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;❗️ An example using &lt;strong&gt;gRPC&lt;/strong&gt; was omitted due to its extensive nature. For detailed info about gRPC in Elixir, see &lt;a href="https://github.com/elixir-grpc/grpc" rel="noopener noreferrer"&gt;elixir-grpc&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧪 Practical Examples of Integration
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;📚 The goal of this article is to give an overview of available native integration methods. Deep implementations and edge cases are omitted to maintain balance between theory and practical application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Exploring Criteria for Choosing Integration Methods
&lt;/h3&gt;

&lt;p&gt;Before moving onto examples, let's define key factors influencing our choice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; How fast does the call execute?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety:&lt;/strong&gt; Risk level for BEAM stability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation Complexity:&lt;/strong&gt; Ease of implementation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language Support:&lt;/strong&gt; Suitability of the chosen language with BEAM&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  ⚙️ &lt;strong&gt;C — Maximum Performance Using NIF and Port Drivers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Optimal Methods:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NIF&lt;/strong&gt; — for instantaneous operations (&amp;lt;1ms)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port Drivers&lt;/strong&gt; — for long-lasting or async tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Choose This Approach?&lt;/strong&gt;&lt;br&gt;
C offers seamless compatibility with BEAM, making it ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Critically performant tasks&lt;/li&gt;
&lt;li&gt;Low-level operations (hardware, GPU)&lt;/li&gt;
&lt;li&gt;Integrating pre-existing C libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Example 1: Fast NIF for Hashing (Best Suited for C)&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// hash_nif.c&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;erl_nif.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;openssl/sha.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ERL_NIF_TERM&lt;/span&gt; &lt;span class="nf"&gt;sha256_nif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErlNifEnv&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ERL_NIF_TERM&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ErlNifBinary&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;enif_inspect_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enif_make_badarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SHA256_DIGEST_LENGTH&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;ErlNifBinary&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;enif_alloc_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SHA256_DIGEST_LENGTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SHA256_DIGEST_LENGTH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enif_make_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ErlNifFunc&lt;/span&gt; &lt;span class="n"&gt;nif_funcs&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"sha256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sha256_nif&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;ERL_NIF_INIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CryptoNif&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nif_funcs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Elixir Part:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;CryptoNif&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@on_load&lt;/span&gt; &lt;span class="ss"&gt;:load_nif&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;load_nif&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:erlang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_nif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"./hash_nif"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="s2"&gt;"NIF not loaded!"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compiling C Source:&lt;/strong&gt;&lt;br&gt;
You'll need the &lt;strong&gt;OpenSSL&lt;/strong&gt; library installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# For Ubuntu/Debian&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libssl-dev

&lt;span class="c"&gt;# For CentOS/RHEL&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;openssl-devel

&lt;span class="c"&gt;# For macOS (with Homebrew)&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;openssl
Compile the C &lt;span class="nb"&gt;source&lt;/span&gt;:


gcc &lt;span class="nt"&gt;-shared&lt;/span&gt; &lt;span class="nt"&gt;-fPIC&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; hash_nif.so hash_nif.c &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-I&lt;/span&gt; /usr/lib/erlang/erts-15.2.7/include &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# Path depends on your system&lt;/span&gt;
  &lt;span class="nt"&gt;-I&lt;/span&gt; /usr/include/openssl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-L&lt;/span&gt; /usr/lib/openssl &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-lssl&lt;/span&gt; &lt;span class="nt"&gt;-lcrypto&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;CryptoNif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;159&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;134&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;208&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;129&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;136&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;76&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;125&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;154&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;160&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;197&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;208&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;163&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;191&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;130&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;209&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;93&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;176&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Example 2: Port Driver for String Reversal&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// reverse_driver.c&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"erl_driver.h"&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ErlDrvPort&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;reverse_and_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErlDrvData&lt;/span&gt; &lt;span class="n"&gt;drv_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErlDrvSizeT&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;drv_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErlDrvSizeT&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;driver_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ErlDrvData&lt;/span&gt; &lt;span class="nf"&gt;driver_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErlDrvPort&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;driver_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErlDrvData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;driver_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErlDrvData&lt;/span&gt; &lt;span class="n"&gt;drv_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DriverData&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;drv_data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;driver_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ErlDrvEntry&lt;/span&gt; &lt;span class="n"&gt;driver_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;driver_stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reverse_and_send&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ready_input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ready_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"reverse_driver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;finish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outputv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ready_async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extended_marker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ERL_DRV_EXTENDED_MARKER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;major_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ERL_DRV_EXTENDED_MAJOR_VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minor_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ERL_DRV_EXTENDED_MINOR_VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process_exit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_select&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;span class="n"&gt;DRIVER_INIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse_driver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;driver_entry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Elixir Part:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;ReverseString&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:erl_ddll&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_driver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'reverse_driver'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:spawn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'reverse_driver'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binary&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;receive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;after&lt;/span&gt;
      &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:timeout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compiling C Source:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gnu99 &lt;span class="nt"&gt;-shared&lt;/span&gt; &lt;span class="nt"&gt;-fPIC&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; reverse_driver.so reverse_driver.c &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-I&lt;/span&gt;/usr/lib/erlang/erts-15.2.7/include/ &lt;span class="c"&gt;# Path depends on your system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ReverseString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;
&lt;span class="c1"&gt;#Port&amp;lt;0.6&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;ReverseString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="s2"&gt;"olleh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🦀 Rustler — Security and Performance with Rustler
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Optimal Method:&lt;/strong&gt; Rustler (specialized wrapper for NIF)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Rustler?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full memory safety&lt;/li&gt;
&lt;li&gt;Convenient macros for working with BEAM terms&lt;/li&gt;
&lt;li&gt;Automatic error handling&lt;/li&gt;
&lt;li&gt;Async task support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example: Parallel Data Processing&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Add &lt;code&gt;Rustler&lt;/code&gt; to mix.exs &lt;em&gt;(Current version is 0.36.1)&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:rustler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.36.1"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create Rust Project Inside Elixir:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mix deps.get
mix rustler.new

This is the name of the Elixir module the NIF module will be registered to.
Module name &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; RustUtils
This is the name used &lt;span class="k"&gt;for &lt;/span&gt;the generated Rust crate. The default is most likely fine.
Library name &lt;span class="o"&gt;(&lt;/span&gt;rustutils&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Enter&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rayon&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[rustler::nif]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parallel_double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;i64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="nf"&gt;.par_iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.map&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;rustler&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;init!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Elixir.RustUtils"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Elixir Part:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;RustUtils&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rustler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:rust_utils&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;crate:&lt;/span&gt; &lt;span class="s2"&gt;"rustutils"&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;parallel_double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_list&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="ss"&gt;:erlang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nif_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:not_loaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="no"&gt;RustUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parallel_double&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# =&amp;gt; [2, 4, 6]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. 🐍 Python — Effortless Integration Through Ports
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Optimal Method:&lt;/strong&gt; Ports&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Ports?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total process isolation&lt;/li&gt;
&lt;li&gt;Easy debugging&lt;/li&gt;
&lt;li&gt;Access to Python ecosystem&lt;/li&gt;
&lt;li&gt;Handles long operations (like ML models)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example: Creating Your Own Model and Integrating TensorFlow&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Build the Model:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# create_model.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tensorflow&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Sequential&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt; &lt;span class="n"&gt;use_bias&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_weights&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;]])])&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_model.h5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Model saved to my_model.h5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#tensorflow_port.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tensorflow&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TF_CPP_MIN_LOG_LEVEL&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_logger&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ERROR&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keras&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_model.h5&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;input_array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reshape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input_array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Elixir Part:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;TensorflowPort&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@timeout&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:spawn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"python3 tensorflow_port.py"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:binary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:use_stdio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:exit_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:stderr_to_stdout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;input_data&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Jason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Port&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;wait_for_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;wait_for_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;receive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:eol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;}}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="no"&gt;Jason&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"result"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wait_for_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:exit_status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Python process exited with status &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;after&lt;/span&gt;
      &lt;span class="nv"&gt;@timeout&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:timeout&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To run the script, enter the Python environment and launch &lt;code&gt;iex -S mix&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;venv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;TensorflowPort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;
&lt;span class="c1"&gt;#Port&amp;lt;0.10&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;TensorflowPort&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;]]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. 🦫 &lt;strong&gt;Go — Efficiency Through CGO or gRPC&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Optimal Methods:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CGO wrappers for NIF — best for max performance&lt;/li&gt;
&lt;li&gt;gRPC — suitable for complex interactions and microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CGO Wrapper for Go Code (Suitable for Performance)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// fib.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;GoFib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;C Wrapper:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// c_src/go_nif.c&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;erl_nif.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;"libfib.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ERL_NIF_TERM&lt;/span&gt; &lt;span class="nf"&gt;go_fib_nif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ErlNifEnv&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ERL_NIF_TERM&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;enif_get_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enif_make_badarg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;enif_make_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GoFib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ErlNifFunc&lt;/span&gt; &lt;span class="n"&gt;nif_funcs&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"go_fib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;go_fib_nif&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;ERL_NIF_INIT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NifGo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nif_funcs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Elixir part:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;NifGo&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@on_load&lt;/span&gt; &lt;span class="ss"&gt;:load_nif&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;load_nif&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;nif_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"priv/go_nif"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cwd!&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;# Preload Go library&lt;/span&gt;
    &lt;span class="ss"&gt;:erlang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_nif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"priv/libfib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cwd!&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Load main NIF library&lt;/span&gt;
    &lt;span class="ss"&gt;:erlang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load_nif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nif_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;go_fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_n&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"NIF not loaded!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compile:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go build &lt;span class="nt"&gt;-buildmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c-shared &lt;span class="nt"&gt;-o&lt;/span&gt; priv/libfib.so fib.go

gcc &lt;span class="nt"&gt;-shared&lt;/span&gt; &lt;span class="nt"&gt;-fPIC&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-I&lt;/span&gt;/usr/lib/erlang/erts-15.2.7/include/ &lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c"&gt;# Path varies based on your system&lt;/span&gt;
    &lt;span class="nt"&gt;-I&lt;/span&gt;./priv &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-o&lt;/span&gt; priv/go_nif.so &lt;span class="se"&gt;\&lt;/span&gt;
    c_src/go_nif.c &lt;span class="se"&gt;\&lt;/span&gt;
    ./priv/libfib.so &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-Wl&lt;/span&gt;,-rpath,&lt;span class="s1"&gt;'$ORIGIN'&lt;/span&gt;

&lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./priv iex &lt;span class="nt"&gt;-S&lt;/span&gt; mix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Run:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;NifGo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;go_fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ If you'd rather avoid dealing with &lt;strong&gt;CGO&lt;/strong&gt;, opt for &lt;strong&gt;gRPC&lt;/strong&gt; or &lt;strong&gt;Ports&lt;/strong&gt; instead. They're safer, although slower.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. 🐊 Zig — Integration Via Zigler&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Optimal Method:&lt;/strong&gt; &lt;strong&gt;Zigler&lt;/strong&gt; (wrapper for Zig-based NIFs)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Zigler?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimum overhead for native calls&lt;/li&gt;
&lt;li&gt;Memory safety without garbage collection&lt;/li&gt;
&lt;li&gt;Direct access to low-level operations&lt;/li&gt;
&lt;li&gt;Simplified compilation relative to other native extensions&lt;/li&gt;
&lt;li&gt;Hot-reloading capabilities&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Example: Quick Binary Data Handling&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Add &lt;code&gt;Zigler&lt;/code&gt; to &lt;strong&gt;mix.exs&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:zigler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 0.13.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;runtime:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;lib/nif_zig.ex:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;NifZig&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Zig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;otp_app:&lt;/span&gt; &lt;span class="ss"&gt;:zigler&lt;/span&gt;

  &lt;span class="sx"&gt;~Z""&lt;/span&gt;&lt;span class="s2"&gt;"
  pub fn string_count(string: []u8) i64 {
    return @intCast(string.len);
  }

  pub fn list_sum(array: []f64) f64 {
    var sum: f64 = 0.0;
    for(array) | item | {
      sum += item;
    }
    return sum;
  }
  """&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;NifZig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;NifZig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;list_sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;1.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;2.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;3.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;4.5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="mf"&gt;11.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Optimal Method&lt;/th&gt;
&lt;th&gt;Alternatives&lt;/th&gt;
&lt;th&gt;When To Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NIF, Port Drivers&lt;/td&gt;
&lt;td&gt;Ports&lt;/td&gt;
&lt;td&gt;Maximum performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rust&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rustler (NIF)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Safety + performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ports&lt;/td&gt;
&lt;td&gt;gRPC&lt;/td&gt;
&lt;td&gt;Integration with ML/scientific tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Go&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;C-обёртки (NIF), gRPC&lt;/td&gt;
&lt;td&gt;Ports&lt;/td&gt;
&lt;td&gt;Leveraging Go ecosystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zig&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zigler (NIF)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Modern alternative to C&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Integrating Elixir with native code unlocks new possibilities for developers, combining BEAM's strengths (scalability, reliability) with the performance and libraries of other languages. We've explored various integration techniques: &lt;strong&gt;NIF&lt;/strong&gt;, &lt;strong&gt;Dirty NIF&lt;/strong&gt;, &lt;strong&gt;Ports&lt;/strong&gt;, &lt;strong&gt;Port Drivers&lt;/strong&gt;, &lt;strong&gt;Rustler&lt;/strong&gt;, &lt;strong&gt;Zigler&lt;/strong&gt;, and &lt;strong&gt;gRPC&lt;/strong&gt;, along with their optimal usage scenarios.&lt;/p&gt;

&lt;p&gt;Remember, choosing an integration method should align with your project's requirements regarding performance, security, and ease of development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Author's Notes
&lt;/h2&gt;

&lt;p&gt;Thank you for reading this article! I hope you've found it insightful learning about ways to integrate other languages into Elixir. Among these options, I found Port Drivers particularly challenging, as crafting one turned out to be quite an adventure, requiring hours of trial-and-error to find the correct approach. Overall, however, integrating other languages into Elixir proved neither difficult nor boring—it was actually fun!&lt;/p&gt;

&lt;p&gt;If you spot inaccuracies or have interesting additions, please share them in the comments section. Constructive feedback is always appreciated 😎&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>nif</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
