Data validation in elixir


Keywords
elixir, elixir-lang, parameters, plug, schema, validation, validator
License
MIT

Documentation

Litmus

Hex.pm Build Docs Build Status

Data validation in Elixir

Installation

The package can be installed by adding litmus to your list of dependencies in mix.exs:

def deps do
  [
    {:litmus, "~> 1.0.1"}
  ]
end

Usage

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

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

If the data passed does not follow the rules defined in the schema, the function returns {:error, error_message}. It will also return an error when receiving a field that has not been specified in the provided schema.

schema = %{
  "id" => %Litmus.Type.Any{
    required: true
  },
  "username" => %Litmus.Type.String{
    min_length: 6,
    required: true
  },
  "pin" => %Litmus.Type.Number{
    min: 1000,
    max: 9999,
    required: true
  },
  "new_user" => %Litmus.Type.Boolean{
    truthy: ["1"],
    falsy: ["0"]
   },
  "account_ids" => %Litmus.Type.List{
    max_length: 3,
    type: :number
  },
  "remember_me" => %Litmus.Type.Boolean{
    default: false
  }
}

params = %{
  "id" => 1,
  "username" => "user@123",
  "pin" => 1234,
  "new_user" => "1",
  "account_ids" => [1, 3, 9]
}

Litmus.validate(params, schema)
# => {:ok,
#      %{
#        "id" => 1,
#        "new_user" => true,
#        "pin" => 1234,
#        "username" => "user@123",
#        "account_ids" => [1, 3, 9],
#        "remember_me" => false
#      }
#    }

Litmus.validate(%{}, schema)
# => {:error, "id is required"}

Supported Types

Litmus currently supports the following types.

  • Litmus.Type.Any
  • Litmus.Type.Boolean
  • Litmus.Type.DateTime
  • Litmus.Type.List
  • Litmus.Type.Number
  • Litmus.Type.String

Plug Integration

Litmus comes with a Plug for easy integration with Plug's built-in router. You can automatically validate query parameters and body parameters by passing the litmus_query and litmus_body private options to each route. When declaring the plug you must include a on_error/2 function to be called when validation fails. It is recommended that you initialize this Plug between the :match and :dispatch plugs. If you want processing to stop on a validation error, be sure to halt the request with Plug.Conn.halt/1.

Example

defmodule MyRouter do
  use Plug.Router

  plug(Plug.Parsers, parsers: [:urlencoded, :multipart])

  plug(:match)

  plug(Litmus.Plug, on_error: &__MODULE__.on_error/2)

  plug(:dispatch)

  @schema %{
    "id" => %Litmus.Type.Number{
      required: true
    }
  }

  get "/test", private: %{litmus_query: @schema} do
    Plug.Conn.send_resp(conn, 200, "items")
  end

  post "/test", private: %{litmus_body: @schema} do
    Plug.Conn.send_resp(conn, 200, "items")
  end

  def on_error(conn, error_message) do
    conn
    |> Plug.Conn.send_resp(400, error_message)
    |> Plug.Conn.halt()
  end
end