libo
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
Intuitive Schema DSL - Similar to Ecto.Schemasyntax
Strong Type Validation - Comprehensive validation for basic and complex types
JSON Schema Support - Automatic generation of JSON Schema from your Elixir schemas
Custom Types - Easily define reusable custom types
Nested Schemas - Support for deeply nested data structures
Field Constraints - Rich set of built-in constraints
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 · GitHub
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.
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
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
libo
Glad to see that you spotted the pun
Eiji
Here are my few cents:
gteqandlteqis not a common naming. I recommendgeandlerespectively.gtand others looks good as a short key in optionsKeyword, but too short forDSL. I would recommend to addbetween,count_between(for array),length_between(for string) and maybe even change naming for example fromgttogreater_than. If you do not like it you can alternatively create aconstraintsmacro.This is not bad for a contributors in your project, but it doesn’t look best as DSL. You can consider using
@specnotation, so you can convert it’s AST into such data structure within your library.looks much more clear.
You can change the default to
{:__block__, [], []}, so it would look like:Since you don’t use
_optsyou can change it to:and since you are not using
optsin other place you can also change the function heading to:That’s
-7lines of code (LOC) without losing any feature.As you may know variable
field_metawould 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 useProcessoften and especially in compile time. If you would use your ownAgent(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 falsein case some module is not part of public API. To avoid such things in future I recommend to give a try credo tool.Elixact.Applicationis 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 removemod: {Elixact.Application, []}inapplicationfunction insideElixact.MixProject.The file
lib/elixact/config.exis empty. If you don’t use it you should remove it.libo
Hey, these tips are super helpful.
constraintsmacro approach. It provides a natural grouping for value constraints and other field metadata.@specnotation is nicer, I agree. Never thought about it before. I’d like to look into it.Agenttip. It’s kinda eye-opening TBH.sodapopcan
I had the same thoughts re: spec syntax as well as
between(though I was thinkingrangeas with between I’m never sure if it’s inclusive or not) instead ofgtetc. Otherwise, I always appreciate the fully typed outgreater_thanovergt. Just IMO, of course.mudasobwa
Last week I shamelessly drop a link to my
estructuralibrary for the second time in a row, but still:@specnotation 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
StreamDatatypes, what literally granted me the no-code implementation of data generation for property-based testing.libo
That explains why I never saw a library use
@specnotation for type definition except dialyzer. It matches my initial intuition: it might be either hard to implement or limited in expressiveness.From pure readability aspect, my rank for the notions are
field :settings, %{String.t() => String.t() | boolean | integer} dofield :settings, map(string(), union(binary(), boolean(), integer())) dofield :settings, {:map, {:string, {:union, [:string, :boolean, :integer]}}} doEiji
Fine, but who said you have to support
User-defined types? As long as you support everything in core the user would be able to use:%SomeStruct{}as that’s a literal (not user defined type). In this case the most complicated spec is map and said struct notation as you have to support nested keys and values (which you already do in{:map, …}case.From that point you can even add support to user defined types later as all you would have to do is to fetch nested types, so in case of map and struct you would simply iterate over
keyandvalue, check if any of them isuser-defined, if so fetch it and continue nested work for fetched data.As long as you support everything which is not user defined and you have no problem with recursive calls support even whole
@specnotation should not be as big pain as it looks. So why almost nobody is using that? Well … it may be that they support maps and structs simply in the nestedDSL.%{key => value}notation is usually supported for a flat maps witth dynamic keys. In example above I suggested@spec-like syntax for union and flat map - not for a nested map and struct as if we know nested structure then we can simply useDSLfor that. Otherwise let value be anymap.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:
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.
sodapopcan
The lack of type flexibility has been my biggest problem using Ecto all the time. Lack of union types is probably the biggest thing and ya, custom types are too tied to the database.