Subscribe to access all episodes. View plans →
Published November 13, 2017
This episode has been revised. Find the new expanded episode here: https://elixircasts.io/plug.router
Elixir 1.4.5
Plug 1.4.3
Cowboy 1.1.2
View source on GitHub
In this episode let’s build a simple website that serves up static content. While we could use Phoenix, it would be a bit much for what we need.
Instead let’s use Plug.Router to build it.
To start let’s go to the command line and create a new Elixir project with:
$ mix new teacher
Then we’ll open our new project. Let’s open our ‘mixfile’ and we’ll need to add plug as a dependency. We’ll also add include Cowboy to use as our web server.
mix.exs
defp deps do
[{:plug, "~> 1.4"},
{:cowboy, "~> 1.1"}]
end
Now let’s go to the command line download our dependencies:
$ mix deps.get
With that let’s define the routes for our site. To do that we’ll need to create a router.
Let’s create a new directory in ‘lib’ named ‘web’ and then a we’ll create our ‘router.ex’ inside of it.
We’ll define our module and we’ll use Plug.Router
.
And we’ll include the ‘match’ plug, which is responsible for matching our routes.
And the ‘dispatch’ plug, which receives/processes the matched route.
Looking at the router docs, we’ll see the router gives us a simple DSL - or domain specific language - that we can use to define the routes of our application.
Let’s create our first route at ‘/elixir’. And inside we’ll define what happens when someone hits the route.
Let’s use Plug’s send_resp
to send a response with a status of 200 and a simple message for the body.
Let’s also define a ‘catch-all’ match that will catch all other requests to our application. And inside we’ll set our catch-all to send a status of 404 and a message.
lib/web/router.ex
defmodule Web.Router do
use Plug.Router
plug :match
plug :dispatch
get "/elixir" do
send_resp(conn, 200, "I love <3 Elixir")
end
match _ do
send_resp(conn, 404, "This is not the page you're looking for.")
end
end
Now that we have a basic router defined - we need to create an application module, which we’ll use to supervise our router.
Let’s open our ‘teacher’ module and remove the boilerplate that was created with it.
Since this will be our application module, we’ll first invoke use Application
and then define the start
callback.
We won’t use the start type or any arguments for this demo - so let’s ignore them. And inside our start callback, let’s call our supervisor.
We’ll use Supervisor.start_link/2
which takes a list of children to supervise as the first argument.
And the second argument will be a keyword list of any options we want to specify.
Let’s use the one_for_one
restart strategy, which restarts only the child process that’s terminated.
Now we need to define our ‘children’.
Since we’re using ‘cowboy’ for our web server let’s check out the [documentation] for Plug’s cowboy adapter.
We see there’s a child_spec
function that starts a Cowboy server. It even gives us a great example on how to use it.
We’ll go ahead and grab this example. And back in our module let’s modify it. Let’s keep the same scheme - http- and port - 4001 - but we’ll need to specify our Router module for our plug.
lib/teacher.ex
defmodule Teacher do
use Application
def start(_type, _args) do
children = [
Plug.Adapters.Cowboy.child_spec(:http, Web.Router, [], [port: 4001])
]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
Now that we have our application module defined, let’s go back to our ‘mixfile’.
In it we’ll need to update the application
function to include our Teacher
module. This is the module that will be invoked when our application is started.
mod
needs a tuple, where the first element is the module we want started - in this case our teacher module..
And in the second element we can supply any arguments we want - in this case we’ll just use an empty list.
mix.exs
…
def application do
[mod: {Teacher, []},
extra_applications: [:logger]]
end
…
With that let’s go to the command line and we’ll call
mix run --no-halt
This will keep our application running after the command.
Now let’s go to our browser and test our routes. We’ll first go to ‘localhost:4001/elixir’ - and great we see our message. Now let’s test out a route that we haven’t defined and great our catch-all is triggered: ‘localhost:4001/asdf’.
With our routes working let’s take this a little further and render templates.
Let’s first create an html template - ‘elixir.html’ - in lib/web.
Template path: lib/web/email.html
<h1>I <3 Elixir</h1>
Then let’s open our router and update our ‘/elixir’ route to render our new template.
We’ll call plug’s put_resp_header
to set the content-type header.
Then we’ll pipe that into plug’s send_file
function. Giving it the same status of 200 and then we’ll give it our new HTML file we’ve just created.
get "/elixir" do
conn
|> put_resp_header("content-type", "text/html; charset=utf-8")
|> send_file(200, "lib/web/elixir.html")
end
Now let’ go to the command line and restart our application.
And if we go back to our ‘localhost:4001/elixir’ route - we see our new template is being rendered.
Kirill B.
6 years agoWhat plugin do you use for autocompletion?
Alekx
6 years agoHey Kirill, I am using atom-elixir.
Sergii Doroshenko
6 years agoHi Alekx. Thank you for all for this lessons. Great job! But I think it’s time to update lesson because “plug” and “cowboy” version are updated and now we should define
instead of separated “plug” and “cowboy”
Thanks!
Alekx
4 years agoI have an updated version of this episode that expands on using Plug.Router: https://elixircasts.io/plug.router.