DEV Community

Cover image for Ash: способы взаимодействия — AshJsonApi, AshGraphQL и AshTypescript
Artyom Molchanov
Artyom Molchanov

Posted on

Ash: способы взаимодействия — AshJsonApi, AshGraphQL и AshTypescript

Разработка стабильного продукта, способного держать высокие нагрузки на разных серверах, — задача сложная и ресурсозатратная. Программист не может сфокусироваться только на бизнес-логике: сначала нужно настроить базу данных, спроектировать серверные API и многое другое.

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

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

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




🙏 Благодарности

Хочу выразить глубокую признательность Заку Даниэлю и всем, кто отреагировал на мою предыдущую статью об Ash (Ash Framework: Знакомство) и поделился ею. Ваша поддержка дала огромный заряд энергии и мотивации и подтвердила, что сообщество Ash — потрясающее!


📖 Предисловие

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

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




🧐 Что предлагает экосистема Ash

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

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




😮 REST с AshJsonApi: новый подход

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

AshJsonApi предлагает альтернативу. Главное преимущество — вам больше не нужно предварительно писать спецификацию в YAML/JSON и генерировать из неё код. Для старта достаточно готового домена ресурсов и подключённого расширения AshJsonApi.

Создать новый проект с поддержкой этого инструмента можно прямо на главной странице портала Ash через конструктор. Если проект на Ash уже есть, добавить функциональность можно одной командой:

mix igniter.install ash_json_api
Enter fullscreen mode Exit fullscreen mode

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

Настройка ресурса для работы с JSON:API

Чтобы ресурсы стали доступны через API, подключите расширение AshJsonApi.Resource. Это позволит автоматически сгенерировать эндпоинты на основе определений домена. Добавьте AshJsonApi.Resource в список extensions и задайте имя ресурса в параметре type:

defmodule CourseHub.Courses.Lesson do
  use Ash.Resource, extensions: [AshJsonApi.Resource]

  # Активация генерации эндпоинтов
  json_api do
    type "lesson"
  end

  actions do
    defaults [:read, :create, :destroy, :update]

    action :list_lessons_allowed, :map do
      run fn _input, _ctx ->
        allowed_courses = %{"go" => "Golang", "elixir" => "Elixir", "rust" => "Rust"}
        {:ok, allowed_courses}
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Параметр type определяет имя ресурса в пространстве имён JSON:API. Он используется преимущественно для документации (например, в Swagger или Redoc) и позволяет группировать методы по тегам.

Далее опишите маршруты в домене, указав пути и связав их с конкретными действиями ресурса:

defmodule CourseHub.Courses do
  use Ash.Domain, extensions: [AshJsonApi.Domain]

  json_api do
    routes do
      base_route "/lessons", CourseHub.Courses.Lesson do
        index :read, description: "Получение списка лекций"
        get :read, primary?: true, description: "Получение одной лекции"
        post :create, description: "Создание лекции"
        patch :update, description: "Изменение лекции"
        delete :destroy, description: "Удаление лекции"

        # Путь относителен base_route, итоговый эндпоинт: /lessons/allowed
        route :get, "/allowed", :list_lessons_allowed,
          description: "Получение списка разрешённых лекций"
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

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

Ключевые достоинства AshJsonApi

Единый подход и предсказуемость

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

mix ash.patch.extend CourseHub.Courses.Lesson json_api
Enter fullscreen mode Exit fullscreen mode

Указав адрес ресурса, вы позволите AshJsonApi автоматически сгенерировать всю обвязку для работы с API.

Работа с данными: фильтрация, сортировка и связи

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

GET /courses/1?include=lessons&included_page[lessons][limit]=10&included_page[lessons][offset]=20
Enter fullscreen mode Exit fullscreen mode

Запрос возвращает информацию о курсе и подгружает связанные лекции с заданной пагинацией. Ответ приходит в формате application/vnd.api+json — это стандартизированная спецификация (подробнее на jsonapi.org), которая структурирует JSON для предсказуемой работы.

Пример ответа:

{
  "data": {
    "id": "1",
    "type": "course",
    "attributes": {
      "title": "My Course!"
    },
    "relationships": {
      "lessons": {
        "data": [
          {"id": "1", "type": "lessons"},
          {"id": "2", "type": "lessons"}
        ],
        "links": {
          "self": "/courses/1/relationships/lessons",
          "related": "/courses/1/lessons",
          "first": "/courses/1?include=lessons&included_page[lessons][limit]=10",
          "next": "/courses/1?include=lessons&included_page[lessons][limit]=10&included_page[lessons][offset]=10",
          "prev": null,
          "last": "/courses/1?include=lessons&included_page[lessons][limit]=10&included_page[lessons][offset]=40"
        },
        "meta": {
          "limit": 10,
          "offset": 0,
          "count": 50
        }
      }
    }
  },
  "included": [
    {
      "id": "1",
      "type": "lessons",
      "attributes": {
        "description": "Lessons about Elixir 1!"
      }
    },
    {
      "id": "2",
      "type": "lessons",
      "attributes": {
        "description": "Lessons about Elixir 2!"
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Заключение и личный опыт

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

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




🙂‍↕️ AshGraphQL

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

Что такое GraphQL

GraphQL — это язык запросов для API и рантайм для их исполнения. Клиент сам описывает, какие данные ему нужны, а сервер возвращает ровно эту форму. Контрактом служит строго типизированная схема.

Фундаментальные принципы

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

Виды операций

  • Query — операции на чтение (идемпотентны).
  • Mutation — операции на изменение данных (выполняются последовательно).
  • Subscription — подписка на поток событий в реальном времени (через WebSocket).

Схема и резолверы

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

Сложности

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

Где эффективен

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

Что представляет собой AshGraphQL

Несмотря на то что GraphQL пока уступает в популярности другим подходам, его интеграция с Ash сделана очень качественно. Технологию легко внедрить в проекты на базе Ash.

Для начала добавьте библиотеку в проект:

mix igniter.install ash_graphql
Enter fullscreen mode Exit fullscreen mode

Затем в модуле схемы укажите используемые домены:

use AshGraphql, domains: [Your.Domain1, Your.Domain2]
Enter fullscreen mode Exit fullscreen mode

Настройка ресурсов и доменов

Чтобы ресурс стал доступен через GraphQL, подключите расширение AshGraphql.Resource и опишите базовый блок конфигурации:

defmodule CourseHub.Courses.Lesson do
  use Ash.Resource,
    extensions: [
      AshGraphql.Resource
    ]

  graphql do
    type :lesson
  end

  # ... остальная логика ресурса
end
Enter fullscreen mode Exit fullscreen mode

В модуле домена определите операции (ручки) через расширение AshGraphql.Domain:

defmodule CourseHub.Courses do
  use Ash.Domain,
    extensions: [
      AshGraphql.Domain
    ]

  graphql do
    queries do
      get CourseHub.Courses.Lesson, :get_lesson, :read
      read_one CourseHub.Courses.Lesson, :most_popular_lesson, :most_popular
      list CourseHub.Courses.Lesson, :list_lessons, :read
    end

    mutations do
      create CourseHub.Courses.Lesson, :create_lesson, :create
      update CourseHub.Courses.Lesson, :update_lesson, :update
      destroy CourseHub.Courses.Lesson, :destroy_lesson, :destroy
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

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

Кроме того, вы можете создавать собственные действия для кастомных ответов. Пример:

graphql do
  type :lesson

  queries do
    action :random_lesson, :random_lesson
  end
end

actions do
  action :random_lesson, {:array, :struct} do
    constraints items: [instance_of: __MODULE__]

    run fn _input, _context ->
      # возвращаем список структур, как объявлено в типе действия
      Ash.read(__MODULE__)
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Генерация схемы SDL

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

use AshGraphql,
  domains: [Domain1, Domain2],
  generate_sdl_file: "priv/schema.graphql",
  auto_generate_sdl_file?: true
Enter fullscreen mode Exit fullscreen mode

Создание подписок (Subscriptions)

Можно настроить получение данных в реальном времени через механизм подписок. Для этого используется конфигурация Absinthe:

# в файле схемы Absinthe
subscription do
  field :field, :type_name do
    config(fn
      _args, %{context: %{current_user: %{id: user_id}}} ->
        {:ok, topic: user_id, context_id: "user/#{user_id}"}

      _args, _context ->
        {:error, :unauthorized}
    end)

    resolve(fn args, _, resolution ->
      AshGraphql.Subscription.query_for_subscription(
        YourResource,
        YourDomain,
        resolution
      )
      |> Ash.Query.filter(id == ^args.id)
      |> Ash.read(actor: resolution.context.current_user)
    end)
  end
end
Enter fullscreen mode Exit fullscreen mode

Мониторинг

Для глубокого погружения рекомендую официальное руководство по мониторингу Ash. Здесь кратко опишем механизмы трассировки и события телеметрии, которые становятся доступны с этим расширением.

Можно определить собственный трассировщик на уровне домена — его настройки будут иметь приоритет над глобальным параметром config :ash, :tracer:

graphql do
  tracer MyApp.Tracer
end
Enter fullscreen mode Exit fullscreen mode

Трассировка (Traces)

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

Телеметрия (Telemetry)

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

Список генерируемых событий:

  • [:ash, <имя_домена>, :gql_mutation] — выполнение мутации. Для детализации используйте метаданные resource_short_name и mutation (или action)
  • [:ash, <имя_домена>, :gql_query] — выполнение запроса. Для детализации используйте метаданные resource_short_name и query (или action)
  • [:ash, <имя_домена>, :gql_relationship] — разрешение связи. Для детализации используйте метаданные resource_short_name и relationship
  • [:ash, <имя_домена>, :gql_calculation] — разрешение вычисления. Для детализации используйте метаданные resource_short_name и calculation
  • [:ash, <имя_домена>, :gql_relationship_batch] — пакетное разрешение связей загрузчиком данных. Для детализации используйте метаданные resource_short_name и relationship
  • [:ash, <имя_домена>, :gql_calculation_batch] — пакетное разрешение вычислений загрузчиком данных. Для детализации используйте метаданные resource_short_name и calculation

Отзыв

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




😳 AshTypescript — скрытая жемчужина

Мы уже познакомились с AshJsonApi и AshGraphQL, и это был полезный опыт. Эти два подхода — своего рода индустриальный стандарт веб-разработки, и особых сюрпризов для опытных специалистов в них нет. Но у Ash есть «козырная карта» — AshTypescript.

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

Принцип работы

  1. Определение структуры. Вы описываете домен и ресурсы, указывая нужные действия (actions) и атрибуты
  2. Подключение расширений. В ресурс добавляется AshTypescript.Resource, а в домен — AshTypescript.Rpc
  3. Настройка ресурса. В блоке typescript внутри ресурса задаётся его имя для генерации
defmodule CourseHub.Courses.Lesson do
  use Ash.Resource,
    domain: CourseHub.Courses,
    extensions: [AshTypescript.Resource] # Подключаем расширение

  # Задаём имя типа для сгенерированного RPC-файла
  typescript do
    type_name "Lesson"
  end

  # Стандартные CRUD-действия
  actions do
    defaults [:read, :create, :update, :destroy]
  end

  # Описываем атрибуты ресурса
  attributes do
    uuid_primary_key :id
    attribute :title, :string, allow_nil?: false
    attribute :description, :string, default: "Какой-то текст!"
  end

  # Описываем связь с Course
  relationships do 
    belongs_to :course, CourseHub.Courses.Course
  end 
end
Enter fullscreen mode Exit fullscreen mode
  1. Привязка RPC-методов. В модуле домена вы определяете, какие методы будут доступны фронтенду, и связываете их с действиями ресурса.
defmodule CourseHub.Courses do
  use Ash.Domain, extensions: [AshTypescript.Rpc]

  # Публичные RPC-методы для фронтенда
  typescript_rpc do
    resource CourseHub.Courses.Lesson do
      rpc_action :list_lessons, :read
      rpc_action :create_lesson, :create
      rpc_action :get_lesson, :get
      rpc_action :delete_lesson, :destroy
      rpc_action :update_lesson, :update
    end
  end
end
Enter fullscreen mode Exit fullscreen mode
  1. Генерация кода. Запустив mix ash_typescript.codegen (или общую mix ash.codegen), вы получите сгенерированные файлы по пути из конфигурации. Настройки лежат в config/config.exs:
config :ash_typescript,
  ash_domains: [
    Backend.CourseHub
  ],
  output_file: "assets/js/ash_rpc.ts", # Путь для сохранения сгенерированного кода
  run_endpoint: "/rpc/run",            # Эндпоинт для отправки запросов
  validate_endpoint: "/rpc/validate",  # Эндпоинт для валидации данных по схеме
  input_field_formatter: :camel_case,  # Формат имён полей на входе
  output_field_formatter: :camel_case, # Формат имён полей на выходе
  require_tenant_parameters: false,    # Мультитенантность
  generate_zod_schemas: true,          # Генерация схем для Zod
  generate_phx_channel_rpc_actions: false,
  generate_validation_functions: true,
  zod_import_path: "zod",
  zod_schema_suffix: "ZodSchema",
  phoenix_import_path: "phoenix"
Enter fullscreen mode Exit fullscreen mode

Zod — библиотека для описания и проверки структуры данных. Она позволяет создавать схемы и автоматически валидировать по ним входящие данные.

Ключевые преимущества Zod:

  • Ориентированность на TypeScript — автоматически выводит типы
  • Интуитивно понятный и легко читаемый API
  • Поддержка сложных правил валидации (объединения типов, пересечения, уточнения)
  • Высокая производительность и минимальный вес

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

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

Механизмы сортировки, фильтрации и проекции полей

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

// CREATE
const createLessonResponse = await createLesson({
  fields: ["id", "title"],
  input: { title: "Elixir lesson about Ash", priority: "very high!" },
  headers
});

if (!createLessonResponse.success) {
  console.error("Create failed:", createLessonResponse.errors);
  return;
}

const lessonId = createLessonResponse.data.id;

// READ (single)
const getLesson = await getLesson({
  fields: ["id", "title", "description", { course: ["name"] }],
  input: { id: lessonId },
  headers
});

// READ (list)
const lessons = await listLessons({
  fields: ["id", "title"],
  headers
});

// UPDATE
const updateLessonResult = await updateLessson({
  fields: ["id", "title"],
  identity: lessonId,
  input: { title: "Yoo chill im just a vessel!" },
  headers
});

// DELETE
const deleteLessonResponse = await deleteLesson({
  identity: lessonId,
  headers
});
Enter fullscreen mode Exit fullscreen mode

Преимущества использования AshTypescript

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

Впечатления

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




🧮 Сравнение трёх подходов

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

Критерий AshJsonApi (REST / JSON:API) AshGraphQL AshTypescript
Тип контракта REST по стандарту JSON:API GraphQL-схема (SDL) Сгенерированные .ts-файлы (RPC)
Источник истины ваши actions + автоген OpenAPI ваши actions + автоген SDL ваши actions + автоген TypeScript
Как клиент задаёт запрос query-параметры (filter, sort, include) GraphQL-запрос с деревом полей типизированные вызовы (fields, filter, sort)
Гибкость выборки высокая максимальная высокая
Индустриальный стандарт да, повсеместно да, но нишевый нет, специфичен для стека Ash + TS
Типобезопасность на фронте через кодоген из OpenAPI через кодоген из схемы нативная, из коробки
Реалтайм нет из коробки да (subscriptions) да (Phoenix channels)
HTTP-кэширование работает (GET) затруднено (POST) зависит от реализации
Порог входа на фронте низкий средний/высокий низкий (для TypeScript-команд)
Идеально для публичные и внешние API много разных клиентов со сложными запросами full-stack Elixir + TypeScript

Как выбирать на практике:

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

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




😴 Заключение

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




🥴 От автора

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

Если эта статья пришлась вам по душе и хочется ещё материалов такого плана, присоединяйтесь ко мне в моём телеграм-канале, где выкладываю обзоры книг, публикации по Elixir, переводы технической литературы и интересные новости!

Top comments (0)