{"id":239,"date":"2018-10-29T16:06:42","date_gmt":"2018-10-29T16:06:42","guid":{"rendered":"https:\/\/packlane.com\/blog\/?p=239"},"modified":"2018-10-29T16:06:42","modified_gmt":"2018-10-29T16:06:42","slug":"using-ecto-changeset-for-data-validation","status":"publish","type":"post","link":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation\/","title":{"rendered":"Using Ecto.Changeset for data validation"},"content":{"rendered":"<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD HTML 4.0 Transitional\/\/EN\" \"http:\/\/www.w3.org\/TR\/REC-html40\/loose.dtd\">\n<?xml encoding=\"utf-8\" ?><html><body><p><a href=\"https:\/\/hexdocs.pm\/ecto\/\"><code>Ecto<\/code><\/a>, while commonly used as a database wrapper, can also serve as a powerful data validation tool thanks to its <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html\"><code>Ecto.Changeset<\/code><\/a>.<\/p>\n<p>Let&rsquo;s say you have a list of movie data that you want to clean up and put into a nice <code>%Movie{}<\/code> struct that would consist of the following fields:<\/p>\n<ul>\n<li><code>title<\/code>, a string,<\/li>\n<li><code>runtime<\/code>, an integer representing the movie runtime in minutes,<\/li>\n<li><code>cast<\/code>, a list of strings for each actor name,<\/li>\n<li><code>releases<\/code>, a map of the release dates for different countries.<\/li>\n<\/ul>\n<p>While we could use Ecto&rsquo;s <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Schema.html#schema\/2\"><code>schema\/2<\/code><\/a> macro to define this, we will instead use a simple struct&sup1; as we won&rsquo;t actually be persisting this data to a database&sup2;.<\/p>\n<pre><code class=\"elixir\">defmodule Movie do\r\n  defstruct [:title, :runtime, :cast, :releases]\r\n\r\n  @type_constraints %{\r\n    title: :string,\r\n    runtime: :integer,\r\n    cast: {:array, :string},\r\n    releases: {:map, :date}\r\n  }\r\nend\r\n<\/code><\/pre>\n<p>You can check the list of available types <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Schema.html#module-primitive-types\">here<\/a>, and if you can&rsquo;t find what you need you can always define a <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Type.html\">custom type<\/a>.<\/p>\n<p>Now that we have defined our constraints, we want a function that&rsquo;ll take a regular map, validate it and return a <code>%Movie{}<\/code> if the input data is valid, or an error if it isn&rsquo;t.<\/p>\n<p>The first thing we&rsquo;ll do is filter out any field from the input map that isn&rsquo;t part of our <code>%Movie{}<\/code> struct or that can&rsquo;t be converted to the right type. The <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#cast\/4\"><code>cast\/4<\/code><\/a> function allows us to do just that:<\/p>\n<pre><code class=\"elixir\">cast(data, params, permitted, opts \\\\ [])\r\n<\/code><\/pre>\n<p><code>data<\/code> is our container, so here we&rsquo;ll be using an empty <code>%Movie{}<\/code> struct&sup3;. Since we&rsquo;re not using <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Schema.html#schema\/2\"><code>schema\/2<\/code><\/a> we&rsquo;ll need to add the type constraints here too: <code>{%Movie{}, @type_constraints}<\/code>. <code>params<\/code> is the data we want to filter and validate, <code>permitted<\/code> is a list of the keys we want to allow in <code>params<\/code>. <code>opts<\/code> allows to pass <code>empty_values:<\/code> (for example you might want to set this to [[], &ldquo;&rdquo;] to make empty lists and empty strings nil) but we won&rsquo;t be using it here. It defaults to <code>empty_values: [\"\"]<\/code>.<\/p>\n<p>Let&rsquo;s add a function to our <code>Movie<\/code> module to create a new <code>%Movie{}<\/code> from a map of params:<\/p>\n<pre><code class=\"elixir\">defmodule Movie do\r\n  import Ecto.Changeset\r\n\r\n  ...\r\n\r\n  @allowed_keys Map.keys(@type_constraints) # We want to allow every key that has a type constraint\r\n\r\n  def new(movie_data) do\r\n    {%__MODULE__{}, @type_constraints}\r\n    |&gt; cast(movie_data, @allowed_keys)\r\n  end\r\nend\r\n<\/code><\/pre>\n<p>We can now test it and make sure it works as expected:<\/p>\n<pre><code class=\"elixir\">iex&gt; Movie.new(%{title: \"The Hitchhiker's Guide to the Galaxy\", runtime: \"109\", cast: [\"Zooey Deschanel\", \"Martin Freeman\"], releases: %{\"UK\" =&gt; \"2005-04-28\", \"KR\" =&gt; \"2005-08-26\"}})\r\n#Ecto.Changeset&lt;\r\n  action: nil,\r\n  changes: %{\r\n    cast: [\"Zooey Deschanel\", \"Martin Freeman\"],\r\n    releases: %{},\r\n    runtime: 109,\r\n    title: \"The Hitchhiker's Guide to the Galaxy\"\r\n  },\r\n  errors: [],\r\n  data: #Movie&lt;&gt;,\r\n  valid?: true\r\n&gt;\r\n<\/code><\/pre>\n<p>Ecto uses <code>%Changeset{}<\/code> to track changes in the data, which is really helpful when you want to persist your data to a database using Ecto or to get a list of validation errors, but not really useful for valid data in our case. Thankfully, <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#apply_changes\/1\"><code>apply_changes\/1<\/code><\/a> can help us with that:<\/p>\n<pre><code class=\"elixir\">defmodule Movie do\r\n  ...\r\n\r\n  def new(movie_data) do\r\n    case changeset(movie_data) do\r\n      %{valid?: true} = changeset -&gt; {:ok, apply_changes(changeset)}\r\n      changeset -&gt; {:error, changeset}\r\n    end\r\n  end\r\n\r\n  def changeset(params) do\r\n    {%__MODULE__{}, @type_constraints}\r\n    |&gt; cast(params, @allowed_keys)\r\n  end\r\nend\r\n<\/code><\/pre>\n<p>The <code>%Changeset{}<\/code> struct has a <code>valid?<\/code> flag that we can use to check if our data is valid. <code>apply_changes\/1<\/code> will happily ignore it and return the underlying struct with our new data regardless of this flag&#8308;, so we have to check that it&rsquo;s actually set to <code>true<\/code> before calling it. Let&rsquo;s try that again:<\/p>\n<pre><code class=\"elixir\">iex&gt; Movie.new(%{title: \"The Hitchhiker's Guide to the Galaxy\", runtime: \"109\", cast: [\"Zooey Deschanel\", \"Martin Freeman\"], releases: %{\"UK\" =&gt; \"2005-04-28\", \"KR\" =&gt; \"2005-08-26\"}})\r\n{:ok,\r\n %Movie{\r\n   cast: [\"Zooey Deschanel\", \"Martin Freeman\"],\r\n   releases: %{\"KR\" =&gt; &sim;D[2005-08-26], \"UK\" =&gt; &sim;D[2005-04-28]},\r\n   runtime: 109,\r\n   title: \"The Hitchhiker's Guide to the Galaxy\"\r\n }}\r\niex&gt; Movie.new(%{runtime: \"not a number\", releases: %{\"UK\" =&gt; \"not a date\"}})\r\n{:error,\r\n #Ecto.Changeset&lt;\r\n   action: nil,\r\n   changes: %{},\r\n   errors: [\r\n     releases: {\"is invalid\", [type: {:map, :date}, validation: :cast]},\r\n     runtime: {\"is invalid\", [type: :integer, validation: :cast]}\r\n   ],\r\n   data: #Movie&lt;&gt;,\r\n   valid?: false\r\n &gt;}\r\n<\/code><\/pre>\n<p>As you can see, <code>cast\/3<\/code> took care of converting <code>runtime<\/code> to an integer and our release dates to <a href=\"https:\/\/hexdocs.pm\/elixir\/Kernel.html#sigil_D\/2\"><code>%Date{}<\/code> structs<\/a>, returning a list of errors when some of the data was invalid.<\/p>\n<p>Now that we have our <code>new\/1<\/code> function working, let&rsquo;s add some validation!<\/p>\n<p>First, we want to make sure our movies always have a title and a runtime, so we&rsquo;ll use <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#validate_required\/3\"><code>validate_required\/3<\/code><\/a> to help us with that:<\/p>\n<pre><code class=\"elixir\">defmodule Movie do\r\n  @required_keys [:title, :runtime]\r\n\r\n  ...\r\n\r\n  def changeset(params) do\r\n    {%__MODULE__{}, @type_constraints}\r\n    |&gt; cast(params, @allowed_keys)\r\n    |&gt; validate_required(@required_keys)\r\n  end\r\nend\r\n<\/code><\/pre>\n<p>And that&rsquo;s it! Ecto will return the changeset with <code>valid?<\/code> flag set to <code>false<\/code> if either of these two fields is missing in our data. There are a lot of validation functions in <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html\"><code>Ecto.Changeset<\/code><\/a> to add constraints on all sorts of data. For example, we could use <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#validate_number\/3\"><code>validate_number\/3<\/code><\/a> to ensure the <code>runtime<\/code> is always greater than 30 minutes and shorter than 5 hours, or <code>validate_length\/3<\/code> to make sure there&rsquo;s at least one actor in <code>cast<\/code>.<\/p>\n<p>Sometimes, though, you&rsquo;ll want to have validation rules that a specific to your use case. For this, Ecto provides an helper function <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#validate_change\/3\"><code>validate_change\/3<\/code><\/a> that we can use to define custom validations.<\/p>\n<p>Let&rsquo;s say we want to allow setting the IMDB page for each movie. Ecto doesn&rsquo;t have a URL validator so we&rsquo;re going to define a custom one and use it in our <code>changeset\/1<\/code> function:<\/p>\n<pre><code class=\"elixir\">defmodule Movie do\r\n  ...\r\n\r\n  defstruct [:title, :runtime, :cast, :releases, :imdb_url]\r\n\r\n  @type_constraints %{\r\n    ...\r\n    imdb_url: :string\r\n  }\r\n\r\n  ...\r\n\r\n  def changeset(params) do\r\n    {%__MODULE__{}, @type_constraints}\r\n    &#9474;&gt; cast(params, @allowed_keys)\r\n    &#9474;&gt; validate_required(@required_keys)\r\n    &#9474;&gt; validate_imdb_url()\r\n  end\r\n\r\n  defp validate_imdb_url(changeset) do\r\n    validate_change(changeset, :imdb_url, fn :imdb_url, value -&gt;\r\n      case URI.parse(value) do\r\n        %URI{host: \"imdb.com\"} -&gt; []\r\n        %URI{host: \"www.imdb.com\"} -&gt; []\r\n        _ -&gt; [imdb_url: \"is not a valid URL\"]\r\n      end\r\n    end)\r\n  end\r\nend\r\n<\/code><\/pre>\n<p><a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#validate_change\/3\"><code>validate_change\/3<\/code><\/a> expects a changeset, the name of the field you want to validate and a function that will be passed the name of the field and its value and that should return a list of validation errors if any.<\/p>\n<p>Note that the custom validation function will only be called if there&rsquo;s a change for the given field and if the new value isn&rsquo;t <code>nil<\/code>. Since we have set the type of <code>imdb_url<\/code> to <code>:string<\/code> the value passed to our validation function will always be a string.<\/p>\n<p>Let&rsquo;s give it a try:<\/p>\n<pre><code class=\"elixir\">iex&gt; Movie.new(%{title: \"The Hitchhiker's Guide to the Galaxy\", runtime: \"109\", cast: [\"Zooey Deschanel\", \"Martin Freeman\"], releases: %{\"UK\" =&gt; \"2005-04-28\", \"KR\" =&gt; \"2005-08-26\"}, imdb_url: \"https:\/\/www.imdb.com\/title\/tt0371724\"})\r\n{:ok,\r\n %Movie{\r\n   ...\r\n }}\r\niex&gt; Movie.new(%{title: \"The Hitchhiker's Guide to the Galaxy\", runtime: \"109\", cast: [\"Zooey Deschanel\", \"Martin Freeman\"], releases: %{\"UK\" =&gt; \"2005-04-28\", \"KR\" =&gt; \"2005-08-26\"}, imdb_url: \"https:\/\/www.not-imdb.com\/\"})\r\n{:error,\r\n #Ecto.Changeset&lt;\r\n   action: nil,\r\n   changes: %{\r\n     cast: [\"Zooey Deschanel\", \"Martin Freeman\"],\r\n     imdb_url: \"https:\/\/www.not-imdb.com\/\",\r\n     releases: %{\"KR\" =&gt; &sim;D[2005-08-26], \"UK\" =&gt; &sim;D[2005-04-28]},\r\n     runtime: 109,\r\n     title: \"The Hitchhiker's Guide to the Galaxy\"\r\n   },\r\n   errors: [imdb_url: {\"is not a valid URL\", []}],\r\n   data: #Movie&lt;&gt;,\r\n   valid?: false\r\n &gt;}\r\n<\/code><\/pre>\n<p>Et voil&agrave;!<\/p>\n<ol>\n<li>Note that you don&rsquo;t actually need to define a struct and could just use a map instead.<\/li>\n<li><code>schema\/2<\/code> will add a <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Schema.Metadata.html\"><code>__meta__<\/code><\/a> field that&rsquo;s mostly here to help with data persistance.<\/li>\n<li>We could also pass a non-empty struct, which is nice if you need to update existing data.<\/li>\n<li><a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#apply_action\/2\"><code>apply_action\/2<\/code><\/a> would take care of this but requires an <a href=\"https:\/\/hexdocs.pm\/ecto\/Ecto.Changeset.html#module-changeset-actions\">action<\/a> to be given, and that doesn&rsquo;t make much sense in our case. You would probably want to use it when working with <a href=\"https:\/\/hexdocs.pm\/phoenix_html\/Phoenix.HTML.Form.html\">Phoenix form<\/a>, as they require an action to be set in order to display errors.<\/li>\n<\/ol>\n<p>PS: Looking to join a collaborative remote engineering team writing Elixir and React? We&rsquo;re often hiring, go check out our listings at https:\/\/packlane.com\/jobs<\/p>\n<\/body><\/html>\n","protected":false},"excerpt":{"rendered":"<p>Ecto, while commonly used as a database wrapper, can also serve as a powerful data validation tool thanks to its Ecto.Changeset. Let&rsquo;s say you have a list of movie data that you want to clean up and put into a nice %Movie{} struct that would consist of the following fields: title, a string, runtime, an [&hellip;]<\/p>\n","protected":false},"author":12357,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[9],"class_list":["post-239","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-engineering"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Using Ecto.Changeset for data validation - Packlane<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Using Ecto.Changeset for data validation - Packlane\" \/>\n<meta property=\"og:description\" content=\"Ecto, while commonly used as a database wrapper, can also serve as a powerful data validation tool thanks to its Ecto.Changeset. Let&rsquo;s say you have a list of movie data that you want to clean up and put into a nice %Movie{} struct that would consist of the following fields: title, a string, runtime, an [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation\" \/>\n<meta property=\"og:site_name\" content=\"Packlane\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Packlane\/\" \/>\n<meta property=\"article:published_time\" content=\"2018-10-29T16:06:42+00:00\" \/>\n<meta name=\"author\" content=\"Joseph Bacani\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@Packlane\" \/>\n<meta name=\"twitter:site\" content=\"@Packlane\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Joseph Bacani\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation\"},\"author\":{\"name\":\"Joseph Bacani\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#\\\/schema\\\/person\\\/a96d273aeaef1056618349a449320735\"},\"headline\":\"Using Ecto.Changeset for data validation\",\"datePublished\":\"2018-10-29T16:06:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation\"},\"wordCount\":831,\"publisher\":{\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#organization\"},\"keywords\":[\"engineering\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation\",\"url\":\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation\",\"name\":\"Using Ecto.Changeset for data validation - Packlane\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#website\"},\"datePublished\":\"2018-10-29T16:06:42+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/using-ecto-changeset-for-data-validation#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/packlane.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Using Ecto.Changeset for data validation\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/packlane.com\\\/blog\\\/\",\"name\":\"Packlane\",\"description\":\"Behind the Box\",\"publisher\":{\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/packlane.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#organization\",\"name\":\"Packlane\",\"url\":\"https:\\\/\\\/packlane.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/packlane.com\\\/blog\\\/wp-content\\\/uploads\\\/2017\\\/12\\\/10390041_1615293418716624_7619943306604955155_n.png\",\"contentUrl\":\"https:\\\/\\\/packlane.com\\\/blog\\\/wp-content\\\/uploads\\\/2017\\\/12\\\/10390041_1615293418716624_7619943306604955155_n.png\",\"width\":300,\"height\":300,\"caption\":\"Packlane\"},\"image\":{\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/Packlane\\\/\",\"https:\\\/\\\/x.com\\\/Packlane\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/packlane.com\\\/blog\\\/#\\\/schema\\\/person\\\/a96d273aeaef1056618349a449320735\",\"name\":\"Joseph Bacani\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aed83553dbca129dc08551f3d53860cbe66200574631a60c5034a8287c3ab3dd?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aed83553dbca129dc08551f3d53860cbe66200574631a60c5034a8287c3ab3dd?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aed83553dbca129dc08551f3d53860cbe66200574631a60c5034a8287c3ab3dd?s=96&d=mm&r=g\",\"caption\":\"Joseph Bacani\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Using Ecto.Changeset for data validation - Packlane","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation","og_locale":"en_US","og_type":"article","og_title":"Using Ecto.Changeset for data validation - Packlane","og_description":"Ecto, while commonly used as a database wrapper, can also serve as a powerful data validation tool thanks to its Ecto.Changeset. Let&rsquo;s say you have a list of movie data that you want to clean up and put into a nice %Movie{} struct that would consist of the following fields: title, a string, runtime, an [&hellip;]","og_url":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation","og_site_name":"Packlane","article_publisher":"https:\/\/www.facebook.com\/Packlane\/","article_published_time":"2018-10-29T16:06:42+00:00","author":"Joseph Bacani","twitter_card":"summary_large_image","twitter_creator":"@Packlane","twitter_site":"@Packlane","twitter_misc":{"Written by":"Joseph Bacani","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation#article","isPartOf":{"@id":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation"},"author":{"name":"Joseph Bacani","@id":"https:\/\/packlane.com\/blog\/#\/schema\/person\/a96d273aeaef1056618349a449320735"},"headline":"Using Ecto.Changeset for data validation","datePublished":"2018-10-29T16:06:42+00:00","mainEntityOfPage":{"@id":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation"},"wordCount":831,"publisher":{"@id":"https:\/\/packlane.com\/blog\/#organization"},"keywords":["engineering"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation","url":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation","name":"Using Ecto.Changeset for data validation - Packlane","isPartOf":{"@id":"https:\/\/packlane.com\/blog\/#website"},"datePublished":"2018-10-29T16:06:42+00:00","breadcrumb":{"@id":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/packlane.com\/blog\/using-ecto-changeset-for-data-validation#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/packlane.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Using Ecto.Changeset for data validation"}]},{"@type":"WebSite","@id":"https:\/\/packlane.com\/blog\/#website","url":"https:\/\/packlane.com\/blog\/","name":"Packlane","description":"Behind the Box","publisher":{"@id":"https:\/\/packlane.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/packlane.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/packlane.com\/blog\/#organization","name":"Packlane","url":"https:\/\/packlane.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/packlane.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/packlane.com\/blog\/wp-content\/uploads\/2017\/12\/10390041_1615293418716624_7619943306604955155_n.png","contentUrl":"https:\/\/packlane.com\/blog\/wp-content\/uploads\/2017\/12\/10390041_1615293418716624_7619943306604955155_n.png","width":300,"height":300,"caption":"Packlane"},"image":{"@id":"https:\/\/packlane.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Packlane\/","https:\/\/x.com\/Packlane"]},{"@type":"Person","@id":"https:\/\/packlane.com\/blog\/#\/schema\/person\/a96d273aeaef1056618349a449320735","name":"Joseph Bacani","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/aed83553dbca129dc08551f3d53860cbe66200574631a60c5034a8287c3ab3dd?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/aed83553dbca129dc08551f3d53860cbe66200574631a60c5034a8287c3ab3dd?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/aed83553dbca129dc08551f3d53860cbe66200574631a60c5034a8287c3ab3dd?s=96&d=mm&r=g","caption":"Joseph Bacani"}}]}},"_links":{"self":[{"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/posts\/239","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/users\/12357"}],"replies":[{"embeddable":true,"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/comments?post=239"}],"version-history":[{"count":0,"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/posts\/239\/revisions"}],"wp:attachment":[{"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/media?parent=239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/categories?post=239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/packlane.com\/blog\/wp-json\/wp\/v2\/tags?post=239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}