libo

libo

Elixact - schema definition and validation (think Pydantic in Elixir)

Hey,

I have used a bunch of schema libraries in Elixir and didn’t find the one satisfies all my needs. At some point, I kinda miss Python for having Pydantic. So recently I started to make one for myself.

Elixact

Elixact is a schema definition and validation library, inspired by Python’s Pydantic.

Features

  • :bullseye: Intuitive Schema DSL - Similar to Ecto.Schema syntax
  • :magnifying_glass_tilted_left: Strong Type Validation - Comprehensive validation for basic and complex types
  • :bar_chart: JSON Schema Support - Automatic generation of JSON Schema from your Elixir schemas
  • :puzzle_piece: Custom Types - Easily define reusable custom types
  • :christmas_tree: Nested Schemas - Support for deeply nested data structures
  • :chains: Field Constraints - Rich set of built-in constraints
  • :police_car_light: Structured Errors - Clear and actionable error messages

See the doc for more details: elixact v0.1.2 — Documentation

Github: GitHub - LiboShen/elixact: schema definition and validation library for Elixir
Hex: elixact | Hex

Credits / Prior Arts

As I mentioned above, I have tried many libraries to give me strong data validations. I took inspirations from all of them:

Ecto is the obvious choice when you need simple a schema.

Typestruct is a lightweight improvement of the builtin struct.

Drops is the closest of what I need. But I failed to make it work with nested or complex schemas.

I’m using it for my several projects now. Let me know if you find this is useful. I’m open for collaboration to make it better. Thanks.

Most Liked

Eiji

Eiji

Here are my few cents:

gteq and lteq is not a common naming. I recommend ge and le respectively.


gt and others looks good as a short key in options Keyword, but too short for DSL. I would recommend to add between, count_between (for array), length_between (for string) and maybe even change naming for example from gt to greater_than. If you do not like it you can alternatively create a constraints macro.

defmodule Example do
  use Elixact

  schema "name" do
    field :age, :integer do
      constraints gt: 0, lt: 10
    end
  end
end

    field :settings, {:map, {:string, {:union, [:string, :boolean, :integer]}}} do

This is not bad for a contributors in your project, but it doesn’t look best as DSL. You can consider using @spec notation, so you can convert it’s AST into such data structure within your library.

    field :settings, %{String.t() => String.t() | boolean | integer}) do

looks much more clear.


    {block, _opts} =
      Keyword.pop(
        opts,
        :do,
        quote do
        end
      )

You can change the default to {:__block__, [], []}, so it would look like:

    {block, _opts} = Keyword.pop(opts, :do, {:__block__, [], []})

Since you don’t use _opts you can change it to:

    block = opts[:do] || {:__block__, [], []}

and since you are not using opts in other place you can also change the function heading to:

  defmacro field(name, type, opts \\ [do: {:__block__, [], []}])
  defmacro field(name, type, do: block) do

That’s -7 lines of code (LOC) without losing any feature. :+1:


      var!(field_meta) =

As you may know variable field_meta would not be hygienized. I would recommend to store it in other ways. A common practice is to use module attributes, but those as same as variables may conflict with the code. What I personally like is to store data in process. People don’t use Process often and especially in compile time. If you would use your own Agent (with moduledoc set to false) which starts only in compile time then it would be even better as there is no chance that some developer would use such data.


Many modules are not documented. Consider adding documentation or use @moduledoc false in case some module is not part of public API. To avoid such things in future I recommend to give a try credo tool.


Elixact.Application is starting without any children. If you don’t plan to use it you don’t have to even create such file. All you have to do is to remove mod: {Elixact.Application, []} in application function inside Elixact.MixProject.


The file lib/elixact/config.ex is empty. If you don’t use it you should remove it.

sodapopcan

sodapopcan

I have yet to use one of these libraries—I just keep using Ecto for now even though it gets clunky for general use—but I really like this DSL. Nice work, I’ll keep it in mind. Also, good name :sweat_smile:

libo

libo

Hey, these tips are super helpful.

  1. I like the constraints macro approach. It provides a natural grouping for value constraints and other field metadata.
  2. Using @spec notation is nicer, I agree. Never thought about it before. I’d like to look into it.
  3. And thanks for the compile time Agent tip. It’s kinda eye-opening TBH.
  4. I’ll do housekeeping for the silly leftovers.
mudasobwa

mudasobwa

Creator of Cure

Last week I shamelessly drop a link to my estructura library for the second time in a row, but still: @spec notation would make one obliged to implement an ad hoc, informally-specified, bug-ridden, slow implementation of half of dialyzer. One cannot allow spec notation and then restrict it to some types. Working with the remote complex types is a pain and a ton of code.

That’s why I went with StreamData types, what literally granted me the no-code implementation of data generation for property-based testing.

D4no0

D4no0

It seems that there is a huge flow of type validation libraries lately and all try to define their own DSL or data structure for validation.

Folk that is already doing these kind of validations for years at the edge of the system are using ecto + embedded schemas. Ecto strikes the perfect balance between compile-time definition of base types and runtime validation with changesets.

It has some shortcomings including:

  1. Too tied to database - type validation code, custom types are very tied to database and it has inherently some bugs that could be reworked entirely;
  2. No composition, only associations - this can be easily solved even in base ecto with some metaprogramming, but it would be nice to have native support;
  3. Schemas are tied to a module - this one is hard to decide, however I would prefer if schemas were not tied to a module.

Solve all of these problems while keeping the ecto way of doing things and you will not only will have a library that works well and it’s very flexible, but everyone that already uses ecto for non-database validations will happily migrate over, me including.

Where Next?

Popular in Libraries Top

marcuslankenau
I feel kind of stuck with the absence of a proper xml library for Elixir. Currently I use SweetXML which was ok for me more or less to pa...
New
hpopp
After just over two years in development, this latest version of Pigeon is what I finally consider done in regards to my original vision ...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
arkgil
Hi all! I’m happy to announce that Telemetry v0.3.0 is out! This release marks the conversion from Elixir to Erlang so that all the libr...
New
dbern
I’m excited to announce that TaxJar has developed and open-sourced DateTimeParser. We developed it because we found a need to parse user ...
New
mindok
What is ContEx? A pure Elixir server-side data plotting/charting library outputting SVG. It has nice barcharts in particular and works g...
New
zorbash
I created Kitto a framework for dashboards inspired by Dashing. [demo] The distributed characteristics of Elixir and the low memory foo...
New
Eiji
ExApi is a library that I’m developing now and hope release soon This library will allow to: list all apis list all api implementation...
New
mattludwigs
Grizzly is a library for working with Z-Wave devices. Z-Wave is a low-frequency radio protocol for controlling smart home devices on a me...
New

Other popular topics Top

sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Sub Categories:

We're in Beta

About us Mission Statement