In this small article, lets look into a simple strong params implementation for our dear Rubysts! If you've been using Phoenix but miss this little thing about Rails I think you can find the presented approach interesting. But first, let's also look into the motivation and alternatives!
(If you come from Rails, you can safely skip this)
Simply put, the internet is a scary place. We do not trust our "angel" users to send us only "good" data all the time. A common technique to avoid problems is to discard any kind of params your action does not care about. Rails implements this through strong params. But what about Phoenix? Does it not have the equivalent? Well, not exactly…
Ecto and cast/4
Okay so if your first thought about this article was "this is stupid, we have Ecto.cast/4!" I cannot say you're completely wrong. In fact, I'm a strong believer in Ecto myself and use it on most of my projects — it is just an amazing tool that can be used for param whitelisting. The only problem is that is is also many other things — Ecto can perfom validation, manage records, whitelist params, etc. etc. But for my use case, the main problem was that cast/4 is directly associated with a schema — you'll use it with a struct, changeset… But what if you just want to whitelist params at a controller level? Maybe you want to call function from a context with some configuration that comes in as a param (pagination maybe?).
Of course, someone is going to mention pattern matching — and don't get me wrong, there is absolutely nothing wrong with that, in most cases it is also quite elegant:
I do think it becomes much less practical when you just pattern match to reconstruct a map with atom keys (for us, anything past a controller level is treated like an atom, it just makes life much easier) or pass in function params:
In this example, we only have 5 params, what if we had 10? I think its clear to see how it could get out of control…
A different approach (show me code, dude)
Okay, this is a somewhat inspired on Ecto simple solution:
The important thing to remember here, is that atoms in Erlang are not garbage-collected. This implies that once the max limit is reached, your VM will go down. Because of that, our simple implementation never converts input to atoms directly, instead, we convert to strings and only during a positive match we will use
Its not perfect, but it will work for our simple cases:
It can also parse out atom keys, but keep in mind it will not parse out nested attributes:
EDIT: For a more complete solution, supporting nested attributes (and other niceties, you can check Rock Neutoriko's amazing answer here: https://medium.com/@rockneurotiko/nice-post-42f9191c25f3
Using with Controllers
This simple implementation gets a little more exciting when you use it with Controllers. First, let's import our helper function into all controllers by default:
Inside "my_app_web.ex", import your helper inside the controller:
Then, this is how we can use it to simplify our initial "cluttered" example:
… And that's it! I hope you find this useful for your current and future endeavors!