<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[ξtérnal curiosity]]></title><description><![CDATA[ξtérnal curiosity]]></description><link>https://blog.dbrusenin.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 19:28:38 GMT</lastBuildDate><atom:link href="https://blog.dbrusenin.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Model Validation in Golang]]></title><description><![CDATA[Model validation is a crucial part of any piece of software. With proper validation you provide some useful guarantees and therefore simplify your further work. Working under assumptions also makes your code cleaner, because you don't need to make ad...]]></description><link>https://blog.dbrusenin.com/model-validation-in-golang</link><guid isPermaLink="true">https://blog.dbrusenin.com/model-validation-in-golang</guid><category><![CDATA[Web Development]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[golang]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[Validation]]></category><category><![CDATA[Developer]]></category><category><![CDATA[coding]]></category><category><![CDATA[learning]]></category><dc:creator><![CDATA[Dmitriy Brusenin]]></dc:creator><pubDate>Sat, 17 Aug 2024 07:37:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723880327657/987197ca-7cd6-46ee-8842-9051ecfb0502.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Model validation is a crucial part of any piece of software. With proper validation you provide some useful guarantees and therefore simplify your further work. Working under assumptions also makes your code cleaner, because you don't need to make additional checks every time you use the model anymore - you just perceive the model to be completely valid. Probably every language has a community library that already solves the validation problem, so you don't have to worry about implementing your own solution. In this post we will talk about model validation in Golang, best practices and particularly about <a target="_blank" href="https://pkg.go.dev/github.com/go-playground/validator/v10">validator v10</a> library.</p>
<h2 id="heading-validation-library-validator-v10"><strong>Validation Library - validator v10</strong></h2>
<p>There are many Golang libraries you can use for validating your structs. Many of them are listed there: <a target="_blank" href="https://awesome-go.com/validation/">Validation - Awesome Go</a>. There is no much difference between them so I suggest choosing the one with the largest community, because this implies that it has a lot of features, there are no obvious bugs and it will be supported longer. For Golang it's a <a target="_blank" href="https://github.com/go-playground/validator">validator</a> library that has already become the de facto standard for struct validation.</p>
<p><a target="_blank" href="https://github.com/go-playground/validator">Validator</a> library provides a fairly complete list of features. Most importantly, it gives you the ability to validate your structs using field tags and by writing custom validation functions that take the struct value and report all validation errors found. It works as simple as that:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> validate = validator.New(validator.WithRequiredStructEnabled())
​
<span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"required"`</span>
}
​
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
  m := Movie{
    Title: <span class="hljs-string">""</span>,
  }
  err := validate.Struct(m)
  fmt.Println(err) <span class="hljs-comment">// Error: empty title</span>
}
</code></pre>
<p>It's also worth to mention that <a target="_blank" href="https://github.com/gin-gonic/gin">gin</a> framework is integrated with <a target="_blank" href="https://github.com/go-playground/validator">validator</a> library and validation will be automatically applied while binding request into structs.</p>
<p>Now let's take a look at practical examples and how <a target="_blank" href="https://github.com/go-playground/validator">validator</a> library helps us to solve them.</p>
<h2 id="heading-field-validation-with-tags"><strong>Field Validation with Tags</strong></h2>
<p>First and the most simple validation option requires to mark struct fields with relevant validation tags. By default, all validation tags are defined under <code>validate</code> tag name. Tag name can be changed using <code>SetTagName</code> method in the following way:</p>
<pre><code class="lang-go">validate := validator.New(validator.WithRequiredStructEnabled())
validate.SetTagName(<span class="hljs-string">"YOUR_TAG_NAME"</span>)
</code></pre>
<p>Then you need to define your model with validation tags and call <code>Struct</code> method:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> validate = validator.New(validator.WithRequiredStructEnabled())
​
<span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"required"`</span>
}
​
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
  m := Movie{
    Title: <span class="hljs-string">""</span>,
  }
  err := validate.Struct(m)
  fmt.Println(err) <span class="hljs-comment">// Error: empty title</span>
}
</code></pre>
<h3 id="heading-multiple-tags"><strong>Multiple Tags</strong></h3>
<p>When you need multiple validators simply split them with comma (<code>,</code>):</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"required,alpha"`</span>
}
<span class="hljs-comment">// Title: "abc" - Pass</span>
<span class="hljs-comment">// Title: ""    - Fail</span>
<span class="hljs-comment">// Title: "λ"   - Fail</span>
</code></pre>
<h3 id="heading-multiple-tags-with-or"><strong>Multiple Tags with OR</strong></h3>
<p>You can also concatenate validators with logical OR using pipe (<code>|</code>):</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"required|alpha"`</span>
}
<span class="hljs-comment">// Title: "abc" - Pass</span>
<span class="hljs-comment">// Title: ""    - Fail</span>
<span class="hljs-comment">// Title: "λ"   - Pass</span>
</code></pre>
<h3 id="heading-multiple-tags-with-and-and-or"><strong>Multiple Tags with AND and OR</strong></h3>
<p>You can combine AND and OR and OR will be resolved first:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"number|alpha,lowercase"`</span>
  <span class="hljs-comment">// the same as:</span>
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"lowercase,number|alpha"`</span>
}
<span class="hljs-comment">// Title "a"  - Pass  // (TRUE)  AND (FALSE OR TRUE)</span>
<span class="hljs-comment">// Title "1"  - Pass  // (TRUE)  AND (TRUE  OR FALSE)</span>
<span class="hljs-comment">// Title "a1" - Fail  // (TRUE)  AND (FALSE OR FALSE)</span>
<span class="hljs-comment">// Title "A"  - Fail  // (FALSE) AND (FALSE OR TRUE)</span>
</code></pre>
<h3 id="heading-cross-field-tags"><strong>Cross-Field Tags</strong></h3>
<p>It's even possible to perform a cross-field validation using tags. Take a look:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"eqfield=Name"`</span>
  Name  <span class="hljs-keyword">string</span>
}
<span class="hljs-comment">// Title ""  Name ""  - Pass</span>
<span class="hljs-comment">// Title "a" Name "b" - Fail</span>
<span class="hljs-comment">// Title "a" Name "a" - Pass</span>
</code></pre>
<p>And even more magic...</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"eqcsfield=Info.Name"`</span>
  Info  <span class="hljs-keyword">struct</span> {
    Name <span class="hljs-keyword">string</span>
  }
}
<span class="hljs-comment">// Title ""  Name ""  - Pass</span>
<span class="hljs-comment">// Title "a" Name "b" - Fail</span>
<span class="hljs-comment">// Title "a" Name "a" - Pass</span>
</code></pre>
<ul>
<li>All built-in tags can be found on the official documentation page: <a target="_blank" href="https://pkg.go.dev/github.com/go-playground/validator/v10">https://pkg.go.dev/github.com/go-playground/validator/v10</a></li>
</ul>
<h2 id="heading-custom-field-validation"><strong>Custom Field Validation</strong></h2>
<p>You can define your own tags the following way:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">ValidateIsValidTag</span><span class="hljs-params">(fl validator.FieldLevel)</span> <span class="hljs-title">bool</span></span> {
  <span class="hljs-keyword">if</span> fl.Field().Kind() == reflect.String {
    <span class="hljs-keyword">return</span> fl.Field().String() == <span class="hljs-string">"valid"</span>
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
}
​
validate.RegisterValidation(<span class="hljs-string">"is_valid"</span>, ValidateIsValidTag)
</code></pre>
<p>And then...</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span> <span class="hljs-string">`validate:"is_valid"`</span>
}
​
<span class="hljs-comment">// Title ""        - Fail</span>
<span class="hljs-comment">// Title "valid"   - Pass</span>
<span class="hljs-comment">// Title "invalid" - Fail</span>
</code></pre>
<h2 id="heading-custom-struct-validation"><strong>Custom Struct Validation</strong></h2>
<p>For complex validation it's more recommended to use custom validation functions instead of putting dozens of validation tags with a complex boolean logic (AND, OR operators). This way your code is cleaner and easier to comprehend and extend.</p>
<p>Custom struct validation is a function that accepts struct value and returns a validation error if such occured. Here is an example:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title               <span class="hljs-keyword">string</span>
  ReleaseYear         <span class="hljs-keyword">int</span>
  ReleasedCurrentYear <span class="hljs-keyword">bool</span>
}
​
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">CustomValidateMovie</span><span class="hljs-params">(sl validator.StructLevel)</span></span> {
  movie := sl.Current().Interface().(Movie)
  <span class="hljs-keyword">if</span> movie.ReleasedCurrentYear != (movie.ReleaseYear == <span class="hljs-number">2024</span>) {
    sl.ReportError(movie.ReleaseYear, <span class="hljs-string">"release_year"</span>, <span class="hljs-string">"ReleaseYear"</span>, <span class="hljs-string">"release_info"</span>, <span class="hljs-string">""</span>)
    sl.ReportError(movie.ReleasedCurrentYear, <span class="hljs-string">"released_cur_year"</span>, <span class="hljs-string">"ReleasedCurrentYear"</span>, <span class="hljs-string">"release_info"</span>, <span class="hljs-string">""</span>)
  }
}
​
validate.RegisterStructValidation(CustomValidateMovie, Movie{})
​
<span class="hljs-comment">// Year "2024" CurYear "true"  - Pass</span>
<span class="hljs-comment">// Year "2024" CurYear "false" - Fail</span>
<span class="hljs-comment">// Year "2000" CurYear "true"  - Fail</span>
</code></pre>
<h2 id="heading-best-practices"><strong>Best Practices</strong></h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1723880032456/7d09b440-f78c-414e-b21f-4e3856543291.png" alt class="image--center mx-auto" /></p>
<p>In this section I offer two pieces of advice that may not seem obvious in the beginning.</p>
<h3 id="heading-fail-fast"><strong>Fail Fast</strong></h3>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Fail-fast_system">Fail fast</a> is a methodology that advices to fail (finish) your program as soon as fault (unresolvable/unexpected error) occurs and do not attempt to continue the work. It might sound weird to make your program give up each time it encounters an unexpected error, but when you try to debug the system that doesn't fail immediately after getting faulty it becomes clear why fail fast really pays off as a methodology. Practically, I recommend to do not continue model validation if some check has already failed. It's better to report about the error found as soon as possible indicating the primary issue properly.</p>
<h3 id="heading-avoid-global-state"><strong>Avoid Global State</strong></h3>
<p>Probably most of you already know that it's not a good practice to have global variables since it's difficult to manage them and to keep track of every change that happens with them. The same applies to <code>var validate *validator.Validate</code> — (usually) a global variable. I would recommend wrapping validation inside each module into function that should be called to validate any model from this module. It look like this:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> models
​
<span class="hljs-keyword">import</span> (
  <span class="hljs-string">"fmt"</span>
​
  <span class="hljs-string">"github.com/go-playground/validator/v10"</span>
)
​
<span class="hljs-keyword">var</span> validate *validator.Validate
​
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">init</span><span class="hljs-params">()</span></span> {
  validate = validator.New(validator.WithRequiredStructEnabled())
}
​
<span class="hljs-keyword">type</span> Movie <span class="hljs-keyword">struct</span> {
  Title <span class="hljs-keyword">string</span>
}
​
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Validate</span><span class="hljs-params">(obj <span class="hljs-keyword">interface</span>{})</span> <span class="hljs-title">error</span></span> {
  <span class="hljs-keyword">return</span> validate.Struct(obj)
}
​
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
  m := Movie{}
  err := Validate(m)
  fmt.Println(err)
}
</code></pre>
<h2 id="heading-additional"><strong>Additional</strong></h2>
<p>I didn't mention, but</p>
<ul>
<li><p>validator object (<code>validator.Validate</code>) is thread-safe</p>
</li>
<li><p>(obvious, but) validation works recursively and goes through all substructs</p>
</li>
<li><p>validation can access only exported methods and fields (ones starting with capital letters)</p>
</li>
<li><p>multiple validation tags are processed in the order defined</p>
</li>
<li><p>validation can be skipped on empty/nil values with <code>omitempty</code>, <code>omitnil</code> validation tags</p>
</li>
</ul>
<p>It's really hard to cover all the features that <a target="_blank" href="https://pkg.go.dev/github.com/go-playground/validator/v10">validator</a> library provides. And there is no such need. I've given you everything to make you feel comfortable when you start using it.</p>
<h2 id="heading-summary"><strong>Summary</strong></h2>
<p>For model validation in Golang I highly recommend using <a target="_blank" href="https://pkg.go.dev/github.com/go-playground/validator/v10">validator v10</a> library with vast number of features and built-in validation options. I've shown in practice how to use the library and gave particularly useful pieces of advice of how to get the most of it without burdening your system. If you have any questions or something in the article was unclear, please feel free to leave it in the comments.</p>
<p>After all, will you use the <a target="_blank" href="https://pkg.go.dev/github.com/go-playground/validator/v10">validator v10</a> library? Do you like its design (tags, how custom validation works, etc)? If no, then what problems have you found and do you see a better approach that can solve these problems?</p>
]]></content:encoded></item></channel></rss>