{"id":13491216,"url":"https://github.com/elixir-plug/plug","last_synced_at":"2025-05-12T05:29:28.497Z","repository":{"id":11862295,"uuid":"14421569","full_name":"elixir-plug/plug","owner":"elixir-plug","description":"Compose web applications with functions","archived":false,"fork":false,"pushed_at":"2025-04-21T19:01:08.000Z","size":2912,"stargazers_count":2917,"open_issues_count":7,"forks_count":591,"subscribers_count":91,"default_branch":"main","last_synced_at":"2025-05-12T04:07:55.678Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://hex.pm/packages/plug","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elixir-plug.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":"2013-11-15T10:30:47.000Z","updated_at":"2025-05-11T19:45:05.000Z","dependencies_parsed_at":"2023-01-14T00:30:19.680Z","dependency_job_id":"91a23d40-d9c6-4ed2-9b0e-10cc4bc3e11b","html_url":"https://github.com/elixir-plug/plug","commit_stats":{"total_commits":1505,"total_committers":339,"mean_commits":4.43952802359882,"dds":0.6378737541528239,"last_synced_commit":"f804daa204db3a1f1f10742d0964e6e00f932dab"},"previous_names":["elixir-lang/plug"],"tags_count":114,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-plug%2Fplug","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-plug%2Fplug/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-plug%2Fplug/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-plug%2Fplug/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-plug","download_url":"https://codeload.github.com/elixir-plug/plug/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253672707,"owners_count":21945481,"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":[],"created_at":"2024-07-31T19:00:54.627Z","updated_at":"2025-05-12T05:29:28.475Z","avatar_url":"https://github.com/elixir-plug.png","language":"Elixir","readme":"# Plug\n\n[![Build Status](https://github.com/elixir-plug/plug/workflows/CI/badge.svg)](https://github.com/elixir-plug/plug/actions?query=workflow%3A%22CI%22)\n[![hex.pm](https://img.shields.io/hexpm/v/plug.svg)](https://hex.pm/packages/plug)\n[![hexdocs.pm](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/plug/)\n\nPlug is:\n\n  1. A specification for composing web applications with functions\n  2. Connection adapters for different web servers in the Erlang VM\n\nIn other words, Plug allows you to build web applications from small pieces and run them on different web servers. Plug is used by web frameworks such as [Phoenix](https://phoenixframework.org) to manage requests, responses, and websockets. This documentation will show some high-level examples and introduce the Plug's main building blocks.\n\n## Installation\n\nIn order to use Plug, you need a webserver and its bindings for Plug.\nThere are two options at the moment:\n\n1. Use the Cowboy webserver (Erlang-based) by adding the `plug_cowboy` package to your `mix.exs`:\n\n    ```elixir\n    def deps do\n      [\n        {:plug_cowboy, \"~\u003e 2.0\"}\n      ]\n    end\n    ```\n\n2. Use the Bandit webserver (Elixir-based) by adding the `bandit` package to your `mix.exs`:\n\n    ```elixir\n    def deps do\n      [\n        {:bandit, \"~\u003e 1.0\"}\n      ]\n    end\n    ```\n\n## Hello world: request/response\n\nThis is a minimal hello world example, using the Cowboy webserver:\n\n```elixir\nMix.install([:plug, :plug_cowboy])\n\ndefmodule MyPlug do\n  import Plug.Conn\n\n  def init(options) do\n    # initialize options\n    options\n  end\n\n  def call(conn, _opts) do\n    conn\n    |\u003e put_resp_content_type(\"text/plain\")\n    |\u003e send_resp(200, \"Hello world\")\n  end\nend\n\nrequire Logger\nwebserver = {Plug.Cowboy, plug: MyPlug, scheme: :http, options: [port: 4000]}\n{:ok, _} = Supervisor.start_link([webserver], strategy: :one_for_one)\nLogger.info(\"Plug now running on localhost:4000\")\nProcess.sleep(:infinity)\n```\n\nSave that snippet to a file and execute it as `elixir hello_world.exs`.\nAccess \u003chttp://localhost:4000/\u003e and you should be greeted!\n\nIn the example above, we wrote our first **module plug**, called `MyPlug`.\nModule plugs must define the `init/1` function and the `call/2` function.\n`call/2` is invoked with the connection and the options returned by `init/1`.\n\n## Hello world: websockets\n\nPlug v1.14 includes a connection `upgrade` API, which means it provides WebSocket\nsupport out of the box. Let's see an example, this time using the Bandit webserver\nand the `websocket_adapter` project for the WebSocket bits. Since we need different\nroutes, we will use the built-in `Plug.Router` for that:\n\n```elixir\nMix.install([:bandit, :websock_adapter])\n\ndefmodule EchoServer do\n  def init(options) do\n    {:ok, options}\n  end\n\n  def handle_in({\"ping\", [opcode: :text]}, state) do\n    {:reply, :ok, {:text, \"pong\"}, state}\n  end\n\n  def terminate(:timeout, state) do\n    {:ok, state}\n  end\nend\n\ndefmodule Router do\n  use Plug.Router\n\n  plug Plug.Logger\n  plug :match\n  plug :dispatch\n\n  get \"/\" do\n    send_resp(conn, 200, \"\"\"\n    Use the JavaScript console to interact using websockets\n\n    sock  = new WebSocket(\"ws://localhost:4000/websocket\")\n    sock.addEventListener(\"message\", console.log)\n    sock.addEventListener(\"open\", () =\u003e sock.send(\"ping\"))\n    \"\"\")\n  end\n\n  get \"/websocket\" do\n    conn\n    |\u003e WebSockAdapter.upgrade(EchoServer, [], timeout: 60_000)\n    |\u003e halt()\n  end\n\n  match _ do\n    send_resp(conn, 404, \"not found\")\n  end\nend\n\nrequire Logger\nwebserver = {Bandit, plug: Router, scheme: :http, port: 4000}\n{:ok, _} = Supervisor.start_link([webserver], strategy: :one_for_one)\nLogger.info(\"Plug now running on localhost:4000\")\nProcess.sleep(:infinity)\n```\n\nSave that snippet to a file and execute it as `elixir websockets.exs`.\nAccess \u003chttp://localhost:4000/\u003e and you should see messages in your browser\nconsole.\n\nThis time, we used `Plug.Router`, which allows us to define the routes\nused by our web application and a series of steps/plugs, such as\n`plug Plug.Logger`, to be executed on every request.\n\nFurthermore, as you can see, Plug abstracts the different webservers.\nWhen booting up your application, the difference is between choosing\n`Plug.Cowboy` or `Bandit`.\n\nFor now, we have directly started the server in a throw-away supervisor but,\nfor production deployments, you want to start them in application\nsupervision tree. See the [Supervised handlers](#supervised-handlers) section next.\n\n## Supervised handlers\n\nOn a production system, you likely want to start your Plug pipeline under your application's supervision tree. Start a new Elixir project with the `--sup` flag:\n\n```shell\n$ mix new my_app --sup\n```\n\nAdd `:plug_cowboy` (or `:bandit`) as a dependency to your `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:plug_cowboy, \"~\u003e 2.0\"}\n  ]\nend\n```\n\nNow update `lib/my_app/application.ex` as follows:\n\n```elixir\ndefmodule MyApp.Application do\n  # See https://hexdocs.pm/elixir/Application.html\n  # for more information on OTP Applications\n  @moduledoc false\n\n  use Application\n\n  def start(_type, _args) do\n    # List all child processes to be supervised\n    children = [\n      {Plug.Cowboy, scheme: :http, plug: MyPlug, options: [port: 4001]}\n    ]\n\n    # See https://hexdocs.pm/elixir/Supervisor.html\n    # for other strategies and supported options\n    opts = [strategy: :one_for_one, name: MyApp.Supervisor]\n    Supervisor.start_link(children, opts)\n  end\nend\n```\n\nFinally create `lib/my_app/my_plug.ex` with the `MyPlug` module.\n\nNow run `mix run --no-halt` and it will start your application with a web server running at \u003chttp://localhost:4001\u003e.\n\n## Plugs and the `Plug.Conn` struct\n\nIn the hello world example, we defined our first plug called `MyPlug`. There are two types of plugs, module plugs and function plugs.\n\nA module plug implements an `init/1` function to initialize the options and a `call/2` function which receives the connection and initialized options and returns the connection:\n\n```elixir\ndefmodule MyPlug do\n  def init([]), do: false\n  def call(conn, _opts), do: conn\nend\n```\n\nA function plug takes the connection, a set of options as arguments, and returns the connection:\n\n```elixir\ndef hello_world_plug(conn, _opts) do\n  conn\n  |\u003e put_resp_content_type(\"text/plain\")\n  |\u003e send_resp(200, \"Hello world\")\nend\n```\n\nA connection is represented by the `%Plug.Conn{}` struct:\n\n```elixir\n%Plug.Conn{\n  host: \"www.example.com\",\n  path_info: [\"bar\", \"baz\"],\n  ...\n}\n```\n\nData can be read directly from the connection and also pattern matched on. Manipulating the connection often happens with the use of the functions defined in the `Plug.Conn` module. In our example, both `put_resp_content_type/2` and `send_resp/3` are defined in `Plug.Conn`.\n\nRemember that, as everything else in Elixir, **a connection is immutable**, so every manipulation returns a new copy of the connection:\n\n```elixir\nconn = put_resp_content_type(conn, \"text/plain\")\nconn = send_resp(conn, 200, \"ok\")\nconn\n```\n\nFinally, keep in mind that a connection is a **direct interface to the underlying web server**. When you call `send_resp/3` above, it will immediately send the given status and body back to the client. This makes features like streaming a breeze to work with.\n\n## `Plug.Router`\n\nTo write a \"router\" plug that dispatches based on the path and method of incoming requests, Plug provides `Plug.Router`:\n\n```elixir\ndefmodule MyRouter do\n  use Plug.Router\n\n  plug :match\n  plug :dispatch\n\n  get \"/hello\" do\n    send_resp(conn, 200, \"world\")\n  end\n\n  forward \"/users\", to: UsersRouter\n\n  match _ do\n    send_resp(conn, 404, \"oops\")\n  end\nend\n```\n\nThe router is a plug. Not only that: it contains its own plug pipeline too. The example above says that when the router is invoked, it will invoke the `:match` plug, represented by a local (imported) `match/2` function, and then call the `:dispatch` plug which will execute the matched code.\n\nPlug ships with many plugs that you can add to the router plug pipeline, allowing you to plug something before a route matches or before a route is dispatched to. For example, if you want to add logging to the router, just do:\n\n```elixir\nplug Plug.Logger\nplug :match\nplug :dispatch\n```\n\nNote `Plug.Router` compiles all of your routes into a single function and relies on the Erlang VM to optimize the underlying routes into a tree lookup, instead of a linear lookup that would instead match route-per-route. This means route lookups are extremely fast in Plug!\n\nThis also means that a catch all `match` block is recommended to be defined as in the example above, otherwise routing fails with a function clause error (as it would in any regular Elixir function).\n\nEach route needs to return the connection as per the Plug specification. See the `Plug.Router` docs for more information.\n\n## Testing plugs\n\nPlug ships with a `Plug.Test` module that makes testing your plugs easy. Here is how we can test the router from above (or any other plug):\n\n```elixir\ndefmodule MyPlugTest do\n  use ExUnit.Case, async: true\n  import Plug.Test\n  import Plug.Conn\n  \n  @opts MyRouter.init([])\n\n  test \"returns hello world\" do\n    # Create a test connection\n    conn = conn(:get, \"/hello\")\n\n    # Invoke the plug\n    conn = MyRouter.call(conn, @opts)\n\n    # Assert the response and status\n    assert conn.state == :sent\n    assert conn.status == 200\n    assert conn.resp_body == \"world\"\n  end\nend\n```\n\n## Available plugs\n\nThis project aims to ship with different plugs that can be re-used across applications:\n\n  * `Plug.BasicAuth` - provides Basic HTTP authentication;\n  * `Plug.CSRFProtection` - adds Cross-Site Request Forgery protection to your application. Typically required if you are using `Plug.Session`;\n  * `Plug.Head` - converts HEAD requests to GET requests;\n  * `Plug.Logger` - logs requests;\n  * `Plug.MethodOverride` - overrides a request method with one specified in the request parameters;\n  * `Plug.Parsers` - responsible for parsing the request body given its content-type;\n  * `Plug.RequestId` - sets up a request ID to be used in logs;\n  * `Plug.RewriteOn` - rewrite the request's host/port/protocol from `x-forwarded-*` headers;\n  * `Plug.Session` - handles session management and storage;\n  * `Plug.SSL` - enforces requests through SSL;\n  * `Plug.Static` - serves static files;\n  * `Plug.Telemetry` - instruments the plug pipeline with `:telemetry` events;\n\nYou can go into more details about each of them [in our docs](http://hexdocs.pm/plug/).\n\n## Helper modules\n\nModules that can be used after you use `Plug.Router` or `Plug.Builder` to help development:\n\n  * `Plug.Debugger` - shows a helpful debugging page every time there is a failure in a request;\n  * `Plug.ErrorHandler` - allows developers to customize error pages in case of crashes instead of sending a blank one;\n\n## Contributing\n\nWe welcome everyone to contribute to Plug and help us tackle existing issues!\n\nUse the [issue tracker][issues] for bug reports or feature requests. Open a [pull request][pulls] when you are ready to contribute. When submitting a pull request you should not update the `CHANGELOG.md`.\n\nIf you are planning to contribute documentation, [please check our best practices for writing documentation][writing-docs].\n\nFinally, remember all interactions in our official spaces follow our [Code of Conduct][code-of-conduct].\n\n## Supported Versions\n\n| Branch | Support                  |\n|--------|--------------------------|\n| v1.15  | Bug fixes                |\n| v1.14  | Security patches only    |\n| v1.13  | Security patches only    |\n| v1.12  | Security patches only    |\n| v1.11  | Security patches only    |\n| v1.10  | Security patches only    |\n| v1.9   | Unsupported from 10/2023 |\n| v1.8   | Unsupported from 01/2023 |\n| v1.7   | Unsupported from 01/2022 |\n| v1.6   | Unsupported from 01/2022 |\n| v1.5   | Unsupported from 03/2021 |\n| v1.4   | Unsupported from 12/2018 |\n| v1.3   | Unsupported from 12/2018 |\n| v1.2   | Unsupported from 06/2018 |\n| v1.1   | Unsupported from 01/2018 |\n| v1.0   | Unsupported from 05/2017 |\n\n## License\n\nPlug source code is released under Apache License 2.0.\nCheck LICENSE file for more information.\n\n  [issues]: https://github.com/elixir-plug/plug/issues\n  [pulls]: https://github.com/elixir-plug/plug/pulls\n  [code-of-conduct]: https://github.com/elixir-lang/elixir/blob/master/CODE_OF_CONDUCT.md\n  [writing-docs]: https://hexdocs.pm/elixir/writing-documentation.html\n","funding_links":[],"categories":["`API Frameworks`","Elixir","API Frameworks"],"sub_categories":["Elixir"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-plug%2Fplug","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-plug%2Fplug","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-plug%2Fplug/lists"}