{"id":20080634,"url":"https://github.com/bluzky/tarams","last_synced_at":"2025-04-05T07:06:01.209Z","repository":{"id":38304235,"uuid":"277786809","full_name":"bluzky/tarams","owner":"bluzky","description":"Cast and validate external data and request parameters for Elixir and Phoenix","archived":false,"fork":false,"pushed_at":"2024-02-27T02:15:35.000Z","size":111,"stargazers_count":115,"open_issues_count":3,"forks_count":12,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T06:06:47.087Z","etag":null,"topics":["elixir","params","params-parser","params-validate","params-validator","phoenix","tarams"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/tarams/","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bluzky.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-07-07T10:30:17.000Z","updated_at":"2025-02-17T11:39:45.000Z","dependencies_parsed_at":"2024-02-27T03:25:53.970Z","dependency_job_id":"c4edc30b-7a8c-4f69-9cc3-d2dae721c33e","html_url":"https://github.com/bluzky/tarams","commit_stats":{"total_commits":51,"total_committers":8,"mean_commits":6.375,"dds":"0.47058823529411764","last_synced_commit":"51bc86879b51be683a4bc85bbe3fc8471a4d8a1b"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluzky%2Ftarams","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluzky%2Ftarams/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluzky%2Ftarams/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluzky%2Ftarams/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bluzky","download_url":"https://codeload.github.com/bluzky/tarams/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247299832,"owners_count":20916190,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elixir","params","params-parser","params-validate","params-validator","phoenix","tarams"],"created_at":"2024-11-13T15:29:23.941Z","updated_at":"2025-04-05T07:06:01.188Z","avatar_url":"https://github.com/bluzky.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tarams\n\nPhoenix request params validation library.\n\n[![Build Status](https://github.com/bluzky/tarams/workflows/Elixir%20CI/badge.svg)](https://github.com/bluzky/tarams/actions) [![Coverage Status](https://coveralls.io/repos/github/bluzky/tarams/badge.svg?branch=master)](https://coveralls.io/github/bluzky/tarams?branch=master) [![Hex Version](https://img.shields.io/hexpm/v/tarams.svg)](https://hex.pm/packages/tarams) [![docs](https://img.shields.io/badge/docs-hexpm-blue.svg)](https://hexdocs.pm/tarams/)\n\n\n**Warning: Tarams v1.0.0 APIs is not back compatible**\n\n- [Tarams](#tarams)\n    - [Why Tarams](#why-tarams)\n    - [Installation](#installation)\n    - [Usage](#usage)\n    - [Define schema](#define-schema)\n        - [Default value](#default-value)\n        - [Custom cast function](#custom-cast-function)\n            - [1. Custom cast function accept value only](#1-custom-cast-fuction-accept-value-only)\n            - [2. Custom cast function accept value and current object](#2-custom-cast-function-accept-value-and-current-object)\n            - [3.Custom cast function accept tuple {M, f}](#3custom-cast-function-accept-tuple-m-f)\n        - [Nested schema](#nested-schema)\n    - [Transform data](#transform-data)\n        - [Field name alias](#field-name-alias)\n        - [Convert data](#convert-data)\n    - [Validation](#validation)\n    - [Contributors](#contributors)\n\n\n## Why Tarams\n    - Reduce code boilerplate \n    - Shorter schema definition\n    - Default function which generate value each casting time\n    - Custom validation functions\n    - Custom parse functions\n    \n## Installation\n\n[Available in Hex](https://hex.pm/tarams), the package can be installed\nby adding `tarams` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:tarams, \"~\u003e 1.0.0\"}\n  ]\nend\n```\n\n## Usage\n\n**Process order**\n\u003e Cast data -\u003e validate casted data -\u003e transform data\n\n```elixir\n@index_params_schema  %{\n    keyword: :string,\n    status: [type: :string, required: true],\n    group_id: [type: :integer, number: [greater_than: 0]],\n    name: [type: :string, from: :another_field]\n  }\n\ndef index(conn, params) do\n    with {:ok, better_params} \u003c- Tarams.cast(params, @index_params_schema) do\n        # do anything with your params\n    else\n        {:error, errors} -\u003e # return params error\n    end\nend\n```\n\n\n## Define schema\n\nSchema is just a map and it can be nested. Each field is defined as\n\n`\u003cfield_name\u003e: [\u003cfield_spec\u003e, ...]`\n\nOr short form\n\n`\u003cfield_name\u003e: \u003ctype\u003e`\n\nField specs is a keyword list thay may include:\n\n- `type` is required, `Tarams` support same data type as `Ecto`. I borrowed code from Ecto\n- `default`: default value or default function\n- `cast_func`: custom cast function\n- `number, format, length, in, not_in, func, required, each` are available validations\n- `from`: use value from another field\n- `as`: alias key you will receive from `Tarams.cast` if casting is succeeded\n\n\n### Default value\nYou can define a default value for a field if it's missing from the params.\n\n```elixir\nschema = %{\n    status: [type: :string, default: \"pending\"]\n}\n```\n\nOr you can define a default value as a function. This function is evaluated when `Tarams.cast` gets invoked.\n\n```elixir\nschema = %{\n    date: [type: :utc_datetime, default: \u0026Timex.now/0]\n}\n```\n\n### Custom cast function\nYou can define your own casting function, `tarams` provide `cast_func` option.\nYour `cast_func` must follows this spec \n\n#### 1. Custom cast fuction accept value only\n\n```elixir\nfn(any) :: {:ok, any} | {:error, binary} | :error\n```\n\n```elixir\ndef my_array_parser(value) do\n    if is_binary(value) do\n        ids = \n            String.split(value, \",\")\n            |\u003e Enum.map(\u0026String.to_integer(\u00261))\n        \n        {:ok, ids}\n    else\n        {:error, \"Invalid string\"\n    end\nend\n\nschema = %{\n    user_id: [type: {:array, :integer}, cast_func: \u0026my_array_parser/1]\n}\n\nTarams.cast(%{user_id: \"1,2,3\"}, schema)\n```\nThis is a demo parser function.\n\n#### 2. Custom cast function accept value and current object\n\n```elixir\ndata = %{\n   name: \"tada\",\n   bold: true\n}\n\nschema = %{\n    name: [type: :string, cast_func: fn value, data -\u003e \n        {:ok, (if data.bold, do: String.upcase(value), else: value)}\n    end]\n}\n\nTarams.cast(data, schema)\n\n# \u003e %{name: \"TADA\"}\n```\n\n#### 3.Custom cast function accept tuple {M, f}\n\nYour cast function must accept 2 arguments\n\n```elixir\ndefmodule MyModule do\n    def upcase(value, data) do\n        {:ok, (if data.bold, do: String.upcase(value), else: value)}\n    end\nend\n```\n\n```elixir\ndata = %{\n   name: \"tada\",\n   bold: true\n}\n\nschema = %{\n    name: [type: :string, cast_func: {MyModule, :upcase}]\n}\n\nTarams.cast(data, schema)\n\n# \u003e %{name: \"TADA\"}\n```\n\n\n### Nested schema\nWith `Tarams` you can parse and validate nested map and list easily\n\n```elixir\n@my_schema %{\n    status: :string,\n    pagination: %{\n        page: [type: :integer, number: [min: 1]],\n        size: [type: :integer, number: [min: 10, max: 100\"]]\n    }\n}\n```\n\nOr nested list schema\n\n```elixir\n@user_schema %{\n    name: :string,\n    email: [type: :string, required: true]\n    addresses: [type: {:array, %{\n        street: :string,\n        district: :string,\n        city: :string\n    }}]\n}\n```\n\n\n## Validation\n\n`Tarams` uses `Valdi` validation library. You can read more about [Valdi here](https://github.com/bluzky/valdi)\nBasically it supports following validation\n\n- validate inclusion/exclusion\n- validate length for string and enumerable types\n- validate number\n- validate string format/pattern\n- validate custom function\n- validate required(not nil) or not\n- validate each array item\n\n\n\n  ```elixir\n  product_schema = %{\n    sku: [type: :string, required: true, length: [min: 6, max: 20]]\n    name: [type: :string, required: true],\n    quantity: [type: :integer, number: [min: 0]],\n    type: [type: :string, in: ~w(physical digital)],\n    expiration_date: [type: :naive_datetime, func: \u0026my_validation_func/1],\n    # dynamic required\n    width: [type: :integer, required: fn value, data -\u003e data.type == \"physical\" end],\n    # validate each array item\n    tags: [type: {:array, :string}, each: [length: [max: 50]]]\n  }\n  ```\n\n### Dynamic required\n- Can accept function or `{module, function}` tuple\n- Only support 2 arity function\n\n\n```elixir\ndef require_email?(value, data), do: is_nil(email.phone)\n\n....\n\n%{\n    phone: :string\n    name: [type: :string, required: fn value, data -\u003e true end],\n    email: [type: :string, required: {__MODULE__, :require_email?}]\n}\n```\n\n### Validate array item\nSupport validate array item with `:each` option, `each` accept a list of validators\n\n```elixir\n%{\n    values: [type: {:array, :number}, each: [number: [min: 20, max: 50]]]\n  }\n```\n\n\n\n## Transform data\n\n### Field name alias\n\nYou can set alias name for schema fields\n```elixir\ndata = %{\n   name: \"tada\"\n}\n\nschema = %{\n    name: [type: :string, as: :full_name]\n}\n\nTarams.cast(data, schema)\n\n# \u003e %{full_name: \"tada\"}\n```\n\n### Convert data\n\nYou can specify a function similar to `cast_func` to manipulate data after casted.\nHowever data object passed to transform function is original data before casting.\n\n```elixir\ndata = %{status: 10}\n\nschema = %{\n    name: [type: :string, into: fn value -\u003e {:ok, \"name: #{value}}\" end]\n}\n\nTarams.cast(data, schema)\n# \u003e %{name: \"name: tada\"}\n\n```\n\n- Transform function can return tuple `{:ok, value}`, `{:error, message}` or value directly.\n\n```elixir\nschema = %{\n    value: [type: :integer, into: \u0026to_string/1]\n}\n```\n  \n\n## Contributors\nIf you find a bug or want to improve something, please send a pull request. Thank you!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluzky%2Ftarams","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbluzky%2Ftarams","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluzky%2Ftarams/lists"}