Personally, I think taking a lesson from Django would be an ideal solution here. Django has a structure for creating web forms where you basically define a “form object” which is very similar to a changeset.
A form object basically just takes your datatypes in the order you want to present them and then gives you the ability to either spit out structured HTML for the form
.as_table to generate the form for you in a single command with well structured HTML. You can also render on a field by field basis if needed as well.
What you get with that form object combines validation, field white listing and form generation in much the same way as having a changeset per form. And all of that happens before you even touch what the form will be submitted to “if valid”, whether it will go to an API or database. Probably the closest equivalent to this in Phoenix is the formex library. The most Phoenix-focused tweak to that would be a clean way to validate a single field as the user was completing the form via a web-socket.
I like the idea of defining things that way since 9/10 times when I’m building a site I want the form rendering to be uniform across the app, but the whitelisting of variables will differ on a per-form basis. The formex approach abstracts the repetitive HTML and let’s you benefit from focusing on the security side of white listing.
It’s a challenge though, because there are a number of benefits that also come from hooking “form” validation to the database especially since the database is going to have strict type rules already defined. Maybe if there were a clean way to merge form validation with database level validation (type transformations/validations, length validations, constraints)?
Defining that interface would provide a good way to create the same structure for apis (both for connected elixir apps or outside apis). Changesets and formex are pretty close right now, IMO.
Just to play devil’s advocate to myself though, I always get concerned when anything dovetails from getting things done productively to hand-wringing over architectural purity. This feels like something that should come up more as a minor re-factor opportunity…because even though we get a lot of extra options from Elixir vs non-BEAM languages…a substantial portion of web applications are still just an interface in front of a database, no matter how much we work to convince ourselves otherwise. In that way, having a seamless form->validation->database flow is very beneficial.
EDIT: Looking at formex again it looks like they’ve got almost everything here. There’s even a formex_ecto library that will spit out a changeset for you.