The Neuronen Schmiede concerns itself with topics related to software development. One focus are robust web applications.

Validations in a Lotus JSON API app

Permalink

One thing that really stuck with me while reading/watching/learning about Uncle Bobs Clean Architecture was the idea that invalid data should not cross the boundary between the delivery mechanism and the application code.1 As a consequence the application code is free of validations and only works with proper data.

To illustrate why this is a desirable result we take a look at Ruby on Rails. Let's assume we have a web form and users can sign up. They have to provide an email address and a password. In a vanilla Ruby on Rails project you would have an ActiveRecord model Identity and add two presence validations to it. While this works for this case it starts breaking when you add the ability for admins to create new users without providing a password. Now you have to start mutilating your application code by skipping validations in certain places.

Having to skip validations is a sign that validating data occurs in the wrong place.

The correct place to validate that an email and a password are provided is at the boundary to the application. The user action has a different entry point than the admin action. Therefore it is easy to specify the proper validation rules for each entry point and prevent invalid data from entering the system. Lotus has this mechanism built into its controller actions. These actions are the entry points to your application when HTTP is used as a delivery mechanism.

Action that requires both email and password parameter

require 'lotus/controller'

class Create
  include Lotus::Action

  params do
    param :identity do
      param :email, type: String, presence: true
      param :password, type: String, presence: true
    end
  end

  def call(params)
    halt 422 unless params.valid?

    # Work with proper parameters
  end
end

Even when you don't know Lotus the code above should be understandable. We specify that we expect the parameters identity[email] and identity[password]. First of all this acts as a whitelist, parameters not specified get stripped before they enter the application. type: String tells Lotus to coerce the parameter, your application code does not have to be bothered with type checks because the data will always have the correct type. The final part is the presence validation making sure that the parameters are actually present.

Should the validations fail we halt the request and return a 422 Unprocessable Entity response. This is exactly what we want in terms of incoming data validations. Adding an admin action where the password is not required is easy because the validations happen at the boundary and not inside the application.

Since this article has “JSON” in the title we are not finished yet. In order to show the user what went wrong most JavaScript frameworks expect a response with the errors.2 If you are using Ember.js with Ember Data like me the expected response looks like this:

{
  "errors": {
    "email": ["presence"],
    "password": ["presence"]
  }
}

How can we produce such a response in Lotus? Simple, we pass halt a message as second argument, this will be the body of our response. In order to achieve this in a clean way we write a small module and a class. The module will hold our unified logic for halting the request if the parameters are not valid. The class serializes the Lotus validations errors object into a consumable format. (If you know a better way to accomplish this I would love to hear from you.)

require 'json'

module ErrorSerializers
  class LotusValidationsErrors
    def initialize(errors)
      @errors = errors
    end

    def to_json
      JSON.generate({ errors: errors_hash })
    end

    private
    def errors_hash
      @errors_hash ||= @errors.to_h.inject({}) { |hash, (key, value)| hash.merge({ value.first.attribute_name.to_sym => value.map(&:validation) }) }.to_h
    end
  end
end
require_relative '../error_serializers/lotus_validations_errors'

module ParameterValidation
  private
  def validate!
    halt_with_errors unless params.valid?
  end

  def halt_with_errors
    halt 422, ErrorSerializers::LotusValidationsErrors.new(errors).to_json
  end
end

Having these two pieces of code in place we can include the module in our action and add an before callback to invoke the validate! method. Now we can clean up the call method and remove the guard condition. At this point the action works again as we want and it is easy to add the same behaviour to other actions. Using controller.prepare you can easily add it by default to all actions.

Refactored action using the ParameterValidation module and a before callback

require 'lotus/controller'
require_relative '../parameter_validation'

class Create
  include Lotus::Action
  include ParameterValidation

  before :validate!

  params do
    param :identity do
      param :email, type: String, presence: true
      param :password, type: String, presence: true
    end
  end

  def call(params)
    # Work with proper parameters
  end
end
  1. A quick search did not turn up a resource where Uncle Bob himself talks about this. In Adam Hawkins article about Form Objects with Virtus Avdi Grimm is mentioned in the context of this topic. But I do believe Adam Hawkins article series about software architecture aligns well with Clean Architecture.

  2. If you are building a JavaScript application and you have client side validations that's great. You still want your backend to decide what is valid data and what not. Therefore you should communicate the errors to the client and show some meaningful help to your user. Otherwise it can happen that the client side validations pass but the backend rejects the data due to different requirements and the user is left alone without any clue what is actually happening.