Ash Framework

repository·main·Indexed 25 days ago

https://github.com/ash-project/ash

A declarative framework for building scalable, secure, and maintainable applications in Elixir. Ash allows developers to model business logic through Resources, Actions, and Relationships, separating intent from implementation. It features a robust ecosystem including data layers (AshPostgres, AshSqlite, AshCsv, AshCubdb), API builders (AshJsonApi, AshGraphql), and utilities for authentication, background jobs, and state management.

Tokens
133.1K
Snippets
394
Records
611
Agent score
81%

What's inside Ash

  1. Explore the Ash ecosystem and packages

    main

    Ash is highly extensible through a variety of specialized packages. Depending on your requirements, you may need to install additional packages for:

    Data Layers

    Connect Ash to different storage engines:

    • AshPostgres: PostgreSQL
    • AshSqlite: SQLite
    • AshCsv: CSV files
    • AshCubdb: CubDB

    API & Web

    Expose your resources via web interfaces:

    • AshJsonApi: JSON:API builder
    • AshGraphql: GraphQL builder
    • AshPhoenix: Phoenix integrations
    • AshAuthentication: User authentication (Password, OAuth, etc.)

    Utilities & Advanced Features

    • Background Jobs: AshOban (backed by Oban)
    • Auditing & History: AshPaperTrail (history) and AshArchival (archiving)
    • State Management: AshStateMachine
    • Security: AshCloak (attribute encryption)
    • Admin UI: AshAdmin (push-button admin interface)
    • Testing: Smokestack (declarative test factories)
  2. Explore Ash documentation types

    main

    Ash documentation is organized into three distinct categories to help you find information based on your current goal:

    1. Topics: Understanding-oriented documentation. Use these to discover design patterns, core features, and how different parts of Ash (like Resources, Actions, or Security) work together.
    2. How-to Guides: Goal-oriented recipes. Use these when you have a specific task to accomplish (e.g., 'Write Queries', 'Encrypt Attributes', or 'Wrap External APIs').
    3. Reference: Information-oriented documentation. This is automatically generated from source code and covers every Ash module, function, expression, and DSL.

    Tip: When using the Hexdocs search bar, prefix your search with dsl (e.g., dsl name) to jump directly to DSL-specific reference information.

  3. Overview of Ash core concepts

    main

    Ash is built around several key abstractions that you will use to model your application logic:

    Resources

    Resources are the heart of Ash. They define the data and logic for a specific domain. Key components include:

    • Attributes: The data fields of a resource.
    • Relationships: How resources connect to one another.
    • Calculations & Aggregates: Derived data and summary values.
    • Identities & Validations: Rules for data integrity.
    • Changes & Preparations: Logic applied during the lifecycle of an action.
    • Embedded Resources: Resources contained within other resources.

    Actions

    Actions define how you interact with resources. They are categorized as:

    • Read Actions: Fetching data.
    • Create Actions: Adding new data.
    • Update Actions: Modifying existing data.
    • Destroy Actions: Removing data.
    • Generic & Manual Actions: Custom logic that doesn't fit standard CRUD patterns.

    Security

    Ash provides built-in mechanisms for:

    • Actors & Authorization: Defining who can perform which actions.
    • Policies: Declarative rules for access control.
    • Sensitive Data: Managing data privacy.
  4. What is a Fully Atomic update?

    main

    A fully atomic update is an action where every part of the process (changes, validations, etc.) is performed in a single operation in the data layer. This prevents race conditions in concurrent environments.

    Non-atomic example (Unsafe): Using an anonymous function that reads the current value in memory and then sets a new one. If two processes do this simultaneously, one update will be lost.

    update :increment_score do
      change fn changeset, _ ->
        Ash.Changeset.change_attribute(changeset, :score, changeset.data.score + 1)
      end
    end

    Atomic example (Safe): Using atomic_update to tell the data layer to increment the value directly.

    update :increment_score do
      change atomic_update(:score, expr(score + 1))
    end
  5. Understand policy evaluation logic (AND vs OR)

    main

    Policies follow a specific evaluation flow:

    1. All applicable policies must pass.
    2. Within a policy, checks are evaluated from top to bottom.
    3. The first check that produces a decision determines the result.

    Implementing AND logic

    Because the first successful check stops evaluation, using multiple authorize_if statements creates OR logic. To require BOTH conditions (AND logic), use one of these patterns:

    1. Use forbid_unless for the first requirement:
      policy action_type(:update) do
        forbid_unless actor_attribute_equals(:admin?, true)
        authorize_if relates_to_actor_via(:owner)
      end
    2. Use a single complex expression: authorize_if expr(condition1 and condition2)
    3. Use multiple separate policies: Each policy must pass independently.
  6. How Ash extensions work

    main

    An Ash extension is typically composed of one or more Spark.Dsl.Extension modules and additional supporting code. Extensions allow you to modify the DSL (Domain Specific Language) of entities like Resources or Domains. For example, AshGraphql provides both a domain extension (AshGraphql.Domain) and a resource extension (AshGraphql.Resource).

    To create a basic extension, you define a module that uses Spark.Dsl.Extension and specifies a list of transformers.

    defmodule MyApp.Extensions.Base do
      use Spark.Dsl.Extension, transformers: [MyApp.Extensions.Base.AddTimestamps]
    end
  7. Understand Aggregates and Calculations

    main

    Both Aggregates and Calculations are special types of fields that are not necessarily stored directly in the data layer, but they serve different purposes:

    • Aggregate: A specialized type of calculation used to summarize related information from associated records (e.g., counting the number of Ticket resources associated with a Project).
    • Calculation: A field that is generated on-demand. It derives its value from other information on the record or from external data sources.
  8. Understand Public vs Private Attributes and Relationships

    main

    In Ash, attributes, calculations, aggregates, and relationships are private by default (public?: false).

    • Internal Code: When using core Ash functions like Ash.read/2, the public/private status does not affect visibility; you can read any attribute.
    • API Extensions: When using extensions like AshGraphql or AshJsonApi, only fields explicitly marked as public?: true will be included in the generated interfaces. This prevents accidental data exposure through external APIs.
  9. Understand the Actor concept

    main

    An Actor is the entity that performs an action.

    Actors are typically used for auditing or for writing policies to control authorization. While an actor can be any value (like a map), it is highly recommended to use a struct to represent the entity performing the action.

    Common examples of actor types include:

    • %MyApp.Accounts.User{}
    • %MyApp.Accounts.Device{}
    • %MyApp.SystemUser{}