{"id":17771653,"url":"https://github.com/akoutmos/pharams","last_synced_at":"2025-07-22T18:33:39.915Z","repository":{"id":57531347,"uuid":"148206841","full_name":"akoutmos/pharams","owner":"akoutmos","description":"Parameter validation for Elixir Phoenix Framework","archived":false,"fork":false,"pushed_at":"2020-04-27T00:52:52.000Z","size":68,"stargazers_count":36,"open_issues_count":2,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-22T03:06:06.920Z","etag":null,"topics":["elixir","phoenix-framework"],"latest_commit_sha":null,"homepage":"","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/akoutmos.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["akoutmos"]}},"created_at":"2018-09-10T19:19:04.000Z","updated_at":"2025-05-16T00:53:20.000Z","dependencies_parsed_at":"2022-09-06T16:30:43.969Z","dependency_job_id":null,"html_url":"https://github.com/akoutmos/pharams","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/akoutmos/pharams","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akoutmos%2Fpharams","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akoutmos%2Fpharams/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akoutmos%2Fpharams/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akoutmos%2Fpharams/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/akoutmos","download_url":"https://codeload.github.com/akoutmos/pharams/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akoutmos%2Fpharams/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266552381,"owners_count":23947173,"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","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","phoenix-framework"],"created_at":"2024-10-26T21:34:36.524Z","updated_at":"2025-07-22T18:33:39.878Z","avatar_url":"https://github.com/akoutmos.png","language":"Elixir","funding_links":["https://github.com/sponsors/akoutmos"],"categories":[],"sub_categories":[],"readme":"# Pharams\n\n[![Hex.pm](https://img.shields.io/hexpm/v/pharams.svg)](http://hex.pm/packages/pharams)\n\n**WORK IN PROGRESS**\n\nDefine Phoenix parameter validators declaratively. Under the hood, the Pharams macros make use of Ecto.Schema and as a results, errors are provided in the form of changesets. These error changesets can then be sent to any error view of your choosing (Pharams comes with a very basic error view out of the box). In addition, the usage of the `pharams` macro will inject a Plug in your controller so that the `params` variable passed to your Phoenix controller action function has already already been validated. The `params` variable passed to your function controller is a map which mirrors your schema with atoms as keys.\n\n## Installation\n\n[Available in Hex](https://hex.pm/packages/pharams), the package can be installed\nby adding `pharams` to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:pharams, \"~\u003e 0.10.0\"}\n  ]\nend\n```\n\nIf you would like the formatter to not add parenthesis to `required` and `optional` you can add the following to you `.formatter.exs`:\n\n```\n[\n  import_deps: [:pharams]\n]\n```\n\nDocumentation can be found at [https://hexdocs.pm/pharams](https://hexdocs.pm/pharams).\n\n## Usage\n\nTo get started, add the following to your Phoenix Controller:\n\n```elixir\nuse Pharams\n```\n\nIf you would like to configure the Pharams module to use a different error view or status code you can do the following (shown with defaults):\n\n```elixir\n# error_status takes an atom (see https://github.com/elixir-plug/plug/blob/master/lib/plug/conn/status.ex for full list of supported statuses)\nuse Pharams, view_module: Pharams.ErrorView, view_template: \"errors.json\", error_status: :unprocessable_entity, key_type: :atom, drop_nil_fields: false\n```\n\nNext you can use the `pharams` macro to define the validator that should be used against incoming requests. Below is a simple example:\n\n```elixir\npharams :index do\n  required :terms_conditions, :boolean\n  required :password, :string\n  required :password_confirmation, :string\n  optional :age, :integer\nend\n\ndef index(conn, params) do\n  # You will only get into this function if the request\n  # parameters have passed the above validator. The params\n  # variable is now just a map with atoms as keys.\n\n  render(conn, \"index.html\")\nend\n```\n\nLet's break down what is happening here. Before the `do` block, we specify to which action the validator will apply. In this case the `:index` atom is provided because we want the validation to take place against the `def index` action in the controller. Next, we define which fields are required/optional in the request, and what their types are. Simple enough so far :), let's look at a more complex example.\n\nIn the following example, we will create a validator with a bit more functionality:\n\n```elixir\npharams :index do\n  required :terms_conditions, :boolean, acceptance: []\n  required :password, :string, confirmation: [], length: [min: 8, max: 16]\n  required :password_confirmation, :string\n  required :age, :integer, number: [greater_than: 16, less_than: 110]\n\n  optional :interests, {:array, :string},\n    subset: [\"art\", \"music\", \"technology\"],\n    length: [max: 2]\n\n  optional :favorite_programming_language, :string,\n    exclusion: ~w(Java Perl PHP),\n    default: \"Elixir\"\n\n  required :addresses, :one do\n    required :billing_address, :one do\n      required :street_line_1, :string\n      optional :street_line_2, :string\n      required :zip_code, :string, format: ~r/\\d{5}/\n\n      required :coordinates, :one do\n        required :lat, :float\n        required :long, :float\n      end\n    end\n\n    required :shipping_address, :one do\n      required :street_line_1, :string\n      optional :street_line_2, :string\n      required :zip_code, :string, format: ~r/\\d{5}/\n\n      required :coordinates, :one do\n        required :lat, :float\n        required :long, :float\n      end\n    end\n  end\nend\n\ndef index(conn, params) do\n  # You will only get into this function if the request\n  # parameters have passed the above validator. The params\n  # variable is now just a map with atoms as keys.\n\n  render(conn, \"index.html\")\nend\n```\n\nLet's break this one down as well to make things clearer. As you can see, you can also have embedded `required` and `optional` schemas in your validator declaration (for example the `:addresses` schema contains both `:billing_address` and `:shipping_address` schemas). You can also pass in a keyword list of additional options for fields. For example the `:favorite_programming_language` field can have a default value of `Elixir`, and using the `Ecto.Changeset.validate_exclusion/4` via the `exclusion: ~w(Java Perl PHP)` keyword entry, you can validate that the string is not in the provided list. All of the `Ecto.Changeset.validate_*` functions are supported, with the caveat that you call them without their `validate_` prefix. You can also get fairly involved with your validations using the `validate_change` Ecto.Changeset function:\n\n```elixir\npharams :index do\n  required(:rand, :string,\n    change: [\n      :something,\n      fn :rand, rand -\u003e\n        if rand == \"foo\" do\n          [rand: \"cannot be foo\"]\n        else\n          []\n        end\n      end\n    ]\n  )\nend\n```\n\nWhen using the `Ecto.Changeset.validate_*/3` functions, all you have to provide is the last argument. The Pharams macro will populate the first two parameters under the hood for you. When using the `Ecto.Changeset.validate_*/4` functions, you have to pass the last two parameters as a list. See below the two different uses of `validate_subset`:\n\n```elixir\n# Using Ecto.Changeset.validate_subset/3\noptional(:interests, {:array, :string},\n  subset: [\"art\", \"music\", \"technology\"],\n  length: [max: 2]\n)\n\n# Using Ecto.Changeset.validate_subset/4\noptional(:interests, {:array, :string},\n  subset: [[\"art\", \"music\", \"technology\"], [message: \"Only 3 cool interests to pick from!\"]],\n  length: [max: 2]\n)\n```\n\nAnother thing to note in this example is that multiple `Ecto.Changeset.validate_*` functions can be applied to a specific field. In this example, both `validate_length` and `validate_subset` are used.\n\nOne of last things to cover is that you have the ability to define whether an embedded schema is only embedded once or many times. See the following examples:\n\n```elixir\npharams :index do\n  required :item, :one do\n    required :quantity, :integer, number: [greater_than: 0, less_than: 100]\n    required :item_id, Ecto.UUID\n  end\nend\n\n# Versus\n\npharams :index do\n  required :items, :many do\n    required :quantity, :integer, number: [greater_than: 0, less_than: 100]\n    required :item_id, Ecto.UUID\n  end\nend\n```\n\nWith the former, your params will look like so:\n\n```elixir\n%{\n  item: %{\n    item_id: \"7c1bb0bf-8f31-4dbc-88c5-568f11ab7443\",\n    quantity: 5\n  }\n}\n```\n\nWhile with the latter, your params will like like this:\n\n```elixir\n%{\n  items: [\n    %{\n      item_id: \"7c1bb0bf-8f31-4dbc-88c5-568f11ab7443\",\n      quantity: 5\n    }\n  ]\n}\n```\n\nIf you would like to drop nil or empty fields from your params (empty fields meaning `nil, %{}, \"\", and []`) then you can either set the option on the individual route like so:\n\n```\npharams :update, drop_nil_fields: true do\n  required(:id, :integer)\n  optional(:quantity, :integer)\n  optional(:delivery_date, :string)\n  optional(:cancel_order, :boolean, default: false)\nend\n```\n\nOr add the same `drop_nil_fields: true` option when you use the Pharams module up at the top of your controller. This would in turn apply that configuration to all pharams calls. As a note, setting it at the individual endpoint level overrides controller wide configuration.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakoutmos%2Fpharams","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakoutmos%2Fpharams","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakoutmos%2Fpharams/lists"}