Why am I having this error?

I have spent a lot of time trying to figure this out but it’s going nowhere…

I am trying out Phoenix API

in router.ex I have:-

  scope "/api", PhoneBookWeb do
    pipe_through :api

    post "/persons", PersonController, :create
  end

in person_controller.ex I have this:-

  def create(conn, %{"person" => person_params}) do
    with {:ok, %Person{} = person} <- Contact.create_person(person_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.person_path(conn, :show, person))
      |> render("show.json", person: person)
    end
  end

When I run a test with Postman, I get:-

no function clause matching in PhoneBookWeb.PersonController.create/2
The following arguments were given to PhoneBookWeb.PersonController.create/2:

# 1
%Plug.Conn{adapter: {Plug.Cowboy.Conn, :...}, assigns: %{}, before_send: [#Function<0.24098476/1 in Plug.Telemetry.call/2>], body_params: %{}, cookies: %{}, halted: false, host: "127.0.0.1", method: "POST", owner: #PID<0.534.0>, params: %{"name" => "john"}, path_info: ["api", "persons"], path_params: %{}, port: 4000, private: %{PhoneBookWeb.Router => {[], %{}}, :phoenix_action => :create, :phoenix_controller => PhoneBookWeb.PersonController, :phoenix_endpoint => PhoneBookWeb.Endpoint, :phoenix_format => "json", :phoenix_layout => {PhoneBookWeb.LayoutView, :app}, :phoenix_request_logger => {"request_logger", "request_logger"}, :phoenix_router => PhoneBookWeb.Router, :phoenix_view => PhoneBookWeb.PersonView, :plug_session_fetch => #Function<1.38038610/1 in Plug.Session.fetch_session/1>}, query_params: %{"name" => "john"}, query_string: "name=john", remote_ip: {127, 0, 0, 1}, req_cookies: %{}, req_headers: [{"accept", "*/*"}, {"accept-encoding", "gzip, deflate, br"}, {"connection", "keep-alive"}, {"content-length", "0"}, {"host", "127.0.0.1:4000"}, {"postman-token", "e1b02619-0ebe-4aa6-a695-3d4b1c3460ac"}, {"user-agent", "PostmanRuntime/7.26.3"}], request_path: "/api/persons", resp_body: nil, resp_cookies: %{}, resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}, {"x-request-id", "FjISXtqtZnvNAqkAAAGD"}], scheme: :http, script_name: [], secret_key_base: :..., state: :unset, status: nil}

# 2
%{"name" => "john"}

It seems it hits the right function with the correct number of arguments. But why is %{“person” => person_params} causing issues?

Error:-
Screenshot from 2020-09-05 23-16-11

Thank you for helping…

You’re not POSTing your params properly. Phoenix is expecting to receive person[name] for example as form params but you’re passing name.

Probably easier to see formatted as json

{
  "person": { 
    "name": "John"
  }
} 

Is what’s expected.

2 Likes

In Elixir, matching requires three things:

  1. The same name of the function
  2. The same amity of the function
  3. Parameters match the function signature

In your case, you are providing %{“name” => “john”} not something that matches with %{“person” => person_params}. Which is where @John-Goff’s answer kicks in.

2 Likes

ok got it. More of a wrong option chosen in Postman than a Phoenix coding issue.