Debugging Gigalixir:
- run locally:
MIX_ENV=prod mix phx.server
- if still issue,
gigalixir logs
react-native-
W
eb
+ Relay + Absinthe + PostgreSQL
At a bare minimum, I'd go through:
- Learn Elixir (free!)
- Learn Phoenix ($23, Kindle-compatible)
- Learn Absinthe ($18, w/ 30% discount code in the back of any PragProg book)
- WRAP Stack Walkthrough (free; how to put all of these technologies together for your first project)
- Learn Relay (free; implement with an Absinthe backend; skip to Implementing the Login Mutations for Step 5, and use Ch. 8 of Learn Absinthe for authentication)
- performance (backend): Absinthe is ~10x faster than NodeJS, Python, Ruby, and (probably even more for) PHP because it can use multiple cores concurrently
- performance (API): GraphQL (Relay + Absinthe) allows for more efficient communication between your frontend and backend than REST
- productivity (backend): Absinthe is built on top of Phoenix, Elixir's main framework, which provides a Rails-like developer experience for the backend
- productivity (frontend):
react-native-web
will save you time with cross-platform apps and make it easier to reason about your component hierarchy; Relay allows you to put your API with its component--a dramatic timesaver and bug-reducer compared with Redux - platform: Elixir (Absinthe is an implementation of GraphQL written in Elixir) is built on top of the Erlang VM, which allows for fault-tolerant apps using the OTP API and live uploads to your production app; Phoenix's channels make setting up websockets easy--Absinthe leverages this to allow for real-time apps via subscriptions
Love every part of your stack! Try WRAP:
- Meet creator of Elixir, José Valim
- GO DEEPER: Elixir vs. Go
- Learn Elixir
- Practice problems
- ElixirForum (like StackOverflow, but specifically for Elixir developers; responses within a few hours, in my experience)
- Erlang modules -- extra Elixir-style modules, in case you don't find a particular function in Elixir
- Learn Functional Programming with Elixir
- GO DEEPER: Learn Metaprogramming in Elixir
- Intro to Functional Programming Design
- "Phoenix is Not Your Application" (speaks a bit slow--can watch at 1.5x speed)
- GO DEEPER: Functional Development with Elixir
- Intro to Domain-Driven Design
- GO DEEPER: Domain Modeling Made Functional
- Learn Phoenix
- Updates to Phoenix (just do
⌘ + f
for Creating schema--the rest is dated)Because Elixir is a functional programming language, Phoenix uses schemas rather than models. A schema defines a struct's fields, its relationships to other structs, and changeset(s) for it (which handle validations and constraints).
mix phx.gen.context
generates a context which holds the API for all schemas within that context (in an object-oriented language, these functions would be defined within each model). For example, instead of defining functions for user authentication within/lib/<APP>_web/models/user.ex
, one should generate anAccounts
context and write the functions in/lib/<APP>/accounts/accounts.ex
(User
will be passed as an argument). - Phoenix Presence
- Hot Module Replacement API &
hmr-brunch
- Mox
- Property-based testing
- Testing database interface concurrently
- Updates to Phoenix (just do
react-native-web
setup (can also server-side render)- Native Litho (Android) & ComponentKit (iOS) frameworks
- Lazy-loading/Code-splitting (EXAMPLE)
- Web-only (incl. mobile browsers) basic data viz components
- Creating a link
- Making an
absolute
element pressable
Note: I haven't found clear/explicit documentation for using
react-native-web
SSR with Phoenix.- Underscore.js -- library with functional programming methods
|>
and patternmatch
JavaScript plugins
-
GO DEEPER: GraphQL server overview (NOTE: Absinthe's implementation is slightly different)
Instead of passing a data
prop down the entire component hierarchy (which is why a fragment is a specification of data requirements), you can use React's new Context API. Further, if you don't use Relay, you still don't need to use Redux...!
-
"Relay only allows components to access data they specifically ask for in GraphQL fragments — nothing more."
- GO DEEPER: Relay Deep Dive
-
Relay tutorial -- NOTE: Skip to Implementing the Login Mutations for Step 5: Authentication; the strategy used is insecure. Ch. 8 of the Absinthe book provides secure auth on the backend.
- Relay docs
- Sending a request with variables (not covered in the book)
- Relay DevTools
-
GO DEEPER: Relay Subscription strategies
-
"Found Relay runs queries for matched routes in parallel, and supports fetching Relay data in parallel with downloading async bundles from code splitting when using Relay Modern."
- On routing
"Found Relay has a naive approach to client rehydration that is less than ideal."
found-relay
- On routing
To deploy your frontend and backend separately, deploy your frontend /build
to a CDN, and deploy your backend to a Plaform-as-a-Service below, with some configurations.
- If you are deploying to a cloud platform (AWS EC2, Google Cloud), use Distillery
"Distillery is a release tool. It builds a deployable artifact."
- GO DEEPER: Continuous Integration & Delivery
"Ansible isn't meant to be a continuous integration engine."
- DevOps 2.0 (check out the free sample)
-
GO DEEPER: Erlang VM Garbage Collection
-
GO DEEPER: The Erlang Runtime System
-
Elixir is built on top of the Erlang VM, which powers something like 50% of the phone system. This means Elixir code is compiled into Erlang VM code, which is then transpiled into assembly code for your machine.
Elixir builds upon the Erlang VM. The Erlang VM supports zero-downtime upgrades, runs isolated processes (so if one crashes, none of the others do and the one that crashed is restarted by a Supervisor process), and--while not the fastest of all programming languages--is an order of magnitude faster than PHP, Python, Ruby, and Node.js because it leverages multicore processors to run processes in parallel. As of the mid-2000s, computers' processing power has remained stagnant and the trend has been to include more cores in computers. As this trend continues, the speed of concurrent languages like Elixir over single-threaded languages will only grow.
Elixir also emerged out of the Rails community. José Valim, the author/creator of the language, was part of the Rails core team. In my view, he took the best of both Erlang and Ruby on Rails to produce Elixir. Same for Chris McCord's Phoenix framework and the whole ecosystem.
It is important to note Elixir is a functional programming language. This means any data stored in memory is immutable. If you no longer use it, it becomes "garbage" to be cleared. Additionally, developers think in terms of steps of functions rather than aspects of objects. Instead of calling a function on a datum, the datum is passed as an argument to the function.
Elixir is a server-side scripting language, meaning it handles requests from the browser and returns responses.
Now let's delve into its datatypes:
Basic
- integer
- float
- boolean
- atom
Integers are numbers. Floats are numbers with decimal points. Booleans consist of true
and false
. All expressions of code (which are like clauses in sentences) have a true
boolean value, except false
(which is a boolean value) and nil
which is what some functions return on failure. An atom begins (or, as we'll see later, can end) with :
. It is intended for use as a unique value.
Advanced
- string
- tuple
- list
- keyword list
- map
You'll notice I did not include strings (words/letters/anything in double-quotes) under Basic.
When code is compiled to assembly (for the machine to understand), everything is converted to binary. Binary means base 2. That means all code is represented as a series of 0
s and 1
s. Why? Computers work because they have transistors which can store a charge. If a transistor is off (it does not have a charge), this can be represented as 0
.
Numbers are simply converted from base 10 to base 2. Booleans are binary. Floats and atoms are more complicated, but each is a single value. By contrast, a series of contiguous values can also be used to represent data. A string is a series of numbers which have been converted into human-readable characters. AKA, all of the characters in a string can be converted into base 10 numbers, which can then be converted to binary (likely implemented with a special leading binary sequence to differentiate them from integers). The series of continguous values can be represented as a tuple or a (linked) list. A tuple is represented as {}
. A list is represented as []
. Tuples are used to store short series. Lists are for series that are long enough that they may not be stored continguously in a computer's memory. Memory comes in units of bytes, which are 8 bits. A bit is a binary unit (a 0
or a 1
). So a short value can fit in a byte. A longer value needs to be split across bytes.
The most common use of tuples in Elixir is pattern-matching. Often, the tuple will have only 2 values. The first is an atom. You set cases for each possible atom where you do something with the second value of the tuple. Linked lists perform the function of arrays in other languages: storing series of values which you can go through or alter via functions.
There is a third data structure called a keyword list, which is a linked list of 2-value tuples: [{}, {}]
. The first value of the tuple must be an atom (a key). Additionally, keyword lists can be represented as {key: value, key2: value2}
. Keyword lists are often defined as the last argument of a function. Because a keyword list is a set of values, this means you can pass a ranging number of arguments, each of which has a specific use in the function as identified by its key.
Lastly, there are maps, %{}
. In a keyword list, the first value of the tuple must be an atom and the list is ordered. A map is unordered and the key of the key-value pair can be any basic datatype (literals) or a string. The value can be any datatype.
The language has additional datatypes but these are generally the most relevant.