Hacker News new | past | comments | ask | show | jobs | submit login
Aino, an experimental HTTP framework for Elixir (oestrich.org)
97 points by clessg 35 days ago | hide | past | favorite | 12 comments

> I have listened to several mentors of mine from REST Fest talk about how we shouldn’t keep state on the server for web/API clients. It’s conversations and lessons like these that are guiding me towards exploring something like Aino.

I feel the same way. I'm not a fan of approaches that keep so much state on the server (LiveView and its clones). That's a disagreement I have with Phoenix's general direction.

Out of curiosity what is the objection (and is it any worse than keeping a database on the server?)

Imagine the database were compiled into the application itself. If you only ever have one http server and one database, it's not an issue. If you run into load issues and need to scale out, things become trickier. Moving the database to its own application (say, mongo or redis or postgres) means that your main application can now reside on one machine while your database(s) can reside on a beefier machine (or just multiple machines).

Likewise, 'stateful' applications keep local state, and 'stateless' applications push everything off to either the client or database. They become fungible commodities- at peak load, you can quickly spin up more and shut them down after. With a 'stateful' application, you need to keep it running as long as the sessions (and maybe background jobs or any other local state) are live, unless you have some trivial means of passing that off to another machine.

Load balancing also means needing sticky sessions (i.e. once a client connects to an application server behind the load balancer, the load balancer needs to be able to keep sending the client to the same server). It's a solved problem, but needing to deal with it at all is a thing.

The web got along just fine with stateful applications for years, and things like websockets mandate a certain amount of statefulness, so there's nothing wrong with using it if you want to... OTOH you might just save yourself a lot of pain and effort if you don't. Right tools for the right jobs and all that.

Most of these things are pretty easy in elixir phoenix... And the tradeoff is that if you are sending gobs of http and also doing a fresh db query each time, it's wasteful and can possibly hurt latency too; phoenix tries to solve that. Conceptually one way you can think of it is that it's just a "smarter (in the feature sense, not necessarily the access-optimized sense) cache layer" in front of your DB.

In my practical experience of a moderate sized e-commerce site heavily using liveview I LOVE state on the server. Not having an API and heavy client code is so great.

> With the current trajectory of Phoenix, I’ve been thinking a lot on how to keep “dead views” still alive. I personally think they have a seat at the table. I have listened to several mentors of mine from REST Fest talk about how we shouldn’t keep state on the server for web/API clients.

I don't understand this part. What are "dead views"? I would have thought maybe they are views to which no client is connected anymore, but the rest of the paragraph seems to contradict that. What is the "current trajectory of Phoenix" that is alluded to?

"Dead views" are what folks are now calling non-LiveView routes in a Phoenix app. I think it will sort itself out - LiveView has its place but non-LiveView routes have theirs too.

Thank you, it makes sense now! I had missed that bit of lingo.

Eric is a really sharp guy, I’m super interested to see where he goes with this. The ideas of a Clojure/Ring inspired HTTP framework in Elixir land it really cool.

Would this be better/simpler for API/endpoints?

I think for simple API endpoint Plug is still the best option

A simple example

    defmodule AddressParser.PlugHandler do
      import Plug.Conn
      require Logger

      def init(options) do
        Logger.info("Running #{__MODULE__} with Plug using")

      def call(
            %Plug.Conn{method: method, request_path: request_path, query_string: query_string} = conn,
          ) do
        request_id = generate_request_id()
        Logger.info("#{request_id} #{method} #{request_path} #{query_string}")

        case handle_request(method, request_path, URI.decode_query(query_string)) do
          {:ok, response} ->
            Logger.info("#{request_id} sent 200 ok")

            |> put_resp_content_type("application/json")
            |> send_resp(200, Jason.encode!(response))

          {:error, status, response} ->
            Logger.error("#{request_id} sent #{status} #{response}")

            |> put_resp_content_type("text/html")
            |> send_resp(status, response)

      defp handle_request("GET", "/parse", %{"address" => address}) do
        {:ok, Address.parse(address)}

      # more handlers

Reminds me of sinatra

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact