/janus

Primary LanguageElixirMIT LicenseMIT

HIGHLY EXPERIMENTAL AND IN ACTIVE DEVELOPEMENT: USE AT YOUR OWN RISK

Janus

Roman God of beginnings, gates, transitions, time, duality, doorways, passages, and ending

Wikipedia

I've been a huge fan of GraphQL and everything that specification has accomplished. When compared to other similar wire standards, GraphQL's combination of power, tooling, community support, extensibility and other amazing tools like Apollo Federation blow other tools out of the water (in my opinion).

But when I saw The Maximal Graph by Wilker Lúcio, the simplicity and power of Pathom's model converted me. Thanks to Wilker Lúcio for all the great work, it's only because of his work (and presentations) on Pathom that this library exists!

Janus is basically Pathom in Elixir. See the following for more details:

Musings

Should properties emulate the Pathom form or follow a more idiomatically Erlang/Elixir form?

# more like Pathom/EQL
prop = :"foo.bar/baz"

# more Elixir/Erlang-y?
prop = {Foo.Bar, :baz}

The former looks less like Elixir, but using 2-tuples as map keys doesn't look super Elixir-y either...

# more like Pathom/EQL
map = %{"foo.bar/baz": :test}

# more Elixir/Erlang-y???
map = %{{Foo.Bar, :baz} => :test}

I (personally) don't find either of these options truly satifying...

Maybe create a Map-like substitute struct that behaves like a map, but presents itself differently to seem more idiomatic?

As an aside, I understand that Clojure (and I would assumes Lisps in general) are built intentionally with this type of thing in mind, while Erlang and Elixir were not, hence the friction. I still find the core idea of namespaced attributes + resolvers compelling enough to attempt in this (amazing) language regardless.

At the moment, I think I still prefer the 2-tuple option for Elixir (although I'm open to being persuaded on this point).

Or maybe use a more struct-based approach for query expression, rather than the data-oriented apporoach that Clojure's EDN and EQL uses? Which would potentially avoid this entire issue? I'm a huge fan of the whole query-as-data premise though because it allows for 'straight-forward' query manipulation using existing core functions.

Roadmap

  • support subqueries (i.e. ident + join + recursion)
  • Janus.Runner + Janus.Resolver (i.e. batching, async, transforming, etc.)
  • weight alorithm behaviour (i.e. OR branches) for Janus.Runner
  • Janus.Plugin (e.g. middleware, interceptor, behaviour, walker / planner / runner lifecycle hooks)
  • Janus API (i.e. process/parse, index)
  • index
  • docs and typespecs for attributes (docs for resolvers?)
  • mutation
  • subscriptions
  • export, defer, live, stream
  • dynamic resolvers / GraphQL integration
  • Inspect
  • Error Struct(s) instead of {:error, reason :: term}
  • resolver diplomats for Tesla, Ecto, :ets

Related Next-Steps

  • EQL (rename?) wire format
  • Janus Plug
  • Generic Digraph Phoenix.LiveView Plug
  • graphiql / graphql playground / visualization Plug (via Phoenix.LiveView)
  • Janus Phoenix templates