Codementor Events

Using Dry Validation for validating Ruby on Rails Controller params

Published May 27, 2022Last updated May 30, 2022

I have being using Joi Validation for almost all my node project, getting to Rails, I am not always happy with how I validate writing different functions to validate request and sometimnes, it makes my controller bloated. I am not always happy about this, little did I know there is a gem for that.

In all my Node APIs, I usually create schema, add the schema to the middleware, to validate incoming requests and throws error if some rules are not followed. The good thing about Joi is that it has lot of functions to validate if something is a number, string etc.

I have been looking for a rails version of JOI but I have not been able to get, until today I was reading an article on Microservice in rails and a gem was mentioned which I check out and viola!! it has all what I need.

dry-validation is a data validation library that provides a powerful DSL for defining schemas and validation rules. It is very easy to use, define your contracts and call it in your controller. Sound Easy right.

An example of what I have been doing before I start using the gem, what if I have lot of parameter to validate, is the parameter a date, is start_date greater than end_date and the likes, the file will become bloated. Some can say I can create a module for that, but it still look dirty to me.

I moved my validation to a library that makes everything easy.

How to Setup⌗

## Gemfile gem 'dry-validation'

And then run bundle:

$ bundle

After this, you are good to go.

The next thing is to create a contracts.

# app/contracts/profile_contract.rb
class ProfileContract < Dry::Validation::Contract 
    params do 
       required(:email).filled(:string)
       required(:username).filled(:string)
       required(:accent).filled(:string) 
    end 
    rule(:email) do
       unless /\A[\w+\-.][email protected][a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i.match?(value) 
          key.failure('has invalid format') 
       end 
   end
   values = ['blue', 'yellow']
   rule(:accent) do 
      key.failure('must be one of Blue or Yellow') if values.include value 
   end
end

In our controller, we can easily call the contract and do our validation.

class profileController < ApplicationController
    def create
       contract = ProfileContract.new
       contract.call(profile_params) 
       render :json { errors: contract.errors } if contract.errors 
       ## Save 
    end
end

Thats all, it makes the controller lean and has a railsey way of doing things. I love Rails. If you have a question, you can reach me on github. danielshow

Discover and read more posts from Daniel Shotonwa
get started