Expanding Our API Request

We have a good start to our code, but we should really expand on this a little bit more. In order for us to do that, we'll need to start off by modifying our controller. This should actually be very similar to what you've already seen in the past when displaying regular information to normal templates. We'll alias our Votes Context, grab the list of polls from our list_most_recent_polls/0 call, and then send that information on to our views! Let's take a look at the changes to lib/controllers/api/poll_controller.ex:

  alias Vocial.Votes

def index(conn, _params) do
polls = Votes.list_most_recent_polls()
render(conn, "index.json", polls: polls)
end

Next, we'll need to set up the process for our data to go from the database into JSON output! There's a function call here that calls out to a function that we will write later. We'll send the polls variable off to a function that will render each poll that you have. In lib/views/api/poll_view.ex:

def render("index.json", %{polls: polls}) do
%{
polls: render_many(polls)
}
end

Next, we'll need to implement our first render function. This will be the render call that is utilized by the render_many/1 function. All this does is take in a poll struct and convert it into a normal Elixir map. We'll also map through the list of the options available for each poll and grab the title and the vote count from each.

Map.take/2 takes in the map that you want to grab the information out of as the first argument and a list of keys and returns the result out as a map of only the keys you requested!
def render_one(poll) do
%{
id: poll.id,
title: poll.title,
options: Enum.map(poll.options, fn o -> Map.take(o, [:title, :votes]) end),
image: %{
url: poll.image.url,
alt: poll.image.alt,
caption: poll.image.caption
}
}
end

After that, we can start to implement our render_many/1 function that we had originally started implementing. All this function even needs to do is take in the list of polls, which it will then map over and call the render_one function on each poll. The resulting function should look like this:

def render_many(polls) do
Enum.map(polls, &render_one/1)
end

We can then refactor this a little bit since it's a little ugly right now. We'll start off by writing a render_options/1 function, where if you supply a nil value it will just return out an empty array. If there are any options, we'll iterate over them again using the same logic from the render_one function:

  def render_options(nil), do: []
def render_options(options) do
options
|> Enum.map(fn o -> Map.take(o, [:title, :votes]) end)
end

We'll also want to separate out the logic for rendering our image data for each poll to keep our initial render call nice and lean. Our implementation of the render_image/1 function is shown as follows, with the same nil-handling logic as in the render_options function:

  def render_image(nil), do: nil
def render_image(image) do
%{
url: image.url,
alt: image.alt,
caption: image.caption
}
end

That should leave us with refactoring our original render_one function to be much, much simpler and cleaner:

  def render_one(poll) do
%{
id: poll.id,
title: poll.title,
options: render_options(poll.options),
image: render_image(poll.image)
}
end

That's it! So with our views all nice and refactored, when we make our API request we should see:

Now that we have our API responding back the way we expect it to for the data that we're sending it, let's start requiring an API key to work without API! 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset