{"id":13776376,"url":"https://github.com/implicitly-awesome/exop_plug","last_synced_at":"2025-12-11T23:36:45.818Z","repository":{"id":62429223,"uuid":"216530908","full_name":"implicitly-awesome/exop_plug","owner":"implicitly-awesome","description":"Provides a convenient way to validate incoming parameters of your Phoenix application's controllers by offering you small but useful DSL.","archived":false,"fork":false,"pushed_at":"2020-03-06T11:19:03.000Z","size":35,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-21T14:56:35.299Z","etag":null,"topics":["coercion","elixir","phoenix","validation"],"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/implicitly-awesome.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}},"created_at":"2019-10-21T09:36:23.000Z","updated_at":"2024-07-25T10:40:50.000Z","dependencies_parsed_at":"2022-11-01T20:03:23.607Z","dependency_job_id":null,"html_url":"https://github.com/implicitly-awesome/exop_plug","commit_stats":null,"previous_names":["madeinussr/exop_plug"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/implicitly-awesome/exop_plug","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/implicitly-awesome%2Fexop_plug","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/implicitly-awesome%2Fexop_plug/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/implicitly-awesome%2Fexop_plug/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/implicitly-awesome%2Fexop_plug/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/implicitly-awesome","download_url":"https://codeload.github.com/implicitly-awesome/exop_plug/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/implicitly-awesome%2Fexop_plug/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280281400,"owners_count":26303709,"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-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","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":["coercion","elixir","phoenix","validation"],"created_at":"2024-08-03T18:00:24.313Z","updated_at":"2025-12-11T23:36:45.788Z","avatar_url":"https://github.com/implicitly-awesome.png","language":"Elixir","funding_links":[],"categories":["Exop family:"],"sub_categories":["ExopPlug"],"readme":"[![Hex.pm](https://img.shields.io/hexpm/v/exop_plug.svg)](https://hex.pm/packages/exop_plug) [![API Docs](https://img.shields.io/badge/api-docs-yellow.svg?style=flat)](http://hexdocs.pm/exop_plug/) [![Build Status](https://travis-ci.org/madeinussr/exop_plug.svg?branch=master)](https://travis-ci.org/madeinussr/exop_plug)\n\n# ExopPlug\n\nThis library provides a convenient way to validate incoming parameters\nof your Phoenix application's controllers by offering you small but useful DSL\nwhich makes as little magic behind the scenes as possible leaves you all the control\nunder HTTP request.\n\n## Table of Contents\n\n[CHANGELOG](https://github.com/madeinussr/exop_plug/blob/master/CHANGELOG.md)\n\n- [Installation](#installation)\n- [How it works](#how-it-works)\n- [Step-by-step](#step-by-step)\n  - [1. create a plug](#1-create-a-plug)\n  - [2. in a controller](#2-in-a-controller)\n- [More examples](#more-examples)\n  - [coercing](#coercing)\n  - [rely on `action_fallback`](#rely-on-action_fallback)\n  - [respond directly from `on_fail` callback](#respond-directly-from-on_fail-callback)\n- [LICENSE](#license)\n\n## Installation\n\n```elixir\ndef deps do\n  [\n    {:exop, \"~\u003e 1.4\"},\n    {:exop_plug, \"~\u003e 1.1\"}\n  ]\nend\n```\n\n## How it works\n\nBasically speaking ExopPlug utilizes the power of [Exop](https://github.com/madeinussr/exop) library\nby generating in compile time Exop-operations for your actions defined in a plug.\n\nSo, you define a plug with number of actions and parameters with validation checks.\nThen you use this plug in corresponding controller and that's it: once HTTP request comes,\nyour controller's plug takes an action: it figures out whether this particular HTTP request's\nparameters should be validated or not, and if yes - validates them.\n\nIf parameters pass the validation ExopPlug returns `Plug.Conn` (as usual plug does),\nif not - it returns [Exop's](https://github.com/madeinussr/exop) error-tuple as described\n[here](https://github.com/madeinussr/exop#operation-results). And it is up to you then to decide\nhow you want to handle the result.\n\nPlease keep in mind: ExopPlug doesn't transform your HTTP request nor `Plug.Conn.t()` structure.\nSo, if you define `get '/user/:user_id'` in your router you receive `%{\"user_id\" =\u003e \"1\"}` for\nthe request `http://localhost:4000/user/1`. There is no any coercion or type inference done\nunder the scenes.\n\n_(if such feature(-s) will be requested, they'll be added and you'll need to specify them\nexplicitly in an action definition)_\n\n## Step-by-step\n\n### 1. create a plug\n\nCreate a new module plug and define actions with parameters you want to validate.\nA parameter's validations are the same as Exop has for an operation parameter checks.\nYou can find all them [here](https://github.com/madeinussr/exop#parameter-checks)\nalong with other features like coercion.\n\n```elixir\ndefmodule MyAppWeb.UserControllerPlug do\n  use ExopPlug\n\n  action(:show, params: %{\"id\" =\u003e [type: :string, length: %{min: 5}]}, on_fail: \u0026__MODULE__.on_fail/3)\n\n  def on_fail(conn, action_name, errors_map) do\n    Plug.Conn.assign(conn, :errors, errors_map)\n  end\nend\n```\n\nHere we also defined an `on_fail` callback. This 3-arity function is called when an action's parameters\nfailed the specified validation.\n\n### 2. in a controller\n\nSimply add `plug MyAppWeb.UserControllerPlug` at the top of your controller.\n\n```elixir\ndefmodule MyAppWeb.UserController do\n  use MyAppWeb, :controller\n\n  plug MyAppWeb.UserControllerPlug\n\n  # ...\n\n  def show(conn, params) do\n    json(conn, params)\n  end\n\n  # ...\nend\n```\n\nNow, if you receive invalid parameters for your `show` you get (for example)\n`errors: %{\"id\" =\u003e [\"has wrong type\"]}}` within your `Plug.Conn` assigns\n(as you earlier specified in your plug). And then it is up to you how to deal with this errors map.\n\n## More examples\n\nThe power of ExopPlug actually is provided by Exop and its validation capabilities.\nBasically, every check you use in Exop you can apply for an action's parameter in your plug.\n\nBelow I put a couple of examples just in order to show different checks and things you can do with\na parameter or validation errors.\n\n\n### coercing\n\nIn the example above there is one tricky thing which I've just omit for the sake of example:\nwith `get '/user/:user_id'` route you'll get `user_id` parameter as string, always, because it is\npath parameter.\n\nBut you can coerce it before the validation:\n\n```elixir\naction(:show, params: %{\"id\" =\u003e [type: :integer, coerce_with: \u0026__MODULE__.coerce_integer/2]})\n\ndef coerce_integer({_, param_value}, _) when is_binary(param_value) do\n  {integer, \"\"} = Integer.parse(param_value)\n  integer\nend\n```\n\n_(and again: read more about `:coerce_with` option in Exop [docs](https://github.com/madeinussr/exop))_\n\n### rely on `action_fallback`\n\nAfter assigning errors to a connection you can later pattern-match it in a controller's action\nand invoke your `action_fallback`'s fallback controller:\n\n```elixir\n# in a plug ...\n\naction(:show, params: %{\"id\" =\u003e [type: :integer]}, on_fail: \u0026__MODULE__.on_fail/3)\n\ndef on_fail(conn, action_name, errors_map) do\n  Plug.Conn.assign(conn, :errors, errors_map)\nend\n\n# in a controller ...\n\naction_fallback MyAppWeb.FallbackController\n\ndef show(%Plug.Conn{assigns: %{errors: errors_map}}, _params) do\n  {:error, errors_map}\nend\n\n# in the fallback controller ...\n\ndef call(conn, {:error, errors_map}) do\n  json(conn, errors_map)\nend\n```\n\n### respond directly from `on_fail` callback\n\nIt might be useful not to assign validation errors to a connection, but respond immediately:\n\n```elixir\naction(:show, params: %{\"id\" =\u003e [type: :integer]}, on_fail: \u0026__MODULE__.on_fail/3)\n\ndef on_fail(conn, action_name, errors_map) do\n  response = %{\n    action: action_name,\n    errors: errors_map\n  }\n\n  Phoenix.Controller.json(conn, response)\nend\n```\n\n## LICENSE\n\n    Copyright © 2019 - 2020 Andrey Chernykh ( andrei.chernykh@gmail.com )\n\n    This work is free. You can redistribute it and/or modify it under the\n    terms of the MIT License. See the LICENSE file for more details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimplicitly-awesome%2Fexop_plug","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimplicitly-awesome%2Fexop_plug","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimplicitly-awesome%2Fexop_plug/lists"}