Readable and Customizable DSL

skinny-validator is portable, so you can use skinny-validator with Play2, Scalatra and any other web app frameworks. Furthermore, you can use it in not only web apps but also any other applications (batch operation, cli and so on).

You can see simple usage of skinny-validator here:


Creating New Validation Rule

The way to create new validation rule is pretty simple:

That’s all.

The following is an example from the built-in validations.

import skinny.validator._

object required extends required(true)

case class required(trim: Boolean = true) extends ValidationRule {
  def name = "required"
  def isValid(v: Any) = !isEmpty(v) && {
    if (trim) v.toString.trim.length > 0
    else v.toString.length > 0

Then you can use it like this:

val validator = Validator(
  param("name" -> null) is required
validator.hasErrors // true

val errorsForName: Seq[Error] = validator.errors.get("name")
val error: Error = errorsForName.head // -> "required"
error.messageParams // -> List("name")

A validation rule which accepts value when using such as minLength(6) is like this:

case class minLength(min: Int) extends ValidationRule {
  def name = "minLength"
  override def messageParams = Seq(min.toString)

  def isValid(v: Any) = isEmpty(v) || {
    toHasSize(v).map(_.size >= min).getOrElse(v.toString.length >= min)

val validator = Validator(
  param("name" -> "xxxx") is checkAll(required, minLength(6)),
  param("password" -> "xxxx") is required & minLength(6)

Basic usage involves combining validation rules with &. If a failure is found, by default the rest of the validation rules will be skipped. checkAll executes all the validations even if some of them already failed.

Built-in Validation Rules

The built-in validators are good enough to cover most common cases.



Validator & MapValidator

skinny-validator provides Validator and MapValidator. You already see Validator in above code. This one accepts parameters when defining validation rules.

MapValidator is another one which accepts Map[String, Any] object as parameters. Usually MapValidator is more useful in web applications.

val params = Map("name" -> "Alice", "age" -> 20)

val validator = MapValidator(params)(
  paramKey("name") is required,
  paramKey("age") is required & numeric

Error Messages

skinny-validator’s Error looks like this:

val error = validator.errors.head // -> required
error.messageParams // -> List("name")

skinny.validator.Messages loads error messages from *.conf or *.properties.

This is src/main/resources/messages.conf example:

error {
  required="{0} is required"
  minLength="{0} length must be greater than or equal to {1}"

Now Messages can load error messages for validation errors named “required” or “minLength”. The name comes from ValidationRule#name.

val messages = Messages.loadFromConfig()
// val messages = Messages.loadFromConfig("messages2") // loads messages2.conf
// val messages = Messages.loadFromProperties() // loads
// val messages_ja = Messages.loadFromConfig(locale = Option(locale)) // loads messages_ja.conf

messages.get("required", Seq("name")) 
// -> Some("name is required")

messages.get("minLength", Seq("password", 6)) 
// -> Some("password length must be greater than or equal to 6")

How It Works in Skinny apps

You can easily understand how skinny-validator works in Skinny apps.


The following is an example with Skinny Framework:

// withDate joins birthdayYear, birthdayMonth and birthdayDay into single birthday
def createParams = Params(params).withDate("birthday")

def createForm = validation(createParams,
  paramKey("name") is checkAll(minLength(5), maxLength(20)),
  paramKey("birthday") is required & dateFormat,
  paramKey("point") is numeric

def createStrongParameters = Seq(
  "name" -> ParamType.String,
  "birthday" -> ParamType.LocalDate,
  "point" -> ParamType.Int

def create = {
  if (createForm.validate) {
    val id = Member.createNewModel(createParams.permit(createStrongParameters))
  } else {
    // error messages is set as "errorMessages" and "keyAndErrorMessages"
  // or using #fold

See also:



If you find a typo or mistake in this page, please report or fix it. How?