Joi

Hex.pmBuild Docs

This project is inspired by sideway/joi and lob/litmus.

And the code of this repository is based on lob/litmus, but the API of this repository is completely different from litmus.

Background

The community already has a lot of verification-related libraries, such as skooma, vex, but why write a new one?

The api of vex is very much like Rails ActiveModel Validations, and it feels too complex for me, especially when it comes to customizing some validation modules, which is not convenient and flexible enough. Skooma, on the other hand, is very flexible and I find it particularly useful when validating non-map data structures.

So the goal of this repository is:

  1. Support most of the types supported by the native sideway/joi
  2. Nested validation support.
  3. Easy internationalization
  4. Easy to extend

Installation

def deps do
  [
    {:joi, "~> 0.2.0"},
  ]
end

Usage

Joi validates data against a predefined schema with the Joi.validate/2 function.

If the data is valid, the function returns {:ok, data}. The returned data will perform the transformation according to the provided schema.

if the passed data does not match the type defined in the schema, the function returns {:error, errors}, the errors is a list of Joi.Error, that a struct contains four fields:

, When a field is received that is not specified in the provided schema, it does nothing and returns {:ok, data}.


  iex> schema = %{a: [:integer]}
  %{a: [:integer]}
  iex> data1 = %{a: 1}
  %{a: 1}
  iex> Joi.validate(data1, schema)
  {:ok, %{a: 1}}
  iex> data2 = %{a: <<123>>}
  iex> Joi.validate(data2, schema)
  {:error,
  [
    %Joi.Error{
      context: %{key: :a, value: "{"},
      message: "a must be a integer",
      path: [:a],
      type: "integer.base"
    }
  ]}

Supported Types

Atom

error types

Boolean

error types

Date

error types

Datetime

error types

Decimal

error types

Float

error types

Integer

error types

List

list supports some special features, such as validation for each element:

iex> schema = %{l: [:list, type: :integer]}
%{l: [:list, {:type, :integer}]}
iex> Joi.validate(%{l: [<<123>>]}, schema)
{:error,
 [
   %Joi.Error{
     context: %{key: :l, value: ["{"]},
     message: "l must be a list of integer",
     path: [:l],
     type: "list.integer"
   }
 ]}

or a sub schema:

iex> sub_schema = %{key: [:integer]}
%{key: [:integer]}
iex> schema = %{l: [:list, schema: sub_schema]}
%{l: [:list, {:schema, %{key: [:integer]}}]}
iex> Joi.validate(%{l: [%{key: 1}, %{key: <<123>>}]}, schema)
{:error,
 [
   %Joi.Error{
     context: %{key: :key, value: "{"},
     message: "key must be a integer",
     path: [:l, 1, :key],
     type: "integer.base"
   }
 ]}

error types

Additional local context properties:

%{limit: integer()}

Additional local context properties:

%{limit: integer()}

Map

:map also supports validation with sub schema:

iex> sub_schema = %{sub_key: [:integer]}
%{sub_key: [:integer]}
iex> schema = %{m: [:map, schema: sub_schema]}
%{m: [:map, {:schema, %{sub_key: [:integer]}}]}
iex> Joi.validate(%{m: %{sub_key: <<123>>}}, schema)
{:error,
 [
   %Joi.Error{
     context: %{key: :sub_key, value: "{"},
     message: "sub_key must be a integer",
     path: [:m, :sub_key],
     type: "integer.base"
   }
 ]}

error types

String

error types

Custom functions

There is nothing magical about custom functions, you just need to return the same format as Joi's type, and then use :f as the key for the custom function in the schema, so you can use one or more custom functions inside a type.

iex> import Joi.Util
iex> func = fn field, data -> 
...>   case data[field] == 1 do
...>     true -> {:ok, data}
...>     false -> error("custom", path: [field], value: data[field], message: "does not match the custom function")
...>   end
...> end
iex> schema = %{id: [:integer, f: func]}
iex> data = %{id: 2}
iex> Joi.validate(data, schema)
{:error, [
  %Joi.Error{
    context: %{key: :id, message: "does not match the custom function", value: 2},
    message: "does not match the custom function",
    path: [:id],
    type: "custom"
  }
]}

Contributing

Feel free to dive in! Open an issue or submit PRS.

Joi follows the Contributor Covenant Code of Conduct.