<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="385220" data-post-id="385220">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="phcurado" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/phcurado/120/39030_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  phcurado
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>thanks <a class="mention" href="/u/pjode" rel="nofollow">@pjode</a></p>
<p>I’m using in almost anything that requires external input validation</p>
<h3><a name="p-385220-phoenix-controllers-1" class="anchor" href="#p-385220-phoenix-controllers-1" aria-label="Heading link" rel="nofollow"></a>Phoenix controllers</h3>
<p>I use on phoenix controllers to have user input validation and OpenAPI generation, you can find a simple example <a href="https://hexdocs.pm/zoi/using_zoi_to_generate_openapi_specs.html" rel="noopener nofollow ugc">here</a><br>
Most applications will be fine with just <code>Ecto</code> changesets, it have great validation capabilities and a lot of types.<br>
Some applications may have APIs that have different shape compared to their data on DB. You can use <code>Ecto</code> virutal fields or different schemas for the API. I find it easier to maintain <code>Zoi</code> schemas compared to maintaining <code>Ecto</code> schemas + changesets.</p>
<h3><a name="p-385220-parse-env-vars-2" class="anchor" href="#p-385220-parse-env-vars-2" aria-label="Heading link" rel="nofollow"></a>Parse env vars</h3>
<p>Since environment variables can come in different types and shapes, I use <code>Zoi</code> to parse it:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir"># runtime.exs
config, :my_app,
    auth_enabled?: Zoi.string_boolean() |&gt; Zoi.parse!(System.get_env("AUTH_ENABLED"))
</code></pre>
<p>And since the example above is a bit verbose I created a small helper in my applications:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">config, :my_app,
    auth_enabled?: Env.parse!(Zoi.string_boolean(), "AUTH_ENABLED")
</code></pre>
<p>This keeps your configuration consistent, so the <code>runtime.exs</code> and <code>config.exs</code> will have same types.<br>
If you don’t parse these vars, the key <code>auth_enabled?</code> will be boolean in <code>config.exs</code> and string in <code>runtime.exs</code>. This can potentially break your application.</p>
<h3><a name="p-385220-nimbleoptions-alternative-3" class="anchor" href="#p-385220-nimbleoptions-alternative-3" aria-label="Heading link" rel="nofollow"></a>NimbleOptions alternative</h3>
<p><a href="https://github.com/dashbitco/nimble_options" rel="noopener nofollow ugc">NimbleOptions</a> is very good to validate keywords and their inner data. <code>Zoi.keyword/2</code> is an alternative that implements a similar feature set. There is a small example on the docs <a href="https://hexdocs.pm/zoi/Zoi.Describe.html" rel="noopener nofollow ugc">here</a>, which uses <code>Zoi</code> to validate options, generate it’s typespec and documentation</p>
<h3><a name="p-385220-integration-with-external-apis-4" class="anchor" href="#p-385220-integration-with-external-apis-4" aria-label="Heading link" rel="nofollow"></a>Integration with external APIs</h3>
<p>I mainly work with 3rd party API integrations, specifically in the payments field. The payment’s providers doesn’t have much consistency on how they return the data in their APIs. For every integration, it’s good to parse the data to a format and shape that your internal application understands it.<br>
Also integrating with those APIs, you may find a lot of edge cases such as fields that may have more than one data types. <code>Zoi</code> <a href="https://hexdocs.pm/zoi/Zoi.html#union/2" rel="noopener nofollow ugc">unions</a> are a great solution for that.</p>
<h3><a name="p-385220-ai-5" class="anchor" href="#p-385220-ai-5" aria-label="Heading link" rel="nofollow"></a>AI</h3>
<p><a class="mention" href="/u/mikehostetler" rel="nofollow">@mikehostetler</a> and the whole <a href="https://github.com/agentjido/jido" rel="noopener nofollow ugc">Jido</a> ecosystem are using <code>Zoi</code> for multiple purposes:</p>
<ul>
<li>Structured outputs - Zoi schemas with OpenAPI generation</li>
<li><a href="https://hexdocs.pm/zoi/Zoi.Struct.html" rel="noopener nofollow ugc">Zoi Struct</a> as replacement for <a href="https://github.com/ejpcmac/typed_struct" rel="noopener nofollow ugc">typed_struct</a></li>
<li><code>NimbleOptions</code> alternative</li>
</ul>
<p>And probably even more.</p>
<hr>
<p>Last year I created a blog post explaining my motivation on building this lib, you can check it <a href="https://paulocurado.com/blog/zoi-a-schema-validation-library-for-elixir-inspired-by-zod/" rel="noopener nofollow ugc">here</a></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385220" data-batch-url="/posts/batch_likers">
                        5
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/32">Post #31</a>
	                </div>
	            </div>
              <div id="likers-container-385220" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385220"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #31"></div>
  </section>
</div>
    <div class="postbit" id="385227" data-post-id="385227">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="mikehostetler" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mikehostetler/120/20620_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  mikehostetler
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’ve wholesale replaced almost every implementation of <code>defstruct</code> with zoi across all of Jido at this point - it’s been very very helpful.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385227" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/33">Post #32</a>
	                </div>
	            </div>
              <div id="likers-container-385227" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385227"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #32"></div>
  </section>
</div>
    <div class="postbit" id="385962" data-post-id="385962">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="dimitarvp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/120/38664_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  dimitarvp
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="phcurado" data-post="20" data-topic="72108">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/phcurado/48/39030_2.png" class="avatar"> phcurado:</div>
<blockquote>
<p>Ecto also offers more specialized functions for your database operations, this would not be something I would add on Zoi. I assume you mean integrating Zoi schemas with Ecto schemas (not changesets), to have a unified definition across both, that is interesting, but not a current goal.</p>
</blockquote>
</aside>
<p>This is true but for parsing/validating on the edges of the code (to make sure we are passing the right arguments to our internal functions) embedded <code>Ecto.Schema</code>s are genuinely useful and I’d want a Zoi integration in those cases. Is it still not a priority?</p>
<p>Ideally I’m looking for something that does more than f.ex. <a href="https://hexdocs.pm/typed_struct_ecto_changeset/TypedStructEctoChangeset.html" class="inline-onebox" rel="noopener nofollow ugc">TypedStructEctoChangeset — typed_struct_ecto_changeset v1.1.0</a> but also have validation, coercion logic (i.e. automatically parse ISO8601 dates when the right type is given), and maybe some conditional validation too (“if <code>:product_type</code> is X then fields A, B and C are mandatory; if it’s Y then only C is mandatory but also D and E with defaults”).</p>
<p>My only blocker for adopting this beautiful library is that I’d have to maintain two parallel validation systems: Zoi for f.ex. API inputs and Ecto for DB. Ideally I’d have Zoi do its magic and just generate a changeset (or integrate with an existing <code>Ecto.Schema</code>, embedded or not).</p>
<p>Zoi can do more than <code>Ecto.Changeset</code>. Converting to it is a lossy compression and is doable IMO.</p>
<p>But again: have you thought about this lately? It’s a genuine use case and a blocker for people. We don’t want to use two validation systems in our codebases.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385962" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/34">Post #33</a>
	                </div>
	            </div>
              <div id="likers-container-385962" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385962"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #33"></div>
  </section>
</div>
    <div class="postbit" id="385963" data-post-id="385963">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="dimitarvp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/120/38664_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  dimitarvp
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>btw I <em>really</em> like <code>Zoi.Struct</code> and will start using it in other projects. Amazing addition to Zoi.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385963" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/35">Post #34</a>
	                </div>
	            </div>
              <div id="likers-container-385963" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385963"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #34"></div>
  </section>
</div>
    <div class="postbit" id="385968" data-post-id="385968">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="phcurado" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/phcurado/120/39030_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  phcurado
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="dimitarvp" data-post="34" data-topic="72108">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>This is true but for parsing/validating on the edges of the code (to make sure we are passing the right arguments to our internal functions) embedded <code>Ecto.Schema</code>s are genuinely useful and I’d want a Zoi integration in those cases. Is it still not a priority?</p>
</blockquote>
</aside>
<p>It was never a priority because, at least from this thread and the issues on github, I never got any requests for this feature (besides the one you mentioned but it never became a discussion here).<br>
As for myself, I usually like to separate DB operations and changesets, limiting to context/schema logic and having my API like a different domain, since API contracts and DB schemas often evolve independently.</p>
<p>But let me understand your use case better, like how exactly do you want to use Zoi with changeset? Because if you are already using embedded schemas it will be repetitive since you declare both embedded + zoi schema (unless you want to unify the schema and changeset in a single api like the typed struct lib you shared).</p>
<aside class="quote no-group" data-username="dimitarvp" data-post="34" data-topic="72108">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>My only blocker for adopting this beautiful library is that I’d have to maintain two parallel validation systems: Zoi for f.ex. API inputs and Ecto for DB. Ideally I’d have Zoi do its magic and just generate a changeset (or integrate with an existing <code>Ecto.Schema</code>, embedded or not).</p>
</blockquote>
</aside>
<p>So having two separate schemas are fine? the only problem is having two validations systems?</p>
<aside class="quote no-group" data-username="dimitarvp" data-post="34" data-topic="72108">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/48/38664_2.png" class="avatar"> dimitarvp:</div>
<blockquote>
<p>Zoi can do more than <code>Ecto.Changeset</code>. Converting to it is a lossy compression and is doable IMO.</p>
</blockquote>
</aside>
<p>Thats one of the problems in converting zoi to changeset, we would lose a lot of zoi validations and types since we will be limited by the changeset api.</p>
<p>Let’s discuss, this can be added as long as I can understand better your use case or how exactly you think translating zoi schemas should work with changesets.</p>
<p>I imagine you want something like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule User do
  use Ecto.Schema

  schema "users" do
    field :name, :string
    field :email, :string
    field :age, :integer
    field :product_type, :string
  end

  @zoi Zoi.struct(__MODULE__, %{
         name: Zoi.string(),
         email: Zoi.email(),
         age: Zoi.integer(gte: 0) |&gt; Zoi.optional(),
         product_type: Zoi.enum(["X", "Y"])
       })

  def changeset(user, params) do
    # This would first parse with Zoi, generate Zoi errors (if any) and convert to changeset-like
    Zoi.Ecto.parse_to_changeset(@zoi, user, params)
  end
end
</code></pre>
<p>but I’m not 100% sure</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385968" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/36">Post #35</a>
	                </div>
	            </div>
              <div id="likers-container-385968" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385968"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #35"></div>
  </section>
</div>
    <div class="postbit" id="385979" data-post-id="385979">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="cmo" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/cmo/120/19618_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  cmo
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I think he just wants to write the zoi part and have it generate the <code>schema</code> part, like typed struct does.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385979" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/37">Post #36</a>
	                </div>
	            </div>
              <div id="likers-container-385979" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385979"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #36"></div>
  </section>
</div>
    <div class="postbit" id="385982" data-post-id="385982">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="dimitarvp" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/dimitarvp/120/38664_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  dimitarvp
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Generally I want what <a class="mention" href="/u/cmo" rel="nofollow">@cmo</a> says but even more than that: I want the full validation + coercion pipeline (again, declare something as an ISO8601 date but it’s a string and the library parses it; that is what I mean by “coercion”) to produce Ecto-compatible output.</p>
<p>Your example has both a Zoi schema and an Ecto schema – this is what I want to avoid, otherwise I wouldn’t seek to introduce another library. My fantasy is to define the schema once with Zoi and have it generate the <code>embedded_schema</code> <em>and</em> produce changesets as an output. I want Zoi to be the source of truth for fields, types, coercion rules, and validation (I love the discriminated unions and I need them!).</p>
<p>The idea is that what we might call “the Ecto layer” should just be the output format so downstream code like Absinthe / Phoenix forms / <code>cast_embed</code> works unchanged.</p>
<p>Or maybe even better refined: Zoi owns the “what” and the “how to validate” and <code>Ecto.Changeset</code> is just the error container and the struct container (so stuff like <code>apply_changes</code> should still work on a valid changeset). The lossy compression from Zoi to <code>Ecto.Changeset</code> is IMO fine for errors – I just need the field-level error maps and not Zoi’s full details (which I love but they are not needed for Absinthe / Phoenix forms / <code>cast_embed</code>).</p>
<p>Just lately I am fighting with user onboarding in a very Saga-pattern system. For context, my entry points – GraphQL queries/mutations, SQS consumers, REST endpoints – all accept the same conceptual data but currently each of them reinvents the same wheel (i.e. its own validation). I want one schema definition they would all share. Technically I can use <code>Ecto.Changeset</code> + a few more code snippets on top but I want to try use Zoi.</p>
<p>And f.ex. this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Project.Inputs.OnboardUser do
  use Zoi.EctoSchema  # generates embedded_schema + changeset from Zoi

  zoi_schema do
    field :email, Zoi.email()
    field :product_type, Zoi.enum([:credit_card, :debit_card]), default: :credit_card
    field :origination_date, Zoi.ISO.date() |&gt; Zoi.ISO.to_date_struct()

    variant :product_type do
      :credit_card -&gt; require([:partner_account_number, :initial_balance, :origination_date])
      :debit_card -&gt; require([:last_four])
    end
  end

  # Generates: embedded_schema, parse/1 -&gt; {:ok, %__MODULE__{}} | {:error, %Ecto.Changeset{}}
end
</code></pre>
<p>…would be helpful.</p>
<p>Does that make sense? I haven’t spent much time trying to prototype my ideal solution yet. We could chat further to try and arrive at a design for this integration.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385982" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/38">Post #37</a>
	                </div>
	            </div>
              <div id="likers-container-385982" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385982"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #37"></div>
  </section>
</div>
    <div class="postbit" id="385998" data-post-id="385998">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="phcurado" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/phcurado/120/39030_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  phcurado
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>thanks for the explanation, now it makes more sense to me. I agree that would be nice to define your schema once and you can derive it to different formats such as embedded schemas, absinthe, etc.<br>
This is not very common in other languages, from what I have seem it’s common to use different libraries for these different intents but it would be possible to centralize a lot of it in Zoi indeed.</p>
<p>I think your example decentralizes the schema definition. Because now we have a way to define schemas in the “zoi” way, like <code>Zoi.map(%{first_name: Zoi.string()})</code> and the other way you suggested using <code>zoi_schema</code> macro. The validation under the hood may be the same but now we are giving multiple options to define the schema and using the macro-like api will limit our api surface (or at least make it more complex compared to just plain functions). Also if we want to extend and offer absinthe interface as well, we will need another abstraction.</p>
<p>I would say we could centralize the definitions using plain <code>Zoi</code> schemas:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Project.Inputs.OnboardUser do
   require Zoi.Ecto

  @schema Zoi.map(%{email: Zoi.email()})

  # Macro that will create the embedded in this module
  Zoi.Ecto.generate_embedded_schema(@schema)

  def schema, do: @schema
end
</code></pre>
<p>And then we would have helpers in <code>Zoi.Ecto</code> that can convert parsed zoi results into changesets and so on.</p>
<p>This way you still defines your schema in a single way, and can use everywhere since with Zoi you can use yoru schema to create <a href="https://hexdocs.pm/zoi/Zoi.JSONSchema.html" rel="noopener nofollow ugc">JSONSchema</a>, <a href="https://hexdocs.pm/zoi/Zoi.Describe.html" rel="noopener nofollow ugc">schema descriptions</a>, <a href="https://hexdocs.pm/zoi/Zoi.Form.html" rel="noopener nofollow ugc">phoenix form</a>, <a href="https://hexdocs.pm/zoi/Zoi.TypeSpec.html" rel="noopener nofollow ugc">typespecs</a>, etc.<br>
Basically you have a single entrypoint (zoi schema) and from there you can derive into multiple sources. Does that makes more sense?<br>
I think this way Zoi would be able to create the embedded so you can use changesets validations directly or use Zoi to validate and use changesets as an error container as you described. It would be very flexible. Later it could also output other schemas from other sources such as absinthe, although I didnt dig that far to know how complex it would be.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385998" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/39">Post #38</a>
	                </div>
	            </div>
              <div id="likers-container-385998" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385998"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #38"></div>
  </section>
</div>
    <div class="postbit" id="385999" data-post-id="385999">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jam" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jam/120/36266_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jam
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Nice!</p>
<aside class="quote no-group" data-username="phcurado" data-post="39" data-topic="72108">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/phcurado/48/39030_2.png" class="avatar"> phcurado:</div>
<blockquote>
<p>Basically you have a single entrypoint (zoi schema) and from there you can derive into multiple sources.</p>
</blockquote>
</aside>
<p>Yes, one schema to rule them all. Any ideas on handling relationships?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="385999" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/40">Post #39</a>
	                </div>
	            </div>
              <div id="likers-container-385999" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="385999"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #39"></div>
  </section>
</div>
    <div class="postbit" id="386000" data-post-id="386000">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="phcurado" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/phcurado/120/39030_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  phcurado
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>For embedded schemas I think it’s possible, for example this schema:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Zoi.map(%{
  name: Zoi.string(),
  address: Zoi.map(%{
    street: Zoi.string(),
    number: Zoi.integer()
   })
})
</code></pre>
<p>Would generate this embedded:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule User do
  use Ecto.Schema

  embedded_schema do
    field :name, :string
    embeds_one :address, Address do
      field :street, :string
      field :number, :integer
    end
  end
end
</code></pre>
<p>But I probably need to add some metadata to the schema to be able to infer some fields such as the module name we see on the <code>Address</code>. So probably something like this on the address field:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">Zoi.map(%{
  street: Zoi.string(),
  number: Zoi.integer()
}, metadata: [module: Address])
</code></pre>
<p>Because we have metadata field specific for this, we use metadata to generate json schemas, and <a href="https://hexdocs.pm/zoi/Zoi.html#metadata/1" rel="noopener nofollow ugc">other things</a>.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="386000" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/zoi-schema-validation-library-inspired-by-zod/72108/41">Post #40</a>
	                </div>
	            </div>
              <div id="likers-container-386000" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="386000"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #40"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/72108/load_more?page=5">Load more posts (15 remaining)</a>
</div></template></turbo-stream>