Hi, I am new to Phoenix/Elixir and I am in the process of creating a simple API and I am trying POST, but I am getting a 400 bad request response. Phoenix throws the “no function clause matching in PoorNoMoreWeb.TransactionController.create/2” error. Delete is working, index is working, show is working.
I have checked out other solutions and they do not seem to be working. So any help would be much appreciated. Below is my create in my controller:
def create(conn, %{"transaction" => transaction_params}) do
case Expenses.create_transaction(transaction_params) do
{:ok, _transaction} ->
transactions = Expenses.list_transactions()
render conn, "index.json", transactions: transactions
end
end
Here are my routes:
scope "/api", PoorNoMoreWeb do
pipe_through :api
get "/transactions", TransactionController, :index
get "/transactions/:id", TransactionController, :show
post "/transactions", TransactionController, :create
put "/transactions/:id", TransactionController, :update
delete "/transactions/:id", TransactionController, :delete
end
scope "/", PoorNoMoreWeb do
pipe_through :browser
get "/*path", PageController, :index
end
This means it found the function, but no clause matched.
This means no implicit arguments, so it must be passed in the POST parameters.
This means it is mandating a parameter named "transaction".
However, we don’t know what’s being sent, so we need to see the form that is sending the POST request…
— OR —
Add a new catch-all clause for now and throw the params to see what they ‘actually’ are, like this create after your above create:
def create(conn, params), do: throw params
And see what gets logged. ^.^
In general right now though it is acting as programmed, it is expecting a "transaction" POST field (usually the form name) but it is not getting it, so it is rightfully saying that the client request is in error.
If you are in debug mode using the debug page that pops up (the orange and light tan one) it’s on the rightside to the right of the related source code for the clicked-on part of the stacktrace.
/me is getting off work now so may not be able to respond until tomorrow, depending
Ah cool, in that case needs the line of and the lines above and below for context of lib/poor_no_more/expenses/transaction.ex:17, it sounds like a list might be getting passed into a jsonb or map or embed or so?
defmodule PoorNoMore.Expenses.Transaction do
use Ecto.Schema
import Ecto.Changeset
schema "transactions" do
field :amount, :decimal
field :category, :string
field :description, :string
field :merchant, :string
field :name, :string
timestamps()
end
def changeset(transaction, attrs) do
transaction
|> cast(attrs, [:name, :category, :merchant, :amount, :description])
|> validate_required([:name, :category, :merchant, :amount, :description])
end
end
Ah, I’m noticing that the maps inside the list map to the transaction, so I’m guessing it’s being passed as the list to attrs, in that case what is lib/poor_no_more/expenses.ex:54 and the surrounding lines?
I’m betting there’s a bug in that framework that is wrapping the object into the list, fixing that would be the ‘Proper’ fix, however as a workaround you can just change your function head from this:
def create(conn, %{"transaction" => transaction_params}) do
into:
def create(conn, %{"transaction" => [transaction_params]}) do
It’s really really weird that the javascript lib is wrapping the transaction object into a list though, that really should be looked into… Maybe it is using FormData instead of json? If so can you switch it to a JSON mode?
You’re right, that worked. I really appreciate you taking the time to help! I will looking into the framework side patch it on that end. Again, thank you!