How to do basic POST requests?

Hi there!
First of all: I’m fairly new to Elixir, Phoenix and functional programming in general. But I’m here to learn! I purchased a course on udemy for elixir and phoenix, but sadly it’s for Phoenix v1.2 and I want to learn 1.7.
Anyway. I decided to stick with the course and try to adapt deprecated parts to the new version of phoenix.
Right now I’m stuck while trying to make a simple post-request.
This is my new.html.heex file:

  <.form :let={f} for={@changeset} action={~p"/topics"}>
    <.input field={f[:title]} type="text" name="topic"/>
    <button class="btn btn-primary" type="submit">Submit</button>
  </.form>

And the request reaches the server, the routes should be okay so far.

Here is my topic.ex :

defmodule Discuss.Topic do
  use Ecto.Schema
  import Ecto.Changeset

  schema "topics" do
    field :title, :string

    timestamps(type: :utc_datetime)
  end

  @doc false
  def changeset(topic, attrs) do
    topic
    |> cast(attrs, [:title])
    |> validate_required([:title])
  end
end

And last but not least my TopicController:

defmodule DiscussWeb.TopicController do
  use DiscussWeb, :controller
  alias Discuss.Topic
  alias Discuss.Repo

(...)

  def create(conn, %{"topic" => topic}) do
    changeset = Topic.changeset(%Topic{}, topic)
    case Repo.insert(changeset) do
      {:ok, post} -> IO.inspect(post)
      {:error, changeset} -> IO.inspect(changeset)
    end
  end
end

My Problem is, when I hit that submit button, phoenix is throwing an error:
Ecto.CastError at POST: expected params to be a :map, got: "A unique topic". And I have no clue what is going on. If I understand it correctly, Ecto expects a data-type of map, not plain string (which it received?).
The error is also occuring, if i delete everything inside the def create except changeset = Topic.changeset(%Topic{}, topic). So my guess is, that the core of that error is happening in my def changeset() - but where and why?

And: do you have helpful resources for beginner with phoenix, like tutorials etc? Udemy’s videos are pretty much outdated and I’m missing stuff like handling post-requests with db-inserts in the phoenix docs. Thanks so much and have a nice day! :slight_smile:

The naming here is off, I think you meant for the field to be named name="topic[title]" so that the map %{"title" => "A unique topic"} will be the value of topic in def create(conn, %{"topic" => topic}) do

Because your field name is topic, the params passed to your create function now only contain a string which you then pass on to the changeset.

Next time you can use dbg/2 as the fist line in your function to see what every variable contains.

I’d suggest you take a look at this Programming Phoenix LiveView: Interactive Elixir Web Programming Without Writing Any JavaScript by Bruce A. Tate and Sophie DeBenedetto

might work.

Looking at your error, it seems like you should change this line:

changeset = Topic.changeset(%Topic{}, topic)

To this:

changeset = Topic.changeset(%Topic{}, %{title: topic})

You are passing the string topic directly into changeset, while changeset is expecting a map.

1 Like