Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Build Your Own Web Framework in Elixir

You're reading from   Build Your Own Web Framework in Elixir Develop lightning-fast web applications using Phoenix and metaprogramming

Arrow left icon
Product type Paperback
Published in Jun 2023
Publisher Packt
ISBN-13 9781801812542
Length 274 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Aditya Iyengar Aditya Iyengar
Author Profile Icon Aditya Iyengar
Aditya Iyengar
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Preface 1. Part 1: Web Server Fundamentals
2. Chapter 1: Introducing the Cowboy Web Server FREE CHAPTER 3. Chapter 2: Building an HTTP Server in Elixir 4. Part 2: Router, Controller, and View
5. Chapter 3: Defining Web Application Specifications Using Plug 6. Chapter 4: Working with Controllers 7. Chapter 5: Adding Controller Plugs and Action Fallback 8. Chapter 6: Working with HTML and Embedded Elixir 9. Chapter 7: Working with Views 10. Part 3: DSL Design
11. Chapter 8: Metaprogramming – Code That Writes Code 12. Chapter 9: Controller and View DSL 13. Chapter 10: Building the Router DSL 14. Index

Building a web application using Cowboy

In this section, we will take a look at some of the individual components of the Cowboy web server and use them to build a simple web application in Elixir.

Creating a new Mix project

Let’s start by creating a new Mix project by entering the following in your terminal:

$ mix new cowboy_example --sup

What is Mix?

Mix is a build tool written in Elixir. Its purpose is to bundle all the dependencies required by a project and provide an interface to run tasks that rely on the application environment. If you’re familiar with the Ruby world, you can think of Mix as a combination of Rake and Bundler.

Passing the --sup option to the mix new command allows us to create a Mix project with a supervision tree. A supervision tree (or a supervisor) is a process that simply monitors other processes and is responsible for automatically restarting any process within the tree if it fails or crashes. We will be using the supervision tree in this application to start and supervise our web server process to make sure it is started when the application is started and to ensure that it keeps running.

Now, we will add Cowboy as a dependency to our project by adding it to the mix.exs file’s dependencies:

mix.exs

defmodule CowboyExample.MixProject do
  # ...
  defp deps do
    [
      {:cowboy, "~> 2.8"}
    ]
  end
end

Follow it up by running mix deps.get from the project’s root directory, which should fetch Cowboy as a dependency.

Adding a handler and router to Cowboy

Now that we have added Cowboy, it is time to configure it to listen on a port. We will be using two functions to accomplish that:

  • :cowboy_router.compile/1: This function is responsible for defining a set of requested hosts, paths, and parameters to a dedicated request handler. This function also generates a set of rules, known as dispatch rules, to use those handlers.
  • :cowboy.start_clear/3: This function is responsible for starting a listener process on a TCP channel. It takes a listener name (an atom), transport options such as the TCP port, and protocol options such as the dispatch rules generated using the :cowboy_router.compile/1 function.

Now, let us use these functions to write an HTTP server. We can start by creating a new module to house our new HTTP server:

lib/cowboy_example/server.ex

defmodule CowboyExample.Server do
  @moduledoc """
  This module defines a cowboy HTTP server and starts it
  on a port
  """
  @doc """
  This function starts a Cowboy server on the given port.
  Routes for the server are defined in CowboyExample.Router
  """
  def start(port) do
    routes = CowboyExample.Router.routes()
    dispatch_rules =
      :cowboy_router.compile(routes)
    {:ok, _pid} =
      :cowboy.start_clear(
        :listener,
        [{:port, port}],
        %{env: %{dispatch: dispatch_rules}}
      )
  end
end

The preceding function starts a Cowboy server that listens to HTTP requests at the given port. By pattern matching on {:ok, _pid}, we’re making sure that :cowboy.start_clear/3 doesn’t fail silently.

We can try to start our web server by calling the CowboyExample.Server.start/1 function with a port. But, as you can see, we will also need to define the CowboyExample.Router router module. This module’s responsibility is to define routes that can be used to generate dispatch rules for our HTTP server. This can be done by storing all the routes, parameters, and handler tuples in the router module and passing them to the :cowboy_router.compile/1 call.

Let’s define the router module with the route for the root URL of the host (/):

lib/cowboy_example/router.ex

defmodule CowboyExample.Router do
  @moduledoc """
  This module defines all the routes, params and handlers.
  This module is also the handler module for the root
  route.
  """
  require Logger
  @doc """
  Returns the list of routes configured by this web server
  """
  def routes do
    [
      # For now, this module itself will handle root
      # requests
      {:_, [{"/", __MODULE__, []}]}
    ]
  end
end

We will also be using CowboyExample.Router itself as the handler for that route, which means we have to define the init/2 function, which takes the request and its initial state.

So, let us define the init/2 function:

lib/cowboy_example/router.ex

defmodule CowboyExample.Router do
  # ..
  @doc """
  This function handles the root route, logs the requests
  and responds with Hello World as the body
  """
  def init(req0, state) do
    Logger.info("Received request: #{inspect req0}")
    req1 =
      :cowboy_req.reply(
        200,
        %{"content-type" => "text/html"},
        "Hello World",
        req0
      )
    {:ok, req1, state}
  end
end

As you can see in the preceding code, we have defined the routes/0 function, which returns the dispatch rules routes for our web application. For the handler module, we’re currently using the CowboyExample.Router module itself by defining the init/2 function, which responds with "Hello World" and a status of 200, whenever invoked. We have also added a call to the Logger module to log all requests to the handler. This will increase visibility while running it in the development environment.

In order for our web server to start up when the app is started, we need to add it to our application’s supervision tree.

You have been reading a chapter from
Build Your Own Web Framework in Elixir
Published in: Jun 2023
Publisher: Packt
ISBN-13: 9781801812542
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime