Working With Schemas

A schema is an object which contains a list of rules that will be applied to its input when you call a schema. It returns a result object which provides an API to retrieve error messages and access to the validation output.

Schema definition best practices:

  • Be specific about the exact shape of the data, define all the keys that you expect to be present
  • Specify optional keys too, even if you don’t need additional rules to be applied to their values
  • Specify type expectations for all the values!
  • Use custom predicates to keep things concise when built-in predicates create too much noise
  • Assign schema objects to constants for convenient access
  • Define a base schema for your application with common configuration

Calling a Schema

Calling a schema will apply all its rules to the input. High-level rules defined with the rule API are applied in a second step and they are guarded, which means if the values they depend on are not valid, nothing will crash and a high-level rule will not be applied.


schema = Dry::Schema.Params do

result = '', age: 21)

# access validation output data
# => {:email=>'', :age=>21}

# check if all rules passed
# => true

# check if any of the rules failed
# => false

Defining Base Schema Class

class AppSchema < Dry::Schema::Params
  config.messages_file = '/my/app/config/locales/en.yml'
  config.messages = :i18n

  define do
    # define common rules, if any

# now you can build other schemas on top of the base one:
class MySchema < AppSchema
  # define your rules

my_schema =

Working With Error Messages

The result object returned by Schema#call provides an API to convert error objects to human-friendly messages.

result = nil, age: 21)

# get default errors
# => {:email=>['must be filled']}

# get full errors
result.errors(full: true)
# => {:email=>['email must be filled']}

# get errors in another language
result.errors(locale: :pl)
# => {:email=>['musi być wypełniony']}

Using Validation Hints

In addition to error messages you can also access hints, which are generated from your rules. While errors tells you which predicate checks failed, hints tells you which additional predicate checks weren’t evaluated at all because an earlier predicate failed:

schema = Dry::Schema.Params do
  required(:age).filled(gt?: 18)
result = '', age: '')
# {:age=>['must be greater than 18']}

result = '', age: '')

# {:age=>['must be filled']}

# {:age=>['must be greater than 18']}
# hints takes the same options as errors:
result.hints(full: true)
# {:age=>['age must be greater than 18']}

You can also use messages to get a combination of both errors and hints:

result = '', age: '')
# {:age=>["must be filled", "must be greater than 18"]}

Learn more about customizing error and hint messages