Top Related Projects
Peace of mind from prototype to production
Fast, unopinionated, minimalist web framework for node.
Classy web-development dressed in a DSL (official / canonical repo)
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.
Package gorilla/mux is a powerful HTTP router and URL matcher for building Go web servers with 🦍
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
- 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.
- 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.
- 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
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.
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.
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.
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.
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.
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 designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Plug
Plug is:
- A specification for composing web applications with functions
- 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:
-
Use the Cowboy webserver (Erlang-based) by adding the
plug_cowboy
package to yourmix.exs
:def deps do [ {:plug_cowboy, "~> 2.0"} ] end
-
Use the Bandit webserver (Elixir-based) by adding the
bandit
package to yourmix.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 usingPlug.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 fromx-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
Branch | Support |
---|---|
v1.15 | Bug fixes |
v1.14 | Security patches only |
v1.13 | Security patches only |
v1.12 | Security patches only |
v1.11 | Security patches only |
v1.10 | Security patches only |
v1.9 | Unsupported from 10/2023 |
v1.8 | Unsupported from 01/2023 |
v1.7 | Unsupported from 01/2022 |
v1.6 | Unsupported from 01/2022 |
v1.5 | Unsupported from 03/2021 |
v1.4 | Unsupported from 12/2018 |
v1.3 | Unsupported from 12/2018 |
v1.2 | Unsupported from 06/2018 |
v1.1 | Unsupported from 01/2018 |
v1.0 | Unsupported from 05/2017 |
License
Plug source code is released under Apache License 2.0. Check LICENSE file for more information.
Top Related Projects
Peace of mind from prototype to production
Fast, unopinionated, minimalist web framework for node.
Classy web-development dressed in a DSL (official / canonical repo)
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.
Package gorilla/mux is a powerful HTTP router and URL matcher for building Go web servers with 🦍
A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot