andreashasse

andreashasse

PhoenixSpectral - FastAPI-style type-driven APIs for Phoenix

Hi all,

I’ve been working on PhoenixSpectral, a library that makes Phoenix controller @spec annotations drive both OpenAPI generation and runtime request/response validation — no separate schema definitions.

The main design departure from standard Phoenix is the action signature. Instead of action(conn, params), controllers receive five explicit arguments — (conn, path_args, query_params, headers, body):

@spec update(Plug.Conn.t(), %{id: integer()}, %{notify: boolean()}, %{"x-api-key": String.t()}, User.t()) ::
        {200, %{}, User.t()} | {404, %{}, Error.t()}
def update(_conn, %{id: id}, %{notify: notify}, %{"x-api-key": _key}, body) do
  case MyApp.Users.update(id, body, notify: notify) do
    {:ok, user} -> {200, %{}, user}
    :not_found  -> {404, %{}, %Error{message: "User not found"}}
  end
end

By the time update/5 is called, the incoming HTTP request has already been validated and decoded against the typespec: id is an integer() (not a string), body is a fully populated %User{} struct, etc. Invalid requests are rejected with a 400 before reaching the function. Each source is kept separate because the body is a typed struct (which can’t be merged into a flat map), and the OpenAPI generator needs to know whether a field comes from path, query, header, or body to emit a correct spec. Actions return a {status, headers, body} 3-tuple (union return types produce multiple OpenAPI response entries automatically) or a Plug.Conn directly for streaming and file responses. The repo also contains an example app.

Before stabilising the API I’d love to hear your feedback. Eg, Does five separate arguments feel ergonomic? Does {status, headers, body} as a return type feel right?

Thanks for any thoughts!

Where Next?

Popular in RFCs Top

bartblast
:test_tube: Could use a hand testing something. Just landed a protocol pruning enhancement - the compiler now drops protocol implementati...
New
KristerV
I got fed up with removing unused aliases manually so made a package for it: GitHub - KristerV/remove_unused_ex: Remove unused aliases an...
New
WyvernoDragon
I’ve been working on pgRx: a query engine that uses BEAM lightweight processes instead of PostgreSQL’s fork-per-connection model. The nu...
New
zachallaun
Note: There are a few folks I’d really love to hear from, time permitting. Pinging in case the title isn’t catchy enough :slight_smile: @...
New
pzingg
https://github.com/pzingg/phx_cookie_consent I took a phx.gen.auth application and added support for storing a user’s cookie consent set...
New
ca1989
Hi all, this week I played a bit with Phoenix and Elm. I ended up with this POC: https://github.com/carlotm/elmex The repository cont...
New
tmbb
I’ve started working on a toy project to compile extended POSIX-compatible regular expressions into NimbleParsec combinators. These combn...
New
Overbryd
Polyglot-Ex Rust and wasmex bindings for polyglot. State: Pre-release Hi there, I am starting a thread for my humble bindings library ...
New
tiagodavi
I’ve just created and published my first Elixir library! Ex4j combines the power of Ecto schema with the Bolt protocol + an elegant DSL ...
New
blubparadox
Hi all. If you’ve looked on Twitter or YouTube you’ve seen the 1 billion row challenge, usually done in Java. I’ve written an Elixir vers...
New

Other popular topics Top

siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30877 112
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

We're in Beta

About us Mission Statement