{"id":13508011,"url":"https://github.com/vic/params","last_synced_at":"2025-05-14T11:12:50.692Z","repository":{"id":3604259,"uuid":"50279431","full_name":"vic/params","owner":"vic","description":"Easy parameters validation/casting with Ecto.Schema, akin to Rails' strong parameters.","archived":false,"fork":false,"pushed_at":"2025-02-27T01:25:28.000Z","size":103,"stargazers_count":368,"open_issues_count":7,"forks_count":50,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-04-11T04:56:23.110Z","etag":null,"topics":["ecto","elixir","parameters","phoenix"],"latest_commit_sha":null,"homepage":"http://hex.pm/packages/params","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vic.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,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-01-24T08:38:50.000Z","updated_at":"2025-03-16T05:02:06.000Z","dependencies_parsed_at":"2024-01-31T07:04:53.131Z","dependency_job_id":"61b8c982-f01c-4277-a902-765aceb5891c","html_url":"https://github.com/vic/params","commit_stats":{"total_commits":87,"total_committers":16,"mean_commits":5.4375,"dds":0.4022988505747126,"last_synced_commit":"da56794c887036c28f63841551ddace8b5865d14"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fparams","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fparams/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fparams/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vic%2Fparams/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vic","download_url":"https://codeload.github.com/vic/params/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254129527,"owners_count":22019628,"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":["ecto","elixir","parameters","phoenix"],"created_at":"2024-08-01T02:00:45.412Z","updated_at":"2025-05-14T11:12:50.657Z","avatar_url":"https://github.com/vic.png","language":"Elixir","funding_links":[],"categories":["Framework Components"],"sub_categories":[],"readme":"# Params \n\n[Looking for maintainer](https://github.com/vic/params/issues/new?title=Becoming%20a%20maintainer)\n\n[![Hex Version](https://img.shields.io/hexpm/v/params.svg)](https://hex.pm/packages/params)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/params/)\n[![Total Download](https://img.shields.io/hexpm/dt/params.svg)](https://hex.pm/packages/params)\n[![License](https://img.shields.io/hexpm/l/params.svg)](https://github.com/vic/params/blob/master/LICENSE)\n[![Last Updated](https://img.shields.io/github/last-commit/vic/params.svg)](https://github.com/vic/params/commits/master)\n\nEasily define parameter structure and validate/cast with [Ecto.Schema][Ecto.Schema]\n\n- [About](#about)\n- [Installation](#installation)\n- [Usage](#usage)\n- [API Documentation](https://hexdocs.pm/params/)\n\n## Installation\n\n[Available in Hex](https://hex.pm/packages/params), the package can be installed as:\n\nAdd params to your list of dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:params, \"~\u003e 2.0\"}\n  ]\nend\n```\n\n## About\n\nIf you've been doing [Ecto][Ecto] based applications lately,\nyou know Ecto provides a very easy way to populate structs with data coming\nfrom request parameters, validating and casting their values along the way.\n\nAll this thanks to the [Ecto.Schema][Ecto.Schema] and [Ecto.Changeset][cast] modules.\nThe first specifies the fields your model has (typically the same as your db table)\nand the later provides an easy way to convert potentially unsafe data and validate\nstuff via changesets.\n\nSo for example, in a typical [Phoenix][Phoenix] application, a `User` model\nwould look like:\n\n```elixir\ndefmodule MyApp.User do\n   use MyApp.Web, :model\n\n   schema \"users\" do\n     field :name, :string\n     field :age,  :integer\n   end\n\n   @required [:name]\n   @optional [:age]\n\n   def changeset(changeset_or_model, params) do\n     cast(changeset_or_model, params, @required ++ @optional)\n     |\u003e validate_required(@required)\n   end\nend\n```\n\nNormally, changesets are related to some data that will be persisted into\na database, and your controller would use the `User.changeset` method like:\n\n```elixir\n# UserController.ex\ndef create(conn, params) do\n  ch = User.changeset(%User{}, params)\n  if ch.valid? do\n    ...\nend\n```\n\nHowever, you can use `Ecto.Schema` for validating/casting data that\n*won't necessarily* be persisted into a database. All you need is just specify a module and\ndefine your schema, [Ecto.Changeset][cast] will be happy to work with it.\n\nThis comes handy when you have certain parameter structure you want\nto enforce for example when creating a REST API.\n\nSome Rails developers might be right now wondering where their\n_strong parameters_ can be defined. On Elixir land, there's no need for such a thing, as we will see, just using an `Ecto.Schema` with `Ecto.Changeset`\ncan be much more flexible. Using schemas allows not only\nspecifying which fields we want, but changesets let use\ntype cast, perform validations on values, etc.\n\nSo, for example, suppose your Phoenix based API performs a search for kittens looking for a\nhome and expects something like:\n\n```json\n{\n  \"breed\": \"Russian Blue\",\n  \"age_min\": 0,\n  \"age_max\": 5,\n  \"near_location\": {\n     \"latitude\": 92.1,\n     \"longitude\": -82.1\n  }\n}\n```\n\nYou'd like to validate that your controller has received the correct\nparams structure, all you need to do is create a couple of modules:\n\n```elixir\ndefmodule MyApi.Params.Location\n  use Ecto.Schema\n  import Ecto.Changeset\n\n  @required ~w(latitude longitude)\n  @optional ~w()\n\n  schema \"location params\" do\n    field :latitude, :float\n    field :longitude, :float\n  end\n\n  def changeset(ch, params) do\n    cast(ch, params, @required ++ @optional)\n    |\u003e validate_required(@required)\n  end\nend\n\ndefmodule MyAPI.Params.KittenSearch\n  use Ecto.Schema\n  import Ecto.Changeset\n\n  @required ~w(breed)\n  @optional ~w(age_min age_max)\n\n  schema \"params for kitten search\" do\n    field :breed, :string\n    field :age_min, :integer\n    field :age_max, :integer\n    field :age_max, :integer\n    field :color, Ecto.Enum, values: [:brown, :black, :grey, :unknown],\n    embeds_one :near_location, Location\n  end\n\n  def changeset(ch, params) do\n    cast(ch, params, @required ++ @optional)\n    |\u003e validate_required(@required)\n    |\u003e cast_embed(:near_location, required: true)\n  end\nend\n\n# On your controller:\ndef search(conn, params) do\n  alias MyAPI.Params.KittenSearch\n  changeset = KittenSearch.changeset(%KittenSearch{}, params)\n  if changeset.valid? do\n    ...\nend\n```\n\nThat would allow you to take only valid params as you'd\nnormally have with any other Ecto.Schema module.\n\nHowever it's still a lot of code, most of it\ndefining the the changeset, specifying the optional\nand required fields, etc.\n\n[Params](#usage) is just a simple [Ecto.Schema][Ecto.Schema]\nwrapper for reducing all this boilerplate, while still\nleting you create custom changesets for parameter processing.\n\n## Usage\n\nThe previous example could be written like:\n\n```elixir\ndefmodule MyAPI.KittenController do\n\n  use Params\n\n  defparams kitten_search %{\n    breed!: :string,\n    age_max: :integer,\n    age_min: [field: :integer, default: 1],\n    color: [field: Ecto.Enum, values: [:brown, :black, :grey, :unknown]],\n    near_location!: %{\n      latitude!: :float, longitude!: :float\n    },\n    tags: [:string]\n  }\n\n  def index(conn, params) do\n    changeset = kitten_search(params)\n    if changeset.valid? do\n      search = Params.data changeset\n      IO.puts search.near_location.latitude\n    ...\n  end\nend\n```\n\nThe `defparams` macro generates a module for processing\na [params schema](http://hexdocs.pm/params/Params.Schema.html)\n\nBy default all fields are optional. You can mark\nrequired fields by ending them with a `!`, of course\nthe bang is removed from the field definition and is\nonly used to mark which fields are required by default.\n\nYou can also create a module and define\nyour schema or custom changesets in it:\n\n```elixir\ndefmodule UserSearch do\n  use Params.Schema, %{name: :string, age: :integer}\n  import Ecto.Changeset, only: [cast: 3, validate_inclusion: 3]\n\n  def child(ch, params) do\n    cast(ch, params, ~w(name age))\n    |\u003e validate_inclusion(:age, 1..6)\n  end\nend\n\ndefmodule MyApp.UserController do\n\n  def index(conn, params) do\n    changeset = UserSearch.from(params, with: \u0026UserSearch.child/2)\n    if changeset.valid? do\n      # age in 1..6\n  end\n\nend\n```\n\nThe [Params.data](http://hexdocs.pm/params/Params.html#data/1)\nand [Params.to_map](http://hexdocs.pm/params/Params.html#to_map/1) can be useful\nfor obtaining a struct or map from a changeset.\n\nNote that `Params.data` and `Params.to_map` have different behaviour: `data`\nreturns a struct which will include all valid params. `to_map` returns a map\nthat only includes the submitted keys and keys with default values:\n\n```elixir\ndefmodule UserUpdateParams do\n  use Params.Schema, %{\n    name: :string,\n    age: :integer,\n    auditlog: [field: :boolean, default: true]\n  }\nend\n\nchangeset = UserUpdateParams.from(%{name: \"John\"})\n\nParams.data(changeset) # =\u003e %UserUpdateParams{name: \"John\", age: nil, auditlog: true}\nParams.to_map(changeset) # =\u003e %{name: \"John\", auditlog: true}\n```\n\n## API Documentation\n\n[API Documentation](https://hexdocs.pm/params/)\n\n\n[Phoenix]: http://www.phoenixframework.org\n[Ecto]: https://hexdocs.pm/ecto\n[Ecto.Schema]: https://hexdocs.pm/ecto/Ecto.Schema.html\n[cast]: https://hexdocs.pm/ecto/Ecto.Changeset.html#cast/3\n\n\n## Contributors\n\nHere's a list of [awesome people](https://github.com/vic/params/graphs/contributors) who have contributed code to this project.\n\nIf you find a bug or want to improve something, please send a pull-request. Thank you!\n\n## Copyright and License\n\nCopyright (c) 2016 Victor Hugo Borja\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at [https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvic%2Fparams","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvic%2Fparams","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvic%2Fparams/lists"}