JSON → Elixir Ecto
Elixir & Phoenix Code Generation

JSON to Elixir Ecto Schema & Changeset Generator

Generate Elixir Ecto schemas, embedded_schemas, and cast/validate changesets directly from JSON payload samples.

100% Client-Side Privacy
Zero Server Transmission
Presets:
Ecto Architecture & Compiler Options
Input JSON Payload
defmodule MyApp.Orders.Order do
  @moduledoc """
  Ecto schema for `orders`.
  Generated automatically with ToolsAA 100% Client-Side Engine.
  """
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  @derive {Jason.Encoder, only: [:id, :order_id, :order_number, :customer, :shipping_address, :items, :subtotal, :tax_amount, :total_amount, :currency, :is_fulfilled, :payment_status, :notes, :placed_at]}

  @type t :: %__MODULE__{}

  schema "orders" do
    field :order_id, :binary_id
    field :order_number, :string
    embeds_one :customer, MyApp.Orders.Order.Customer, on_replace: :update
    embeds_one :shipping_address, MyApp.Orders.Order.ShippingAddress, on_replace: :update
    embeds_many :items, MyApp.Orders.Order.Item, on_replace: :delete
    field :subtotal, :float
    field :tax_amount, :float
    field :total_amount, :float
    field :currency, :string
    field :is_fulfilled, :boolean
    field :payment_status, :string
    field :notes, :string
    field :placed_at, :utc_datetime

    timestamps(type: :utc_datetime)
  end

  @required_fields [:order_id, :order_number, :subtotal, :tax_amount, :total_amount, :currency, :payment_status, :placed_at]
  @optional_fields [:is_fulfilled, :notes]

  @doc false
  def changeset(model, attrs) do
    model
    |> cast(attrs, @required_fields ++ @optional_fields)
    |> validate_required(@required_fields)
    |> validate_format(:order_id, ~r/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, message: "must be a valid UUID")
    |> validate_number(:subtotal, greater_than_or_equal_to: 0)
    |> validate_number(:tax_amount, greater_than_or_equal_to: 0)
    |> validate_number(:total_amount, greater_than_or_equal_to: 0)
    |> cast_embed(:customer, required: true)
    |> cast_embed(:shipping_address, required: true)
    |> cast_embed(:items)
  end
end

defmodule MyApp.Orders.Order.Customer do
  @moduledoc """
  Embedded schema representing `MyApp.Orders.Order.Customer`.
  """
  use Ecto.Schema
  import Ecto.Changeset

  @derive {Jason.Encoder, only: [:customer_id, :full_name, :email, :phone_number, :is_vip]}
  @primary_key false
  embedded_schema do
    field :customer_id, :string
    field :full_name, :string
    field :email, :string
    field :phone_number, :string
    field :is_vip, :boolean
  end

  @doc false
  def changeset(struct_or_changeset, attrs) do
    struct_or_changeset
    |> cast(attrs, [:customer_id, :full_name, :email, :phone_number, :is_vip])
    |> validate_required([:customer_id, :full_name, :email, :phone_number])
    |> validate_format(:email, ~r/^[^s]+@[^s]+$/, message: "must have the @ sign and no spaces")
  end
end

defmodule MyApp.Orders.Order.ShippingAddress do
  @moduledoc """
  Embedded schema representing `MyApp.Orders.Order.ShippingAddress`.
  """
  use Ecto.Schema
  import Ecto.Changeset

  @derive {Jason.Encoder, only: [:street, :city, :state, :postal_code, :country_code]}
  @primary_key false
  embedded_schema do
    field :street, :string
    field :city, :string
    field :state, :string
    field :postal_code, :string
    field :country_code, :string
  end

  @doc false
  def changeset(struct_or_changeset, attrs) do
    struct_or_changeset
    |> cast(attrs, [:street, :city, :state, :postal_code, :country_code])
    |> validate_required([:street, :city, :state, :postal_code, :country_code])
  end
end

defmodule MyApp.Orders.Order.Item do
  @moduledoc """
  Embedded schema representing `MyApp.Orders.Order.Item`.
  """
  use Ecto.Schema
  import Ecto.Changeset

  @derive {Jason.Encoder, only: [:sku, :title, :quantity, :unit_price, :discount_rate, :in_stock]}
  @primary_key false
  embedded_schema do
    field :sku, :string
    field :title, :string
    field :quantity, :integer
    field :unit_price, :float
    field :discount_rate, :float
    field :in_stock, :boolean
  end

  @doc false
  def changeset(struct_or_changeset, attrs) do
    struct_or_changeset
    |> cast(attrs, [:sku, :title, :quantity, :unit_price, :discount_rate, :in_stock])
    |> validate_required([:sku, :title, :quantity, :unit_price, :discount_rate])
    |> validate_number(:quantity, greater_than_or_equal_to: 0)
    |> validate_number(:unit_price, greater_than_or_equal_to: 0)
    |> validate_number(:discount_rate, greater_than_or_equal_to: 0)
  end
end
Ecto Types Detected
13 Attributes
PK: binary_id • Timestamps: utc_datetime
Changeset Cast & Embeds
3 Embedded Schemas
Strategy: embeds
Database DDL Status
table(:orders)
PostgreSQL & MySQL Migration Ready

100% Client-Side Private Generator

Zero server uploads or telemetry. Your JSON payloads, internal schemas, and proprietary data structures are processed strictly inside your browser memory.

Idiomatic Phoenix & Ecto Architecture

Generates standard Ecto 3.x schemas with @derive {Jason.Encoder}, embedded_schema, cast/validate changesets, custom regex constraints, and timestamps.

Complete Stack Generation

Instant generation of Ecto schemas, embedded schemas, database migrations, Dialyzer @type specs, and standard Phoenix Context CRUD query functions.

Technical Reference & Architecture Guide

Json To Elixir Ecto — Reference Guide

Architectural guide and configuration reference for compiling raw JSON payloads into production-grade Elixir Ecto schemas, migrations, changesets, and Dialyzer typespecs.

Key Parameters & Controls

schemaMode'table' | 'embedded'

Switches between persistent relational table schemas via Ecto.Schema and stateless struct-based schemas via embedded_schema.

Default: 'table'
primaryKey'binary_id' | 'id' | 'none'

Configures the root entity primary key type, defining @primary_key {:id, :binary_id, autogenerate: true} for UUIDs, auto-incrementing integers, or disabled keys.

Default: 'binary_id'
nestedStrategy'embeds' | 'map'

Controls whether nested JSON objects are decomposed into recursive embeds_one/embeds_many sub-schemas or stored as schemaless :map attributes.

Default: 'embeds'
timestamps'utc_datetime' | 'naive_datetime' | 'none'

Injects Ecto timestamp macro invocations configured for microsecond UTC precision, legacy naive datetimes, or excludes timestamps entirely.

Default: 'utc_datetime'
smartValidationsboolean

Inspects sample field values to append validate_format/3 (emails, UUIDs, URLs) and validate_number/3 checks directly into generated changeset pipelines.

Default: true
deriveJasonboolean

Adds @derive {Jason.Encoder, only: [...]} above schema definitions to permit deterministic JSON encoding in Phoenix views and API responses.

Default: true

Pro Tips & Best Practices

Enforce UTC Datetime Precision

Always configure :utc_datetime over :naive_datetime when dealing with JSON API payloads to prevent silent timezone offsets and ensure zero-loss compatibility with PostgreSQL timestamptz columns.

Decouple Nested Embed Changesets

When using the embeds strategy, isolate nested changeset functions in separate modules or private functions passed to cast_embed/3 with the :with option to avoid bloated root changesets.

Isolate Jason Serialization Keys

Always populate the explicit :only list in @derive Jason.Encoder instead of serializing entire schema structs, preventing unintended leakage of internal virtual fields or sensitive user data.

Common Gotchas & Troubleshooting

Changeset error 'is invalid' when casting null on embedded objects

Ecto rejects null JSON values for mandatory embedded schemas. Set required: false in cast_embed/3 or define a default struct value in the parent schema to gracefully handle null API payloads.

PostgreSQL migration failure with mismatched foreign key types

When generating binary_id primary keys, verify that migration references specify type: :binary_id (e.g., references(:users, type: :binary_id)) to avoid PostgreSQL 42804 type mismatch errors.

Frequently Asked Questions

Does this utility transmit payload JSON to external servers for parsing?▼

No. All JSON parsing, schema inference algorithms, and Elixir AST generation execute entirely client-side within the browser JavaScript runtime. Sensitive production datasets and tokens never touch external network endpoints.

How does the generator distinguish between string, date, and utc_datetime types?▼

Field values are evaluated against strict RFC 3339 / ISO-8601 regular expressions. Strings containing full timestamp offsets resolve to :utc_datetime, calendar dates resolve to :date, and arbitrary text defaults to the standard :string primitive.

How are heterogeneous JSON arrays with mixed primitive types handled?▼

Because Ecto schemas enforce strictly typed array primitives ({:array, :type}), heterogeneous lists fall back to {:array, :map} or an unconstrained :map type to maintain compilation safety under Dialyzer without runtime type errors.

Why are separate migration scripts generated alongside the Ecto schemas?▼

Ecto separates compile-time Elixir application structs (Ecto.Schema) from database DDL migrations (Ecto.Migration). Generating both simultaneously ensures column types, index definitions, and nullability constraints match across your database and domain layers.

Related Tools & Utilities

All Tools →