anthony-khong
I just published my first Hex library Injecto (link to repo), which roughly means Into JSON schema and Ecto. Declaring:
defmodule Post do
@properties %{
title: {:string, required: true},
description: {:string, []},
likes: {:integer, required: true, minimum: 0}
}
use Injecto
end
defines Post as an Ecto schema, and has an accessible JSON schema along with the options:
%ExJsonSchema.Schema.Root{
schema: %{
"properties" => %{
"description" => %{
"anyOf" => [%{"type" => "string"}, %{"type" => "null"}]
},
"likes" => %{"minimum" => 0, "type" => "integer"},
"title" => %{"type" => "string"}
},
"required" => ["likes", "title"],
"title" => "Elixir.Post",
"type" => "object",
"x-struct" => "Elixir.Post"
},
refs: %{},
definitions: %{},
location: :root,
version: 7,
custom_format_validator: nil
}
For a bit of background, I was looking for a tool to achieve a couple of things:
- validate data coming in from external sources and data going out; and
- validate requests and responses using JSON schema and expose the specs using Swagger.
For point 1, I found Ecto changesets and Elixir structs to be really nice to work with, but I couldn’t find anything to automatically translate Ecto schemas into JSON schemas (CMIIW). As for point 2, ex_json_schema works well, but defining JSON schemas by hand is quite clunky, and I didn’t find a way to do automatic struct definition.
I set out to write my own solution for the two options above - that seems to be the recommendation out of this discussion. However, I found that the code I wrote was quite verbose, and packing it into a use macro seems to cut down a lot of the boilerplate code.
I’m still very new with Elixir. Any comments or feedback or suggestions to do things a better way would be very much appreciated!
Trending in Questions
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
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 1- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
zachallaun
This is very neat and certainly useful! I’m working on a project that does a lot of mapping between my Elixir code and API resources, and so far I’ve been rather lax/unstructured about it, but I’ve been thinking of doing something like this.
The first thing that comes to mind – have you considered using
Ecto.Schema’s reflection API instead of a custom data language, so that users can define their schemas using Ecto’s own DSL?Here’s how it might theoretically look:
A few “tricks” that would make something like the above possible:
The Injecto injected function definitions could expect to find data in
:persistent_termstorage (or similar).Module.register_attribute(__MODULE__, :injecto, accumulate: true, persist: true)could make the@injectodeclarations available at runtime throughmodule.__info__(:attributes)(docs).An
@after_compilehook could use the Ecto__schema__(...)reflection API along with the persisted@injectoattribute to pre-compute whatever state Injecto needs to do its stuff and save the result in:persistent_term, or error/warn/etc. if something is wrong (e.g. two@injecto title: [...]declarations are found).I can think of a number of benefits to this approach, but the biggest would be that it would be really easy to adopt. No learning a new thing – if you’re okay with everything being optional, you could stick
use Injectoin an existing schema and you’re all set.If this is a direction you’re interested in, I’d be happy to help where necessary!
Regarding the current API, I also have a couple suggestions:
@propertiesas a keyword option touse Injecto:@propertiesbe defined beforeuse Injectoby using a@before_compilecallback to inject your code. Combined with the above suggestion, the rough pattern would be something like:snake_casetocamelCase. This would probably mean a customJason.Encoderdefinition. In the example I gave with the theoretical API using Ecto’s schema DSL, I thought of using the schema field:sourceto map to the API key, but it might make sense to separate it in case you’re persisting these and don’t want your database field changed.