The requirement to standardize and validate parameters passed from the client is a basic requirement when building Web APIs. I have a tutorial on using Ecto.Changeset to standardize in this article:
Parse and validate request param in Phoenix with Ecto
In this article, I will guide a short and simpler way using the Tarams library available. This library is actually Ecto.Changeset
but it helps us not to have to repeat too much code like when using Ecto.Changeset
Some interesting features of Tarams:
- Provides a simple way to define parameter structures
- Allows defining dynamic default values
- Allows defining functions to cast values to the correct data type
- Defines the function to validate the data
Here’s how to use Tarams
. As an example we are writing an API to update employee profiles. The request is
1 2 3 4 5 6 7 | email: bắt buộc, đúng định dạng first_name: bắt buộc last_name: bắt buộc birthday: không bắt buộc, kiểu ngày tháng title: không bắt buộc start_date: ngày bắt đầu làm việc, ngày tháng, mặc định là ngày hiện tại |
1. The definition of the structure of the passed parameter is quite simple
1 2 3 4 5 6 7 8 9 | <span class="token attribute variable">@schema</span> <span class="token punctuation">%</span> <span class="token punctuation">{</span> <span class="token attr-name">email:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">first_name:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">last_name:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">title:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">,</span> <span class="token attr-name">birth_day:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">start_date:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> |
Schema is simply a map where the key is the field name and value is a list option of that field.
2. Now add the constraints
- To mark a field as required, add the option
required: true
Taram
also allows validate data to reuseChangeset
‘s validate functions
1 2 3 4 5 6 7 8 9 | <span class="token attribute variable">@schema</span> <span class="token punctuation">%</span> <span class="token punctuation">{</span> <span class="token attr-name">email:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">,</span> <span class="token attr-name">required:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> <span class="token attr-name">validate:</span> <span class="token punctuation">{</span> <span class="token atom symbol">:format</span> <span class="token punctuation">,</span> <span class="token regex">~r/@/</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">first_name:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">,</span> <span class="token attr-name">required:</span> <span class="token boolean">true</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">last_name:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">,</span> <span class="token attr-name">required:</span> <span class="token boolean">true</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">title:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">,</span> <span class="token attr-name">birth_day:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">start_date:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> |
3. Now set the default values
default
can be a value or a function. Every time the parameter parse, this function will be called to get its default value
1 2 3 4 5 6 7 | <span class="token attribute variable">@schema</span> <span class="token punctuation">%</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token attr-name">title:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">,</span> <span class="token attr-name">default:</span> <span class="token string">"staff"</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">birth_day:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">start_date:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">,</span> <span class="token attr-name">default:</span> <span class="token capture function">&Timex.today/0]</span> <span class="token punctuation">}</span> |
4. Cast the parameter values to the correct type
Many of the passed parameter values must be converted to correct complex data types such as dates and lists. For example, if the date is passed as string 01/12/1994
, it must be converted to date type to reuse. Tarams supports defining a custom function to cast the value, which returns
{:ok, value}
if parsing is successful{:error, error_message}
failure
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">def</span> parse_date <span class="token punctuation">(</span> date_str <span class="token punctuation">)</span> <span class="token keyword">do</span> Timex <span class="token punctuation">.</span> parse <span class="token punctuation">(</span> date_str <span class="token punctuation">,</span> <span class="token string">"{0D}/{0M}/{YYYY}"</span> <span class="token punctuation">)</span> <span class="token keyword">end</span> <span class="token attribute variable">@schema</span> <span class="token punctuation">%</span> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token attr-name">title:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:string</span> <span class="token punctuation">,</span> <span class="token attr-name">default:</span> <span class="token string">"staff"</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token attr-name">birth_day:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">,</span> <span class="token attr-name">cast_func:</span> <span class="token capture function">&parse_date/1],</span> <span class="token attr-name">start_date:</span> <span class="token punctuation">[</span> <span class="token attr-name">type:</span> <span class="token atom symbol">:date</span> <span class="token punctuation">,</span> <span class="token attr-name">default:</span> <span class="token capture function">&Timex.today/0]</span> <span class="token punctuation">}</span> |
5. Now use any
1 2 3 4 5 6 7 8 9 | <span class="token keyword">def</span> update <span class="token punctuation">(</span> conn <span class="token punctuation">,</span> params <span class="token punctuation">)</span> <span class="token keyword">do</span> with <span class="token punctuation">{</span> <span class="token atom symbol">:ok</span> <span class="token punctuation">,</span> user_data <span class="token punctuation">}</span> <span class="token operator"><-</span> Tarams <span class="token punctuation">.</span> parse <span class="token punctuation">(</span> <span class="token attribute variable">@schema</span> <span class="token punctuation">,</span> params <span class="token punctuation">)</span> <span class="token keyword">do</span> <span class="token comment"># do anything with your params</span> <span class="token comment"># access data bằng atom key: user_data.email</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token atom symbol">:error</span> <span class="token punctuation">,</span> changset <span class="token punctuation">}</span> <span class="token operator">-></span> <span class="token comment"># return params error</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
The parse
function will parse and validate the data. If everything is fine, {:ok, data}
will be returned and {:error, changeset}
will be returned.
Done! Your code will become much simpler and more concise