Convert Figma logo to code with AI

elixir-plug logoplug

Compose web applications with functions

2,841
583
2,841
9

Top Related Projects

21,201

Peace of mind from prototype to production

64,773

Fast, unopinionated, minimalist web framework for node.

12,165

Classy web-development dressed in a DSL (official / canonical repo)

77,851

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.

20,665

Package gorilla/mux is a powerful HTTP router and URL matcher for building Go web servers with 🦍

66,731

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀

Quick Overview

Plug is a specification and powerful set of abstractions for composable modules in between web applications in Elixir. It serves as a common foundation for web-related libraries and frameworks, providing a consistent interface for handling HTTP requests and responses.

Pros

  • Lightweight and modular design, allowing for easy composition of web application components
  • Excellent performance due to its efficient implementation and Elixir's underlying BEAM VM
  • Strong ecosystem support with many plug-compatible libraries and frameworks
  • Well-documented and maintained by the Elixir core team

Cons

  • Learning curve for developers new to functional programming or Elixir
  • Limited built-in features compared to full-fledged web frameworks
  • May require additional libraries for more complex web applications
  • Not as widely adopted as some other web development ecosystems

Code Examples

  1. Basic Plug:
defmodule MyPlug do
  import Plug.Conn

  def init(options), do: options

  def call(conn, _opts) do
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(200, "Hello, World!")
  end
end

This example creates a simple Plug that responds with "Hello, World!" for any request.

  1. Router Plug:
defmodule MyRouter do
  use Plug.Router

  plug :match
  plug :dispatch

  get "/hello" do
    send_resp(conn, 200, "Hello from MyRouter!")
  end

  match _ do
    send_resp(conn, 404, "Not Found")
  end
end

This example demonstrates a basic router using Plug.Router, defining a route for "/hello" and a catch-all for 404 responses.

  1. Middleware Plug:
defmodule AuthPlug do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    case get_auth_token(conn) do
      {:ok, token} -> assign(conn, :auth_token, token)
      :error -> conn |> send_resp(401, "Unauthorized") |> halt()
    end
  end

  defp get_auth_token(conn) do
    case get_req_header(conn, "authorization") do
      ["Bearer " <> token] -> {:ok, token}
      _ -> :error
    end
  end
end

This example shows a middleware Plug that checks for an authorization token in the request headers.

Getting Started

To use Plug in your Elixir project, add it to your mix.exs dependencies:

def deps do
  [
    {:plug, "~> 1.14"}
  ]
end

Then run mix deps.get to install the dependency. You can now create Plug modules and use them in your application.

To start a basic Plug server, you can use:

{:ok, _} = Plug.Adapters.Cowboy.http MyPlug, []

This starts a server using the Cowboy adapter with your MyPlug module.

Competitor Comparisons

21,201

Peace of mind from prototype to production

Pros of Phoenix

  • Full-featured web framework with built-in generators, live reloading, and a robust ecosystem
  • Includes real-time functionality out of the box with Phoenix Channels
  • Provides a structured, opinionated approach to building web applications

Cons of Phoenix

  • Steeper learning curve due to its comprehensive nature
  • Potentially overkill for simple web applications or microservices
  • More dependencies and overhead compared to Plug

Code Comparison

Phoenix routing example:

defmodule MyApp.Router do
  use Phoenix.Router
  get "/", PageController, :index
  resources "/users", UserController
end

Plug routing example:

defmodule MyApp.Router do
  use Plug.Router
  plug :match
  plug :dispatch
  get "/", do: send_resp(conn, 200, "Welcome")
  match _, do: send_resp(conn, 404, "Not Found")
end

Phoenix is a full-featured web framework built on top of Plug, offering a more comprehensive solution for web development in Elixir. It provides additional tools and conventions that streamline the development process, especially for larger applications. Plug, on the other hand, is a more lightweight and flexible option, ideal for building simple web applications or as a foundation for custom frameworks. The choice between Phoenix and Plug depends on the project's complexity and specific requirements.

64,773

Fast, unopinionated, minimalist web framework for node.

Pros of Express

  • Larger ecosystem and community, with more third-party middleware and plugins available
  • Familiar JavaScript syntax and Node.js environment, making it easier for web developers to adopt
  • Extensive documentation and learning resources due to its popularity

Cons of Express

  • Less performant than Plug, especially for high-concurrency scenarios
  • Callback-based middleware can lead to "callback hell" and more complex error handling
  • Lacks built-in support for WebSockets and long-polling, requiring additional libraries

Code Comparison

Express route handling:

app.get('/hello', (req, res) => {
  res.send('Hello, World!');
});

Plug route handling:

get "/hello" do
  send_resp(conn, 200, "Hello, World!")
end

Both frameworks offer simple and intuitive ways to define routes and handle requests. Express uses JavaScript callbacks, while Plug leverages Elixir's pattern matching and function clauses.

Express middleware is typically added using app.use(), whereas Plug uses a pipeline of functions. Plug's approach can lead to more composable and modular code, especially for larger applications.

While Express is more widely used and has a larger ecosystem, Plug benefits from Elixir's concurrency model and the Erlang VM, making it potentially more suitable for high-performance, fault-tolerant systems.

12,165

Classy web-development dressed in a DSL (official / canonical repo)

Pros of Sinatra

  • Mature and well-established Ruby framework with a large community and extensive documentation
  • Simple and intuitive DSL for creating web applications quickly
  • Flexible and lightweight, allowing developers to choose their preferred components

Cons of Sinatra

  • Less performant compared to Plug's Elixir-based implementation
  • Limited built-in features, requiring additional gems for more complex applications
  • Lacks the concurrency and fault-tolerance benefits of Elixir's BEAM VM

Code Comparison

Sinatra:

require 'sinatra'

get '/' do
  'Hello, World!'
end

Plug:

defmodule MyApp do
  use Plug.Router
  plug :match
  plug :dispatch

  get "/" do
    send_resp(conn, 200, "Hello, World!")
  end
end

Both frameworks offer simple and concise ways to define routes and handle requests. Sinatra's DSL is more Ruby-like and arguably more readable, while Plug leverages Elixir's pattern matching and functional programming paradigms. Plug's approach is more explicit in its use of connection objects and response handling, which can lead to better performance and more fine-grained control over the request-response cycle.

77,851

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance -- up to 40 times faster. If you need smashing performance, get yourself some Gin.

Pros of Gin

  • Faster performance due to Go's compiled nature and Gin's optimizations
  • More extensive middleware ecosystem and third-party plugins
  • Simpler routing syntax and easier to set up for REST APIs

Cons of Gin

  • Less functional programming paradigms compared to Elixir and Plug
  • Lacks built-in support for WebSockets and long-polling
  • Less emphasis on fault tolerance and hot code reloading

Code Comparison

Gin (Go):

r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
    c.JSON(200, gin.H{"message": "pong"})
})
r.Run()

Plug (Elixir):

defmodule MyApp.Router do
  use Plug.Router
  plug :match
  plug :dispatch
  get "/ping" do
    send_resp(conn, 200, "pong")
  end
end

Both Gin and Plug are popular web application frameworks, but they cater to different programming paradigms and use cases. Gin is known for its speed and simplicity, making it a great choice for building high-performance REST APIs in Go. Plug, on the other hand, leverages Elixir's functional programming features and the Erlang VM's robustness, making it ideal for building scalable and fault-tolerant web applications.

While Gin offers a more traditional imperative approach to web development, Plug embraces functional programming concepts, which can lead to more composable and maintainable code in complex applications. However, Gin's simplicity and performance make it an attractive option for developers coming from other imperative languages.

20,665

Package gorilla/mux is a powerful HTTP router and URL matcher for building Go web servers with 🦍

Pros of Mux

  • Written in Go, offering excellent performance and concurrency support
  • Provides a more comprehensive routing system with URL parameters and subrouters
  • Includes built-in middleware chaining for request processing

Cons of Mux

  • Less flexible than Plug's modular approach to composing web applications
  • Lacks some of Plug's advanced features like connection adapters and halting

Code Comparison

Mux (Go):

r := mux.NewRouter()
r.HandleFunc("/users/{id}", GetUserHandler).Methods("GET")
r.Use(loggingMiddleware)
http.ListenAndServe(":8080", r)

Plug (Elixir):

defmodule MyApp do
  use Plug.Router
  plug :match
  plug :dispatch
  get "/users/:id", do: send_resp(conn, 200, "User details")
end

Key Differences

  • Mux focuses on HTTP routing and middleware, while Plug provides a more general-purpose framework for composing web applications
  • Plug offers greater flexibility in terms of adapters and protocols, allowing for use with various web servers
  • Mux provides a more traditional request-response model, whereas Plug emphasizes the concept of transforming connections through a pipeline

Both libraries are popular choices in their respective ecosystems, with Mux being widely used in Go web development and Plug serving as a foundation for many Elixir web frameworks.

66,731

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀

Pros of Nest

  • Built on TypeScript, offering strong typing and better tooling support
  • Comprehensive framework with built-in dependency injection and modular architecture
  • Extensive ecosystem with official packages for various integrations (e.g., GraphQL, WebSockets)

Cons of Nest

  • Steeper learning curve due to its opinionated structure and decorators
  • Heavier footprint compared to Plug's lightweight nature
  • Potentially slower performance in high-load scenarios due to additional abstractions

Code Comparison

Nest (Controller):

@Controller('cats')
export class CatsController {
  @Get()
  findAll(): string {
    return 'This action returns all cats';
  }
}

Plug (Router):

defmodule MyApp.Router do
  use Plug.Router
  
  get "/cats" do
    send_resp(conn, 200, "This action returns all cats")
  end
end

Key Differences

  • Nest uses decorators and classes for defining routes and controllers
  • Plug employs a more functional approach with macros and functions
  • Nest provides a full-featured framework, while Plug is a modular web application library
  • Plug is part of the Elixir ecosystem, leveraging its concurrency model and fault tolerance

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Plug

Build Status hex.pm hexdocs.pm

Plug is:

  1. A specification for composing web applications with functions
  2. Connection adapters for different web servers in the Erlang VM

In 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 to manage requests, responses, and websockets. This documentation will show some high-level examples and introduce the Plug's main building blocks.

Installation

In order to use Plug, you need a webserver and its bindings for Plug. There are two options at the moment:

  1. Use the Cowboy webserver (Erlang-based) by adding the plug_cowboy package to your mix.exs:

    def deps do
      [
        {:plug_cowboy, "~> 2.0"}
      ]
    end
    
  2. Use the Bandit webserver (Elixir-based) by adding the bandit package to your mix.exs:

    def deps do
      [
        {:bandit, "~> 1.0"}
      ]
    end
    

Hello world: request/response

This is a minimal hello world example, using the Cowboy webserver:

Mix.install([:plug, :plug_cowboy])

defmodule MyPlug do
  import Plug.Conn

  def init(options) do
    # initialize options
    options
  end

  def call(conn, _opts) do
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(200, "Hello world")
  end
end

require Logger
webserver = {Plug.Cowboy, plug: MyPlug, scheme: :http, options: [port: 4000]}
{:ok, _} = Supervisor.start_link([webserver], strategy: :one_for_one)
Logger.info("Plug now running on localhost:4000")
Process.sleep(:infinity)

Save that snippet to a file and execute it as elixir hello_world.exs. Access http://localhost:4000/ and you should be greeted!

In the example above, we wrote our first module plug, called MyPlug. Module plugs must define the init/1 function and the call/2 function. call/2 is invoked with the connection and the options returned by init/1.

Hello world: websockets

Plug v1.14 includes a connection upgrade API, which means it provides WebSocket support out of the box. Let's see an example, this time using the Bandit webserver and the websocket_adapter project for the WebSocket bits. Since we need different routes, we will use the built-in Plug.Router for that:

Mix.install([:bandit, :websock_adapter])

defmodule EchoServer do
  def init(options) do
    {:ok, options}
  end

  def handle_in({"ping", [opcode: :text]}, state) do
    {:reply, :ok, {:text, "pong"}, state}
  end

  def terminate(:timeout, state) do
    {:ok, state}
  end
end

defmodule Router do
  use Plug.Router

  plug Plug.Logger
  plug :match
  plug :dispatch

  get "/" do
    send_resp(conn, 200, """
    Use the JavaScript console to interact using websockets

    sock  = new WebSocket("ws://localhost:4000/websocket")
    sock.addEventListener("message", console.log)
    sock.addEventListener("open", () => sock.send("ping"))
    """)
  end

  get "/websocket" do
    conn
    |> WebSockAdapter.upgrade(EchoServer, [], timeout: 60_000)
    |> halt()
  end

  match _ do
    send_resp(conn, 404, "not found")
  end
end

require Logger
webserver = {Bandit, plug: Router, scheme: :http, port: 4000}
{:ok, _} = Supervisor.start_link([webserver], strategy: :one_for_one)
Logger.info("Plug now running on localhost:4000")
Process.sleep(:infinity)

Save that snippet to a file and execute it as elixir websockets.exs. Access http://localhost:4000/ and you should see messages in your browser console.

This time, we used Plug.Router, which allows us to define the routes used by our web application and a series of steps/plugs, such as plug Plug.Logger, to be executed on every request.

Furthermore, as you can see, Plug abstracts the different webservers. When booting up your application, the difference is between choosing Plug.Cowboy or Bandit.

For now, we have directly started the server in a throw-away supervisor but, for production deployments, you want to start them in application supervision tree. See the Supervised handlers section next.

Supervised handlers

On 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:

$ mix new my_app --sup

Add :plug_cowboy (or :bandit) as a dependency to your mix.exs:

def deps do
  [
    {:plug_cowboy, "~> 2.0"}
  ]
end

Now update lib/my_app/application.ex as follows:

defmodule MyApp.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  def start(_type, _args) do
    # List all child processes to be supervised
    children = [
      {Plug.Cowboy, scheme: :http, plug: MyPlug, options: [port: 4001]}
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Finally create lib/my_app/my_plug.ex with the MyPlug module.

Now run mix run --no-halt and it will start your application with a web server running at http://localhost:4001.

Plugs and the Plug.Conn struct

In the hello world example, we defined our first plug called MyPlug. There are two types of plugs, module plugs and function plugs.

A 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:

defmodule MyPlug do
  def init([]), do: false
  def call(conn, _opts), do: conn
end

A function plug takes the connection, a set of options as arguments, and returns the connection:

def hello_world_plug(conn, _opts) do
  conn
  |> put_resp_content_type("text/plain")
  |> send_resp(200, "Hello world")
end

A connection is represented by the %Plug.Conn{} struct:

%Plug.Conn{
  host: "www.example.com",
  path_info: ["bar", "baz"],
  ...
}

Data 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.

Remember that, as everything else in Elixir, a connection is immutable, so every manipulation returns a new copy of the connection:

conn = put_resp_content_type(conn, "text/plain")
conn = send_resp(conn, 200, "ok")
conn

Finally, 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.

Plug.Router

To write a "router" plug that dispatches based on the path and method of incoming requests, Plug provides Plug.Router:

defmodule MyRouter do
  use Plug.Router

  plug :match
  plug :dispatch

  get "/hello" do
    send_resp(conn, 200, "world")
  end

  forward "/users", to: UsersRouter

  match _ do
    send_resp(conn, 404, "oops")
  end
end

The 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.

Plug 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:

plug Plug.Logger
plug :match
plug :dispatch

Note 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!

This 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).

Each route needs to return the connection as per the Plug specification. See the Plug.Router docs for more information.

Testing plugs

Plug 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):

defmodule MyPlugTest do
  use ExUnit.Case, async: true
  use Plug.Test

  @opts MyRouter.init([])

  test "returns hello world" do
    # Create a test connection
    conn = conn(:get, "/hello")

    # Invoke the plug
    conn = MyRouter.call(conn, @opts)

    # Assert the response and status
    assert conn.state == :sent
    assert conn.status == 200
    assert conn.resp_body == "world"
  end
end

Available plugs

This project aims to ship with different plugs that can be re-used across applications:

  • Plug.BasicAuth - provides Basic HTTP authentication;
  • Plug.CSRFProtection - adds Cross-Site Request Forgery protection to your application. Typically required if you are using Plug.Session;
  • Plug.Head - converts HEAD requests to GET requests;
  • Plug.Logger - logs requests;
  • Plug.MethodOverride - overrides a request method with one specified in the request parameters;
  • Plug.Parsers - responsible for parsing the request body given its content-type;
  • Plug.RequestId - sets up a request ID to be used in logs;
  • Plug.RewriteOn - rewrite the request's host/port/protocol from x-forwarded-* headers;
  • Plug.Session - handles session management and storage;
  • Plug.SSL - enforces requests through SSL;
  • Plug.Static - serves static files;
  • Plug.Telemetry - instruments the plug pipeline with :telemetry events;

You can go into more details about each of them in our docs.

Helper modules

Modules that can be used after you use Plug.Router or Plug.Builder to help development:

  • Plug.Debugger - shows a helpful debugging page every time there is a failure in a request;
  • Plug.ErrorHandler - allows developers to customize error pages in case of crashes instead of sending a blank one;

Contributing

We welcome everyone to contribute to Plug and help us tackle existing issues!

Use the issue tracker for bug reports or feature requests. Open a pull request when you are ready to contribute. When submitting a pull request you should not update the CHANGELOG.md.

If you are planning to contribute documentation, please check our best practices for writing documentation.

Finally, remember all interactions in our official spaces follow our Code of Conduct.

Supported Versions

BranchSupport
v1.15Bug fixes
v1.14Security patches only
v1.13Security patches only
v1.12Security patches only
v1.11Security patches only
v1.10Security patches only
v1.9Unsupported from 10/2023
v1.8Unsupported from 01/2023
v1.7Unsupported from 01/2022
v1.6Unsupported from 01/2022
v1.5Unsupported from 03/2021
v1.4Unsupported from 12/2018
v1.3Unsupported from 12/2018
v1.2Unsupported from 06/2018
v1.1Unsupported from 01/2018
v1.0Unsupported from 05/2017

License

Plug source code is released under Apache License 2.0. Check LICENSE file for more information.