Introduction to Elixir and
Phoenix
Created by Ridwan Fadjar Septian,
At SenseHealth B.V.
Bandung, 26th September 2018
Disclaimer
- I am just curious about Elixir and Phoenix Web Framework for personal usage only
- Currently I don’t have an experience using Elixir in production
- I bought Elixir and Phoenix course on Udemy for my hobby
- Alam embrace me to learn Elixir and set up a sharing session
Background and Motivation
Why Elixir?
Elixir at Glance
Fundamental
Hello world
iex > IO.puts("hello world!")
hello world!
:ok
Data Type
iex> 255
255
iex> 0b0110
6
iex> 0o644
420
iex> 0x1F
31
iex> 3.14
3.14
iex> 1.0e-10
1.0e-10
iex> true
true
iex> false
False
Data Type
iex> :foo
:foo
iex> :foo == :bar
false
iex> "Hello"
"Hello"
iex> "さようなら"
"さようなら"
iex> "foo
...> bar"
"foonbar"
iex> "foonbar"
"foonbar"
Basic Operator
iex> 2 + 2
4
iex> 2 - 1
1
iex> 2 * 5
10
iex> 10 / 5
2.0
iex> div(10, 5)
2
iex> rem(10, 3)
1
Basic Operator
iex> -20 || true
-20
iex> false || 42
42
iex> 42 && true
true
iex> 42 && nil
nil
iex> !42
false
iex> !false
true
Basic Operator
iex> true and 42
42
iex> false or true
true
iex> not false
true
iex> 1 > 2
false
iex> 1 != 2
true
iex> 2 == 2
true
iex> 2 <= 3
true
iex> 2 == 2.0
true
iex> 2 === 2.0
false
Basic Operator
iex> "Elixir rocks" |> String.upcase() |> String.split()
["ELIXIR", "ROCKS"]
Conditional
iex> if String.valid?("Hello") do
...> "Valid string!"
...> else
...> "Invalid string."
...> end
"Valid string!"
iex> if "a string value" do
...> "Truthy"
...> end
"Truthy"
Conditional
iex> unless is_integer("hello") do
...> "Not an Int"
...> end
"Not an Int"
Conditional
iex> case {:ok, "Hello World"} do
...> {:ok, result} -> result
...> {:error} -> "Uh oh!"
...> _ -> "Catch all"
...> end
"Hello World"
Conditional
iex> cond do
...> 2 + 2 == 5 ->
...> "This will not be true"
...> 2 * 2 == 3 ->
...> "Nor this"
...> 1 + 1 == 2 ->
...> "But this will"
...> end
"But this will"
Recursion
defmodule Loop do
def print_multiple_times
(msg, n) when n <= 1 do
IO.puts msg
end
def print_multiple_times
(msg, n) do
IO.puts msg
print_multiple_times
(msg, n - 1)
end
end
Loop.print_multiple_times
("Hello", 10)
Function
iex> sum = fn (a, b) -> a + b end
iex> sum.(2, 3)
5
iex> sum = &(&1 + &2)
iex> sum.(2, 3)
5
Function and Module
defmodule Greeter do
def hello(name) do
"Hello, " <> name
end
end
iex> Greeter.hello("Sean")
"Hello, Sean"
Function and Module
defmodule Greeter2 do
def hello(), do: "Hello, anonymous person!" # hello/0
def hello(name), do: "Hello, " <> name # hello/1
def hello(name1, name2), do: "Hello, #{name1} and #{name2}"
# hello/2
end
iex> Greeter2.hello()
"Hello, anonymous person!"
iex> Greeter2.hello("Fred")
"Hello, Fred"
iex> Greeter2.hello("Fred", "Jane")
"Hello, Fred and Jane"
Mix
Package Manager in Elixir
Phoenix
Web Framework for Elixir
Getting Started
$ mix archive.install
https://github.com/phoenixframework/archives/raw/master/phx_new.ez
$ mix phx.new tracking-api --app my_app --module TrackingAPI --no-brunch --no-html
$ cd tracking-api
$ mix phx.server
Supported Database
- PostgreSQL
- MySQL
Configs
use Mix.Config
config :tracking_api, TrackingAPIWeb.Endpoint,
http: [port: 4000],
debug_errors: false,
code_reloader: true,
check_origin: false,
watchers: []
config :logger, :console, format: "[$level] $messagen"
config :phoenix, :stacktrace_depth, 20
config :tracking_api, TrackingAPI.Repo,
adapter: Ecto.Adapters.Postgres,
username: "phoenix",
password: "phoenix",
database: "tracking_api_dev",
hostname: "localhost",
pool_size: 10
Models
schema "gps_tracking" do
field :application_id, Ecto.UUID
field :created_at, :utc_datetime
field :latitude, :string
field :longitude, :string
field :user_id, Ecto.UUID
timestamps()
end
@doc false
def changeset(gps_tracking, attrs) do
gps_tracking
|> cast(attrs, [:application_id, :user_id, :latitude, :longitude, :created_at])
|> validate_required([:application_id, :user_id, :latitude, :longitude, :created_at])
end
Repo
def list_gps_tracking do
Repo.all(GPSTracking)
end
def get_gps_tracking!(id), do: Repo.get!(GPSTracking, id)
def create_gps_tracking(attrs  %{}) do
%GPSTracking{}
|> GPSTracking.changeset(attrs)
|> Repo.insert()
end
def update_gps_tracking(%GPSTracking{} = gps_tracking, attrs) do
gps_tracking
|> GPSTracking.changeset(attrs)
|> Repo.update()
end
def delete_gps_tracking(%GPSTracking{} = gps_tracking) do
Repo.delete(gps_tracking)
end
def change_gps_tracking(%GPSTracking{} = gps_tracking) do
GPSTracking.changeset(gps_tracking, %{})
end
Controllers
defmodule TrackingAPIWeb.GPSTrackingController do
use TrackingAPIWeb, :controller
alias TrackingAPI.Tracking
alias TrackingAPI.Tracking.GPSTracking
action_fallback TrackingAPIWeb.FallbackController
def index(conn, _params) do
gps_tracking = Tracking.list_gps_tracking()
render(conn, "index.json", gps_tracking: gps_tracking)
end
def create(conn, %{"gps_tracking" => gps_tracking_params}) do
with {:ok, %GPSTracking{} = gps_tracking} <- Tracking.create_gps_tracking(gps_tracking_params) do
conn
|> put_status(:created)
|> put_resp_header("location", gps_tracking_path(conn, :show, gps_tracking))
|> render("show.json", gps_tracking: gps_tracking)
end
end
end
We cannot use update endpoint! We exclude it.
Views
def render("index.json", %{gps_tracking: gps_tracking}) do
%{data: render_many(gps_tracking, GPSTrackingView, "gps_tracking.json")}
end
def render("show.json", %{gps_tracking: gps_tracking}) do
%{data: render_one(gps_tracking, GPSTrackingView, "gps_tracking.json")}
end
def render("gps_tracking.json", %{gps_tracking: gps_tracking}) do
%{id: gps_tracking.id,
application_id: gps_tracking.application_id,
user_id: gps_tracking.user_id,
latitude: gps_tracking.latitude,
longitude: gps_tracking.longitude,
created_at: gps_tracking.created_at}
end
Router
defmodule TrackingAPIWeb.Router do
use TrackingAPIWeb, :router
pipeline :api do
plug :accepts, ["json"]
end
scope "/api", TrackingAPIWeb do
pipe_through :api
resources "/gps_tracking", GPSTrackingController, except: [:edit]
end
end
Testing
test "list_gps_tracking/0 returns all gps_tracking" do
gps_tracking = gps_tracking_fixture()
assert Tracking.list_gps_tracking() == [gps_tracking]
end
test "get_gps_tracking!/1 returns the gps_tracking with given id" do
gps_tracking = gps_tracking_fixture()
assert Tracking.get_gps_tracking!(gps_tracking.id) == gps_tracking
end
test "create_gps_tracking/1 with valid data creates a gps_tracking" do
assert {:ok, %GPSTracking{} = gps_tracking} = Tracking.create_gps_tracking(@valid_attrs)
assert gps_tracking.application_id == "7488a646-e31f-11e4-aace-600308960662"
assert gps_tracking.created_at == DateTime.from_naive!(~N[2010-04-17 14:00:00.000000Z], "Etc/UTC")
assert gps_tracking.latitude == "some latitude"
assert gps_tracking.longitude == "some longitude"
assert gps_tracking.user_id == "7488a646-e31f-11e4-aace-600308960662"
end
test "create_gps_tracking/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Tracking.create_gps_tracking(@invalid_attrs)
end
References
- https://hexdocs.pm/phoenix/
- https://elixirschool.com/
- https://www.tutorialspoint.com/elixir/
- https://elixir-lang.org/docs.html
- https://github.com/h4cc/awesome-elixir
- https://github.com/droptheplot/awesome-phoenix
Demo

Introduction to Elixir and Phoenix.pdf

  • 1.
    Introduction to Elixirand Phoenix Created by Ridwan Fadjar Septian, At SenseHealth B.V. Bandung, 26th September 2018
  • 2.
    Disclaimer - I amjust curious about Elixir and Phoenix Web Framework for personal usage only - Currently I don’t have an experience using Elixir in production - I bought Elixir and Phoenix course on Udemy for my hobby - Alam embrace me to learn Elixir and set up a sharing session
  • 3.
  • 4.
  • 5.
    Hello world iex >IO.puts("hello world!") hello world! :ok
  • 6.
    Data Type iex> 255 255 iex>0b0110 6 iex> 0o644 420 iex> 0x1F 31 iex> 3.14 3.14 iex> 1.0e-10 1.0e-10 iex> true true iex> false False
  • 7.
    Data Type iex> :foo :foo iex>:foo == :bar false iex> "Hello" "Hello" iex> "さようなら" "さようなら" iex> "foo ...> bar" "foonbar" iex> "foonbar" "foonbar"
  • 8.
    Basic Operator iex> 2+ 2 4 iex> 2 - 1 1 iex> 2 * 5 10 iex> 10 / 5 2.0 iex> div(10, 5) 2 iex> rem(10, 3) 1
  • 9.
    Basic Operator iex> -20|| true -20 iex> false || 42 42 iex> 42 && true true iex> 42 && nil nil iex> !42 false iex> !false true
  • 10.
    Basic Operator iex> trueand 42 42 iex> false or true true iex> not false true iex> 1 > 2 false iex> 1 != 2 true iex> 2 == 2 true iex> 2 <= 3 true iex> 2 == 2.0 true iex> 2 === 2.0 false
  • 11.
    Basic Operator iex> "Elixirrocks" |> String.upcase() |> String.split() ["ELIXIR", "ROCKS"]
  • 12.
    Conditional iex> if String.valid?("Hello")do ...> "Valid string!" ...> else ...> "Invalid string." ...> end "Valid string!" iex> if "a string value" do ...> "Truthy" ...> end "Truthy"
  • 13.
    Conditional iex> unless is_integer("hello")do ...> "Not an Int" ...> end "Not an Int"
  • 14.
    Conditional iex> case {:ok,"Hello World"} do ...> {:ok, result} -> result ...> {:error} -> "Uh oh!" ...> _ -> "Catch all" ...> end "Hello World"
  • 15.
    Conditional iex> cond do ...>2 + 2 == 5 -> ...> "This will not be true" ...> 2 * 2 == 3 -> ...> "Nor this" ...> 1 + 1 == 2 -> ...> "But this will" ...> end "But this will"
  • 16.
    Recursion defmodule Loop do defprint_multiple_times (msg, n) when n <= 1 do IO.puts msg end def print_multiple_times (msg, n) do IO.puts msg print_multiple_times (msg, n - 1) end end Loop.print_multiple_times ("Hello", 10)
  • 17.
    Function iex> sum =fn (a, b) -> a + b end iex> sum.(2, 3) 5 iex> sum = &(&1 + &2) iex> sum.(2, 3) 5
  • 18.
    Function and Module defmoduleGreeter do def hello(name) do "Hello, " <> name end end iex> Greeter.hello("Sean") "Hello, Sean"
  • 19.
    Function and Module defmoduleGreeter2 do def hello(), do: "Hello, anonymous person!" # hello/0 def hello(name), do: "Hello, " <> name # hello/1 def hello(name1, name2), do: "Hello, #{name1} and #{name2}" # hello/2 end iex> Greeter2.hello() "Hello, anonymous person!" iex> Greeter2.hello("Fred") "Hello, Fred" iex> Greeter2.hello("Fred", "Jane") "Hello, Fred and Jane"
  • 20.
  • 21.
  • 22.
    Getting Started $ mixarchive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez $ mix phx.new tracking-api --app my_app --module TrackingAPI --no-brunch --no-html $ cd tracking-api $ mix phx.server
  • 23.
  • 24.
    Configs use Mix.Config config :tracking_api,TrackingAPIWeb.Endpoint, http: [port: 4000], debug_errors: false, code_reloader: true, check_origin: false, watchers: [] config :logger, :console, format: "[$level] $messagen" config :phoenix, :stacktrace_depth, 20 config :tracking_api, TrackingAPI.Repo, adapter: Ecto.Adapters.Postgres, username: "phoenix", password: "phoenix", database: "tracking_api_dev", hostname: "localhost", pool_size: 10
  • 25.
    Models schema "gps_tracking" do field:application_id, Ecto.UUID field :created_at, :utc_datetime field :latitude, :string field :longitude, :string field :user_id, Ecto.UUID timestamps() end @doc false def changeset(gps_tracking, attrs) do gps_tracking |> cast(attrs, [:application_id, :user_id, :latitude, :longitude, :created_at]) |> validate_required([:application_id, :user_id, :latitude, :longitude, :created_at]) end
  • 26.
    Repo def list_gps_tracking do Repo.all(GPSTracking) end defget_gps_tracking!(id), do: Repo.get!(GPSTracking, id) def create_gps_tracking(attrs %{}) do %GPSTracking{} |> GPSTracking.changeset(attrs) |> Repo.insert() end def update_gps_tracking(%GPSTracking{} = gps_tracking, attrs) do gps_tracking |> GPSTracking.changeset(attrs) |> Repo.update() end def delete_gps_tracking(%GPSTracking{} = gps_tracking) do Repo.delete(gps_tracking) end def change_gps_tracking(%GPSTracking{} = gps_tracking) do GPSTracking.changeset(gps_tracking, %{}) end
  • 27.
    Controllers defmodule TrackingAPIWeb.GPSTrackingController do useTrackingAPIWeb, :controller alias TrackingAPI.Tracking alias TrackingAPI.Tracking.GPSTracking action_fallback TrackingAPIWeb.FallbackController def index(conn, _params) do gps_tracking = Tracking.list_gps_tracking() render(conn, "index.json", gps_tracking: gps_tracking) end def create(conn, %{"gps_tracking" => gps_tracking_params}) do with {:ok, %GPSTracking{} = gps_tracking} <- Tracking.create_gps_tracking(gps_tracking_params) do conn |> put_status(:created) |> put_resp_header("location", gps_tracking_path(conn, :show, gps_tracking)) |> render("show.json", gps_tracking: gps_tracking) end end end
  • 28.
    We cannot useupdate endpoint! We exclude it.
  • 29.
    Views def render("index.json", %{gps_tracking:gps_tracking}) do %{data: render_many(gps_tracking, GPSTrackingView, "gps_tracking.json")} end def render("show.json", %{gps_tracking: gps_tracking}) do %{data: render_one(gps_tracking, GPSTrackingView, "gps_tracking.json")} end def render("gps_tracking.json", %{gps_tracking: gps_tracking}) do %{id: gps_tracking.id, application_id: gps_tracking.application_id, user_id: gps_tracking.user_id, latitude: gps_tracking.latitude, longitude: gps_tracking.longitude, created_at: gps_tracking.created_at} end
  • 30.
    Router defmodule TrackingAPIWeb.Router do useTrackingAPIWeb, :router pipeline :api do plug :accepts, ["json"] end scope "/api", TrackingAPIWeb do pipe_through :api resources "/gps_tracking", GPSTrackingController, except: [:edit] end end
  • 31.
    Testing test "list_gps_tracking/0 returnsall gps_tracking" do gps_tracking = gps_tracking_fixture() assert Tracking.list_gps_tracking() == [gps_tracking] end test "get_gps_tracking!/1 returns the gps_tracking with given id" do gps_tracking = gps_tracking_fixture() assert Tracking.get_gps_tracking!(gps_tracking.id) == gps_tracking end test "create_gps_tracking/1 with valid data creates a gps_tracking" do assert {:ok, %GPSTracking{} = gps_tracking} = Tracking.create_gps_tracking(@valid_attrs) assert gps_tracking.application_id == "7488a646-e31f-11e4-aace-600308960662" assert gps_tracking.created_at == DateTime.from_naive!(~N[2010-04-17 14:00:00.000000Z], "Etc/UTC") assert gps_tracking.latitude == "some latitude" assert gps_tracking.longitude == "some longitude" assert gps_tracking.user_id == "7488a646-e31f-11e4-aace-600308960662" end test "create_gps_tracking/1 with invalid data returns error changeset" do assert {:error, %Ecto.Changeset{}} = Tracking.create_gps_tracking(@invalid_attrs) end
  • 32.
    References - https://hexdocs.pm/phoenix/ - https://elixirschool.com/ -https://www.tutorialspoint.com/elixir/ - https://elixir-lang.org/docs.html - https://github.com/h4cc/awesome-elixir - https://github.com/droptheplot/awesome-phoenix
  • 33.