
Elixir, Phoenix, Absinthe, GraphQL, React, and Apollo - schneidmaster
https://schneider.dev/blog/elixir-phoenix-absinthe-graphql-react-apollo-absurdly-deep-dive/
======
xlii
Stack described above is the one I’ve been working on for professionally for
the last year and I wouldn’t recommend it.

Main reason is the absurd amount of complexity with costs heavily
outhweighting benefits gained from the solution.

For example, simple task of adding new entity consists off: On backend:
creating migration, creating data entity (Ecto), writing structure module
(sanitization, validation, basic logic, if needed), mounting queries, writing
input mutation(s), writing output query/queries, unit testing On frontend:
creating component, creating form, writing graphql queries, writing mutation,
wrapping components/queries in components, connecting query to components,
providing action handlers for inputs, unit testing, integration testing

Now I have authors list. And even though I am full stack I haven’t yet spent
even single minute on having Proper UX Design set in place. Oh, do we need to
add author’s birthdate? Dang, let me adjust all of that.

In my opinion technical debt accumulates faster than in other solutions.
GraphQL is complex. React (done right) is complex. Apollo is complex (Elixir
is simple, yet it’s only one cog). Deciding on doing file upload in GraphQL
led me to a rabbit hole, which took at least a week to dug out from.

When trying to find the source of all development issues my thoughts go
towards GraphQL. Maybe it is too complex or we didn’t had enough experience in
it? Yet it was really nice to work with when all the backend and frontend
boilerplate was already written. It makes sense, even though requires some
heavy thought behind it. Maybe it’s Apollo, which locks in one of two specific
trains of thought, or Absinthe, which requires side-hacks in order to get some
bit more advanced features working with Apollo, like file uploads or query
batching.

From a perspective I’d say this is just too much. Every single part of this
stack adds overhead to the other parts. Maybe if I had more specialized team
members it would get easier, but being only 3 of us, with me, as a full stack,
constantly switching between all of that was a tiresome, low-productive
effort. Right now we’re disassembling app to a classic REST-app and we’re
seeing development speed increase on week-to-week basis, even though we had to
scrap most of the frontend.

I guess there would be some benefit on having the write up of all of it, since
it doesn’t even scratch the surface of the year of development issues with
this stack, but even in this very "short" form it may serve for a word of
warning that this stack is not necessarily something you want to care for.

~~~
pmontra
Similar experience but without GraphQL. We had server side rendering with a
Node server. Our production server became a Node farm with Phoenix +
PostgreSQL requiring less than 1 GB of RAM and Node using at least 8 extra
GBs. We eventually ditched SSR, send the React app and wait for it to render.
We're back to 1 core and (mostly unused) 4 GB. It's a business application
with complicated UI, customers don't mind waiting a couple of seconds of they
want to start from a bookmarked screen.

For a simple UI I'd generate HTML server side with eex and spare us the cost
of front end development. It's also a productivity nightmare. The amount of
work needed to add a single form field with React/Redux is insane.

~~~
dafrie
Just a quick one: why would you need redux for forms? This is in my opinion a
total overkill.

I have forms either having their own state or (preferred) just use Formik for
all of this. In my stack, this then allows to just add a field in the GraphQL
schema (backend), add it in the query, add the formik field + yup validation
and done.

~~~
jypepin
Some people would argue that if using Redux, also having local state logic is
an anti pattern.

That would mean that if you use Redux, a form also requires actions for form
update/submit/success/error and the form data should be stored in the redux
store.

That is one of the main issues I have with Redux, which I feel adds automatic
complexity for simple things, but at the same time I'm not sure if it's very
good to have a mix of tings happening from store/actions/reducers and others
from local state/ajax.

~~~
ryanbrunner
> Some people would argue that if using Redux, also having local state logic
> is an anti pattern.

I won't disagree that this is a popular opinion, but there's little practical
benefit to storing state that's truly local to a single component (or a very
small tree) in Redux just because it's there.

Even the maintainers of Redux maintain that it's perfectly acceptable to use
local state - [https://redux.js.org/faq/organizing-state#do-i-have-to-
put-a...](https://redux.js.org/faq/organizing-state#do-i-have-to-put-all-my-
state-into-redux-should-i-ever-use-react-s-setstate)

------
benwilson-512
Hey folks! Co-Author of Absinthe here, happy to answer any questions about it.
The post here is good, although these days we recommend using Dataloader vs
Absinthe.Ecto. Dataloader really extends the idea behind Absinthe.Ecto while
providing in request caching, pluggable backends, and easier query
manipulation.

~~~
AlchemistCamp
I've got an app with back-end oauth-based login. I had a bit of a headache
integrating the sessions with Absinthe and finally arrived on this in my
Context module:

```

    
    
      def call(conn, _) do
        context = build_context(conn)
        Absinthe.Plug.put_options(conn, context: context)
      end
    
      def before_send(conn, %Absinthe.Blueprint{} = blueprint) do
        if blueprint.execution.context[:logout?] do
          Auth.drop_current_user(conn)
        else
          conn
        end
      end
    
      defp build_context(conn) do
        with ["Bearer " <> token] <- get_req_header(conn, "authorization"),
             {:ok, data} <- MyApp.Token.verify(token),
             %{} = user <- get_user(data) do
          %{current_user: user}
        else
          _ -> %{}
        end
      end
    
      # rest of the file

```

Then made a Logout middleware that sets logout? to true in the resolution
context.

Is digging into the Blueprint as in the code above necessary? Is there a
simpler way of solving this?

------
kuon
I just started a new app, and I decided to try Phoenix LiveView. It's really
simpler and easier to work with. It won't work well with all kind of apps,
especially those with offline support, but for many many apps/website it is a
good fit.

The last app I did was a mobile app written in Elm with Phoenix. I can't
recommend Elm enough, it makes JS much easier to work with. For communication
I used a simple REST oriented API.

While I understand why GraphQL exists, I think it is a major addition in
complexity for little to no benefits. I tried it, wrote half a project with
it, but they removed it. When things gets complex (for example, let's say the
user.email field can be redacted under some conditions) your endpoint becomes
really hard to manage. GraphQL certainly have a use for large API with graph
oriented datasource (well, like facebook), but it is a specific use case.

~~~
beckler
I want to like Elm, but every time I try to pick it up it's always my biggest
time sink.

~~~
mercer
Could you elaborate? I'm on the fence about Elm mostly because of 'ecosystem
issues' (single dev, various complaints about communication, etc.), but more
than once I've almost started a project because I figured the practical
benefits would still be worth it.

------
sugarpile
Does anyone here who is working with elixir professionally have a sense of
what kind of mastery is needed to jump into an elixir dev role? I've used it
on and off for ~4 years at this point and in a few side projects (most recent
one using everything in the title, weirdly enough) but can't really tell if
I'm "qualified" to look for a job in it. It's this weird loop of "I've only
done something as a hobby so I'm not qualified to do it professionally" vs "If
I don't use it professionally I'll never be qualified to do so".

Same goes for react.

~~~
simonw
Smart teams hire great engineers and trust them to get good with the stack at
hand. Don't get hung up on how much experience you have with a particular
stack when looking for jobs.

I've hired Rails developers for Django roles and seen them get productive
within weeks.

If you've used Elixir on-and-off for four years you have more experience than
the majority of engineers who might be considered for an Elixir role.

~~~
srndh
> I've hired Rails developers for Django roles and seen them get productive
> within weeks.

What did you like about the Rails developer that you chose the person for the
role? Basically, I want to know irrespective of the language, how do I show my
"quality as an engineer"?

~~~
volkk
oh it's simple, you just have to invert a binary tree

------
diNgUrAndI
> I also find a lot of joy in its pattern matching and its pipe operator. It’s
> refreshing writing heavily functional code that still approaches the beauty
> of ruby, and I find that it drives me to think clearly about my code and
> write fewer bugs as a result.

This.

I use Elixir everyday. While it feels 'refreshing' to use pattern matching,
sometimes it gets over-used, as a simple swtich-case statement is easier to
read.

Same with pipe operator. I found newbie programmers tend to pipe for the
pipe's sake, and write functions just to form the 'shape' of pipe, but the
data shape changes are not easy to see and inspect.

~~~
jaequery
Doesn’t pattern matching lead to either more verbose code or a duplicate code?
Most of the time?

~~~
hdra
It could, when its used "wrong", though thats usually a result of trying to
fit pattern matching where there are other more idiomatically way to express a
code. I wouldn't say "most of the the time", its more of a common beginner's
mistake.

Pattern matching allows you to express the code in a more declarative way, and
usually would result in a more succinct code.

------
hliyan
Great article. One point though (not aimed at the author).

The majority of engineering articles I see today are along the lines of "X
with Y, using Z", where X, Y, Z are specific products, frameworks or
libraries, often trademarks.

I rarely see more generic engineering/architecture topics such as: [virtual
DOM based / string based client side templating] with [JSON/Protobuf] over
[REST/Websocket] with a [compiled / interpreted / JIT compiled / virtual
machine based] backend runtime, built on [RDBMS/NoSQL/hybrid] etc.

Am I alone in feeling this way?

~~~
ngngngng
I'm actually writing a tutorial series aimed at programming beginners where we
implement things in multiple languages/frameworks side by side.

I think it's a great way to learn concepts of software engineering instead of
just memorizing conventions.

~~~
amatecha
Yeah this actually sounds awesome, do you have anything live/bookmark-able
yet?

~~~
ngngngng
I have the first few lessons 90% completed but not published yet. You can
bookmark my personal site natemeye.rs since they will initially be posted
there before I move them to their own location.

------
em_jones
I've just been trying to get back into elixir recently, myself. I'd done some
basic crud 'helloworld' stuff when I first tried about a year into
professional development.

I've since had the Fortune to spend time learning about cloud native apps,
distributed service patterns, and supporting infrastructure (spring cloud,
pcf, vanilla k8s, gcp) and now returning to elixir having at least better
understanding of what erlang and OTP offers.

I'm super excited to see I'm not alone in finding this sort of stack is worth
fiddling with(although, I tend to pick Vue when not using angular for work).

Thanks for posting this!!

For those interested in what resources I'm leaning on: The Manning 'Elixir in
Action' and the Pragmatic Programmer's graphql texts along with exercism.

Anybody else have any preferred resources for these technologies?

~~~
nickjj
I almost exclusively learned the basics of Elixir / Phoenix by looking at the
source code of [https://changelog.com](https://changelog.com) which is at
[https://github.com/thechangelog/changelog.com](https://github.com/thechangelog/changelog.com).

Major kudos to them for open sourcing their platform. It covers like 50+
common web dev features.

I pretty much skimmed the docs to get the ultra basics, looked at that source
while I was building my own app and then asked questions when I got really
stuck. Even managed to sneak in some refactoring on their code base a few days
into learning Elixir. It's super approachable without much more if you have
some type of programming background beforehand.

~~~
em_jones
This is a sweet shout-out. Thanks!

------
stephan83
Elixir and OTP are really nice, but I'm frustrated by the type system, even
with typespecs. I finally gave in and started learning Haskell. I would still
choose Elixir/Phoenix for some web apps though.

~~~
lame88
Expect a long journey if you’re trying to get to the state where you can make
a web app. I am making a conjecture here but from my limited but nontrivial
experience it seems libraries in the applications ecosystem of haskell apply
the most advanced features they know of to solve their problems. That would
normally be fine, but they do not abstract away this complexity; due to the
type system’s rigor, these choices are propagated to you as the consumer of
the library because you have to consume the library in a way that satisfies
whatever advanced type features they have. So the end result is that to get
anything done like make a basic Yesod web app you have to be at an advanced
level. I feel i benefitted from learning the structural and type theoretic
aspects (monads etc) of Haskell but I also am glad i ditched it after some
time because the investment just did not seem worth it anymore after a certain
point. Anyway just my perspective, hope its helpful if you find similar
difficulties. Right now I’m checking out Scala as an alternative, but i’ve
just started so I can’t offer any comparison other than I know it is less
rigorous.

~~~
regularfry
The problem I have with Haskell for getting anything actually done is that
nothing I try to do actually _works_. I mean in the basic "try out the
tutorial to learn the thing and be able to get to the end without an error I
don't have enough experience to debug" sense. Stack should help with this, but
it doesn't when the versions you get (and function signatures of those
versions) have changed since the docs were written. Cloud Haskell is an
example of this: the tutorial docs are incomplete (the very first command they
have you write is wrong, among other things), and they don't specify a stack
version so the Hello World example doesn't build with the current snapshot and
there's a really odd change to a public API I just can't get my head round.

I've had the same experience with Yesod and Scotty too, although not recently
- every time I decide it's been long enough for the ecosystem to mature a bit
more, I bounce off issues like this which I just don't get elsewhere, and
decide to give it another couple of years.

~~~
lame88
I'm not familiar with Cloud Haskell, but I can attest that documentation and
information availability in general have become a huge priority for me
recently after getting burned by lack of it in recent years. This is actually
very tricky to nail a sweet spot in, because it almost requires that a
technology be currently popular, yet stable. There are long-lived Haskell
libraries that have poor documentation and Haskell is rare enough that there
aren't often answers to common pitfalls on StackOverflow etc. Ruby on Rails
still has ongoing releases, but its popularity has declined and apparently so
has its sources of community information beyond the official docs, which only
cover so much. And then you have the immature, fast-moving JS ecosystem.
Ironically for these reasons I could easily see myself choosing Java for
projects in the near future; I don't like the language, but I've learned that
the problems I have with Java are not my biggest problems as a professional
engineer anymore.

------
rossenberg79
Why is Elixir always being paired with Phoenix? Can’t I just have a backend
API running on Elixir and a javascript front end to interact with it? I’d
prefer a simple React, Postgres, Elixir stack (REP).

~~~
bitcrusher
Yep! Just use Plug. There’s no need to drag the entire Phoenix stack in.

[https://hexdocs.pm/plug/readme.html](https://hexdocs.pm/plug/readme.html)

~~~
out_of_protocol
Then you'll need to create own version of migrations, routing, configure asset
building pipeline etc etc etc. Phoenix have everything in place, without need
to re-implement lots and lots of basics

~~~
Xixi
A bit pedantic, but migrations are provided by Ecto [1], not by Phoenix. You
don't need Phoenix to use Ecto.

[https://hexdocs.pm/ecto/Ecto.html](https://hexdocs.pm/ecto/Ecto.html)

~~~
out_of_protocol
Well, you'll still need to add Ecto, configure it to use separate configs for
prod/dev/test environments etc - so point still stands :)

------
onebot
I have just starting learning Elixir and Phoenix. It seems like a solid
replacement for Rails. But I keep getting hung up on performance. With React,
it seems like you are developing more API back-end, then web server back-end
(if that makes sense). But when you look at the performance compared to Go and
the steeper learning curve, why not just use Go? Kubernetes really solves the
share-nothing let-it-fail aspect. Obviously, Erlang/Elixir are hands-down a
good fit for fault tolerant distributed systems where performance may not be
as critical or you can use NIF. But outside chat and a few other use-cases,
not sure the majority of web services falls into this. However, the functional
code is so nice. Anyways, any input on how to convince engineering leadership
how Elixir might be better than Go would be helpful.

~~~
iends
For non-stateful services, it seems very difficult to me to convince people to
use Erlang/Elixir over Go. It also seems to me that Go is going to be more
maintainable over time than Elixir. Also, Elixir has smaller corporate backing
compared to Go.

fwiw, I work at a telecommunications company and I've had no success
convincing anybody to use either Go or Erlang/Elixir over Java (& Clojure).

~~~
onebot
That’s ironic considering that Erlang was designed for telecommunications.

But I think statefulness is the right context between the two.

Why do you think Go would be more maintainable?

~~~
iends
I realize dialyxir exists, but I think a lack of static typing can lead to
some maintainability issues over long term. At least this is my experience
working on a 6 year old JavaScript codebase.

------
adamnemecek
Ok, for backend, I’m deciding between rust with actix, jvm (kotlin +
akka/quasar) or elixir.

I kinda know the theoretical differences but I’m curious what hn thinks. Has
someone actually deployed things in some of these.

I do have preference towards rust but maybe there’s something better.

~~~
SaltyBackendGuy
Dotnet Core is actually really nice as well, I think v3 is right around the
corner. Something worth checking out if you're looking into beckend
technologies.

I've been using it lately, coming from Node.js for the last few years, and
it's been a really nice change.

~~~
harrygeez
I've been on the fence about trying dotnet core for perhaps too long. What do
you like about it?

------
AlchemistCamp
The project I've been working on the past couple of months is a very similar
stack to this—Phoenix, Absinthe, Apollo, React, Redux.

The main difference is I've been using TypeScript and the Absinthe resolvers
are using Dataloader.

~~~
TN1ck
How's the integration of Apollo and TypeScript? Do you like the stack and
would you recommend it?

~~~
AlchemistCamp
Both apollo-client and react-apollo ship with definitions in their npm
packages, so using TypeScript with Apollo isn't a problem.

I don't like the stack. It's way too heavy. If I were building everything
again now, I'd go with a standard Phoenix app and ditch Redux for sure and
hold off on fullblown front-end frameworks and Graphql.

I'd ship v1 with Phoenix, UJS and Turbolinks, and it would take 1/4 the time.
Then, _if_ the project grew to where it needed and could support a larger
team, I'd bring in Absinthe, Apollo and then finally Vue.

My main recommendation would be to keep things as simple as possible for as
long as possible. Use something like Phoenix or Rails or Laravel and get stuff
shipped.

------
sntran
It always bugs me that, with Absinthe, you have to define Ecto schema and
GraphQL schema separately, when most of the time, they are very similar. Can
Absinthe somehow figure out from Ecto?

~~~
pdimitar
Always wanted something like that as well. I am guessing until we get
something like `clojure.spec` -- true typing, even if gradual (but at least
not a success-typing) -- then it's not happening anytime soon.

------
hestefisk
Good read. Curious about Elixir. Why would one use this over ‘plain old’
Erlang / OTP apart from the Rubyesque syntax, which might appeal to RoR devs?

~~~
schneidmaster
It's 100% the syntax. It appeals to ruby devs and also I think it's a lot
easier to grok in general. But you can write Erlang code directly in an Elixir
file, you can use Erlang libraries directly in Elixir, and Elixir compiles to
the same BEAM instructions that Erlang does, so there's functionally no
difference in terms of capabilities or what happens when they run.

~~~
SteveGregory
This is not totally true. It’s not 100% syntax. Elixir has one additional and
widely used feature: macros.

In Elixir, as in Lisp, you can transform your code as a data structure into
new code. This is importantly used in Ecto, the de-facto object data mapper in
Elixir. It’s also used in Absinthe, a library from the posted link for making
GraphQL APIs.

~~~
ramchip
Protocols are also an important feature. `IO.inspect`, `Enum`, etc. are very
nice additions.

~~~
elcritch
In general the standard library organization is superb and well laid out. It’s
the only language I don’t have to consistently lookup "how do I do X with
string", or “Y with a list".

------
macintux
> much friendier syntax for developers

This drives me batty. More familiar? Sure. “Friendlier” is very subjective.

------
yeskia
Loved this deep dive - appreciate you going step by step and explaining each
part.

~~~
schneidmaster
Thanks! Glad you enjoyed it :)

------
swyx
great write up. shall we dub this the PAAGER stack?

~~~
Intermernet
APGEAR

~~~
cpeterso
parage (ˈpærɪdʒ) n 1\. (archaic) lineage, family, or birth

------
sergiotapia
When you get to the absinthe part you lose me. It's the same gut feeling I had
when I see Redux where it works but it was created at the very beginning of
this entire workflow discovery and better tools/approaches have since come
out.

~~~
AWebOfBrown
Can you elaborate on what they are, for the curious?

------
johnnyji
Nice! This is 1 for 1 the exact tech stack we use at Distru :D

