[...] There was a very broad consensus that your style and depth would be a great fit for our team. John Norman, Chief Architect Iora Health
The Phoenix Framework allows developers to build robust, performant and scalable web applications faster than ever. By combining Elixir's modern language syntax and tried-and tested foundation, with many of the productivity benefits that made Ruby on Rails astonishingly popular, this new web framework promises to be great (dare I say, ideal?) option for a variety of projects.
In this workshop we'll begin by teaching you a little bit about Elixir - an easy-to-use functional programming language that runs on the BEAM (Erlang's virtual machine). We'll then dive into our first Phoenix app, tackling topics like data persistence, routing, authentication and error handling before the end of the first day.
On day two, we'll go back to learning about pure Elixir, tackling some more advanced topics like inter-process communication, distributed storage with ETS and security. After lunch, we'll put some of this knowledge to work in our Phoenix app by writing some tests, hashing passwords, encrypting database columns and more!
Before we jump right into the framework, we need to at least know the basics of the programming language we're working with.Agenda
We'll go over our agenda, and set our sights on some goals for the day.
Elixir is unique, in that it provides us the fantastic ergonomics of a modern programming language, while standing on the solid and battle-tested foundation of the Erlang ecosystem.
Elixir's interactive shell (IEx) is one of the most powerful tools in your toolbox. We'll outline some of the most useful features for beginners, including
As with most programming languages, it's useful to know how to interact with files and humans. We'll take care of this early on, and notice a few things that foreshadow some interesting aspects of Elixir's concurrency model.
One must crawl before one walks, and it all starts with basic types and procedural logic. Even if you're experienced in a wide range of programming languages, there's going to be a lot of stuff - even at this basic level - that may change the way you look at writing code forever.Agenda
There's no getting away from these kinds of things. Eventually you're going to need to work with numbers and text, so we'll start with a crash course in some core APIs (including a dip in the erlang pool) that will make life easy.
There's a lot of capability here, but we'll stay close to the commonly-useful and pragmatic path.
We'll create a simple program that calculates and object's projectile motion, given a launch angle and initial velocity.
We've got a bunch of functions that do various things to strings, but our tests are failing.
It stands to reason that functions are really important in a functional programming language. We'll build and work with named an anonymous functions, combine functions together to form pipelines, and even map out some higher-order functions of our own.
Break for Lunch
Often times we find ourselves needing to work with several objects in a "collection", and will need to choose between Elixir's
Tuple types. We'll compare and contrast tuples and lists, and write a few programs highlighting the benefits of each.
We have two main associative data structures in Elixir: keyword lists and maps. Let's learn more about them!
Assembling a bunch of items in a list is really fast, as long as we do it in a way that doesn't involve moving existing items around in memory. We'll write two programs, one which assembles a bunch of dictionary words into a tuple, and another that uses a list instead.
This modern language feature allows destructed assignment, and is often used in order to define several variants of a function, each to handle a specific scenario. This application of pattern matching reduces what would otherwise be a lot of internal function complexity by huge amounts.
We've got an Elixir module that involves some code that could benefit from some pattern matching magic. Refactor the monolith function so all use of if/else are replaced by creating new functions oriented toward handling that specific pattern of arguments.
It's unusual to use if/else in Elixir, because we have some far more powerful approaches to deciding among different branches of code to use. We'll look at the
case control flow structure, where pattern matching really starts to shine. We'll also take a look at how guards can be added to
case clauses (and other control flow structures) to form even more specific and targeted patterns.
We'll refactor some more code to leverage the power of pattern matching and functional control flow tools.
Wrap up for the day
Elixirs module system allows us to define layers of related functions. In this part of the course, we'll explore the concepts of modules, and the ability to reference code in one module from another.Agenda
Modules are just a group of several functions, some of which may be private and some of which may be public. Modules give us the ability to define named functions using the
def macro, which offer a few other features that were unavailable in the world of anonymous functions.
We've got a set of tests for a couple of Elixir modules that are used to control a space ship. Alter the code to make the unit tests pass, and ensure that you've kept as much of each module's internal functionality private as possible.
use macro is not strictly a directive, it's of particular importance when considering "mixins" for common functionality across multiple modules.
use macro can essentially be used to decorate a module with some code from another.
Earlier we outlined and worked with several different types of data structures. Let's take a closer look at some waysAgenda
We have a program that starts with a list of objects read from a file. Using the built-in functions available in the
Map modules, filter out "inactive" items (objects where the "active" attribute is not
true), and then log a list of object names to the console.
Often we find ourselves looping over something enumerable, mapping values into another list, and potentially filtering out some unwanted items. Comprehensions use a generator and a filter to provide some excellent syntactic sugar for this kind of task.
Take another pass at the previous exercise, and use a comprehension to devise a concise solution.
Break for Lunch
A Phoenix app can basically be boiled down to a function that receives a HTTP request, and returns a response. We'll begin with this premise, and start to understand the important parts involved in this process.Agenda
Requests enter your app through an Endpoint, and your app usually will have only one. We'll look at this chain of Elixir Plugs, which ends at the Router, the module ultimately responsible for delegating request-handling to an appropriate Controller.
Plugs are at the core of Phoenix, and they're a relatively simple and approachable concept: things that accept a connection as an argument, and return a slightly-modified connection.
Chain a few plugs together, and it's easy to see how basic building blocks start to assemble into a complete application.
Add a new page to your app, following the existing example set up in your
Build a Plug that interrupts the pipeline (returning a HTTP error for an incoming request) if we ever request a SOAP XML document (
Now that we understand how to leverage Phoenix's routing layer, let's take a closer look at Controllers: the modules ultimately responsible for responding to a request.
In a welcome contrast to other web frameworks, Phoenix's view layer is exceedingly easy to understand and use.
Judging by how easy it is to keep views simple, performant, and easy to manage, It's clear that the hard-learned lessons from older frameworks have paid off.
We'll pass some data from the Phoenix controller layer to the view layer, and leverage view functions to do some light formatting & massaging.
Testing ergonomics is perhaps the most impactful factor in determining whether writing tests is an enjoyable part of day-to-day development, or an annoying slog that's neglected until problems arise.
In this area, Phoenix does not disappoint. We'll focus on several useful patterns for unit and acceptance testing, with the aim of making tests quick, easy, maintainable and intuitive.Agenda
Sometimes we use Phoenix to render HTML, so we'll look at how we can verify that both our controller and view layers (individually) are doing their job.
Often we use Phoenix Controllers to render JSON. We'll explore some built-in helpers that are well-suited for helping us write tests verifying that the JSON contains what we expect, and touch on a few libraries that make this even easier!
Data is an integral part of virtually any web application, and a great persistence library make a huge difference in performance and maintainability.
Thankfully, the Elixir ecosystem has us covered in spades. Ecto is a thin layer of functions that allow us to build composable queries, validate fields, and seamlessly transform records between our DB and application representations.Agenda
Heavy persistence libraries like ActiveRecord offer convenience, but often become performance bottlenecks. We could make every DB query explicitly, but then we're trading in all of our ergonomics for performance.
Ecto manages to strike an enjoyable balance, where we are asked to be deliberate about the records we fetch from a database but (most of the time) aren't dragged into the world of writing SQL queries explicitly.
You'll be amazed at how much we can do with just simple functions, and will never look at other persistence frameworks quite the same way again.
If you've never used code to manage changes to your database schema, you're missing out. Migrations allow us to change our schema in (ideally) reversible steps, so we can apply and un-apply a set of changes while building features. Even if you've seen migrations before, there are some useful things to know about how they work with Ecto, and in particular, Postgres. We'll look, specifically at:
This is one of my favorite parts about Ecto, and one of the parts you'll be most often working with. In contrast to other persistence libraries, the concept of the shape of a record (schema) and the logic for checking the validity of values (validations) are decoupled. There are some incredibly exciting consequences of this design decision.
Ecto ships with a bunch of validations, and because it's so quick and easy, we'll write a few of our own.
Create models and appropriate validations for a blog post/comment app.
While we could use SQL syntax to retrieve records from our database, doing so would open us up to a world of pain. Ecto provides an approachable and composable way of building queries, while stopping short of doing us any "automatic favors" (i.e., N+1 queries) that so often degrade performance.
You'll be given a list of database queries for you and your classmates to make using Ecto. Each query is worth a certain number of points. Highest number of points after the exercise is done, wins!
Break for Lunch
One of the places where Elixir and Phoenix leave the competition in the dust is support for soft real time programming. The ability to keep a lightweight Elixir process running for the duration of a user's time in our app, and holding some small amount of state, makes things far simpler for certain things than it otherwise would be.Agenda
Phoenix Channels are a first class citizen in the framework, on equal footing with Controllers. It shows! You'll be amazed at how easy it is to start adding real-time features to your apps, where we push data from server to client.
Development best practices are increasingly moving in a functional and "stateless" direction, but Elixir Processes are a place where small pieces of state can be safely held and used. We'll explore how powerful this idea is, in the context of Phoenix channels.
Build your first channel, so that you can notify client-side apps of new comments being posted to articles.
While you may have contributed to a REST API project that had 10 endpoints (each handling 1-4 HTTP verbs), it's less likely that you have experience working with a long-lived web socket connection operating on the same scale of complexity. It's important to remember that this is API surface, and because it's often stateful instead of stateless, keeping organized is even more important.
Nearly every app we build these days requires some sort of authentication, and probably a user account to go along with it. Even if your app is an oddball and doesn't need this, user accounts provide us with a well-understood set of use cases that will serve as an excellent case study.
Let's put some of our newfound Phoenix knowledge into practice as we implement a secure user account feature set. The goal will be to reduce explicit management of authorization & authentication on a per-resource basis as much as possible.Agenda
Creating new users will serve to highlight a few concepts at the model layer
We'll also have an opportunity to start defining routes that require a user to be authenticated, and routes that don't.
For our purposes, we'll use a JSON Web Token (JWT) and the OAuth 2 password grant standard, as a mechanism and vehicle for authentication. You will be provided with a client-side app that will talk to our Phoenix, via JSON.
We'll validate a user's credentials in a way that's not incredibly sensitive to timing or brute force attacks, and then assemble our little piece of session state (the JWT) before encrypting it and handing it back to the client.
We often have a concept of roles (or an equivalent concept masquerading as other flags/fields) built on top of our authentication. We'll add roles to our JWT, and design a plug that will raise an error if a user attempts to access a controller action without having the required roles.
We'll recap everything we've learned.
Mike is an opinionated framework and functional programming junkie, frontendmasters.com instructor, frequent open source contributor and global speaker on modern web technologies. He has taught developers all over the world how to use Phoenix, including at Ericsson Headquarters in Stockholm, Sweden - the birthplace of Erlang, and in Sao Paulo Brazil - the birthplace of Elixir.https://twitter.com/MichaelLNorth https://github.com/mike-north