# Fetching data from external Graphql API service in Phoenix LiveView

Recently while creating my personal portfolio website [abulasar.dev](http://abulasar.dev/), I added a `blog` page. For that, I had fetched [hashnode](https://hashnode.com/) blogs on the page. [Hashnode](https://hashnode.com/) has exposed its blog through [API](https://api.hashnode.com/). You can visit `api.hashnode.com` and this API is in Graphql format. Previously, I had fetched these blogs in an Ember.js app and wrote a [blog](https://abulasar.com/fetching-hashnode-blogs-in-ember-app) on this topic. I had no previous experience using graphql client in the elixir projects. I implemented it in [this](http://abulasar.dev/) project and this blog will be about using graphql query in the Phoenix LiveView project. Let's build one!!

### Action Plan
- Creating LiveView project
- Installing and configuring graphql client.
- Adding API layer to fetch data.
- Integrating API layer with UI

### Let's start building 👷
- First of all, as discussed will create a LiveView project. 
- We will confirm if Elixir is installed by running `elixir -v`.
- Once confirmed we need to create/generate the Phoenix LiveView application.
- This can be done by running `mix phx.new fetch_hashnode --no-ecto --live` and this will generate one.
- We will then navigate to the project directory by running `cd fetch_hashnode` and run `mix phx.server`. Once, the project starts running navigate to `localhost:4000` to see the `Welcome` phoenix screen.

### Installing Graphql client 
- I searched for Graphql client in Elixir and came across a few but the one that caught my eye is [Neuron](https://github.com/uesteibar/neuron). It is very easy to configure and use so I decided to go with `Neuron`.
- We will configure `Neuron` by first adding it to the list of dependencies.
- Open the `mix.exs` file and add Neuron to the list of dependencies. 
```
def deps do
  [
   ........,
    {:neuron, "~> 5.0.0"}
  ]
end
```
- Now run `mix deps.get` to install the dependency.
- Next, we are going to configure `Neuron`.

### Configure Neuron
- To query Hashnode API, we have to add a `url` in the Neuron configuration.
- As per documentation we can test Neuron in `iex shell` by running the following query.

```
iex> Neuron.Config.set(url: "https://api.hashnode.com/")
```
- After adding this you can query Hasnode API as follows

```
Neuron.query("""
{
  user(username: "your_username") {
      publication {
          posts(page: 0) {
              title
              brief
              slug
              cuid
              coverImage
          }
      }
  }
}
""")
```

- This query will return a `Neuron` response with a list of blogs on page number 0, along with the nitty-gritty details of `header` responses, something like below.

```
{:ok,
 %Neuron.Response{
   body: %{
     "data" => %{
       "user" => %{
         "publication" => %{
           "posts" => [
             %{ } // blog 1,
             %{ } // blog 2
           ]
    }
           ]
         }
       }
     }
   },
   headers: [
     {"Connection", "keep-alive"},
     {"Content-Length", "3186"},
     ......
   ],
   status_code: 200
 }
}
```
- So far so good, everything is looking fine. In our next step, we'll add an API layer where we'll place the above query to fetch the data.

### Adding API layer to fetch data
- We will add a module that will have all two functions one to fetch all blogs and one to fetch specific blogs.
- Create a file a create a folder `blog_posts` under `lib/fetch_hashnode`. In that, we'll add the `blog.ex` file.
- Add the following code to it.
```
defmodule  FetchHashnode.Blog do
    @username "your_hashnode_username"

    def get_blogs(page \\0) do
      Neuron.query("""
        {
          user(username: "#{@username}") {
            publication {
                posts(page: #{page}) {
                    title
                    brief
                    slug
                    cuid
                    coverImage
                }
            }
          }
        }
      """)
    end

    def get_detail_blog(slug) do
      Neuron.query("""
        {
            post(slug: "#{slug}", hostname: "#{@username}") {
            title,
            slug,
            cuid,
            coverImage,
            content,
            contentMarkdown,
            tags {
            name
            }
         }
       }
      """)
   end
end
```
- The above two functions are self-explanatory. It is simple and very similar to what we have tried earlier in the console.
- Let's fire up the `iex` shell by running `iex -S mix phx.server` and do some testing of these functions.
- We'll first test the `get_blogs` function which expects to return a list of all blogs on page 0 (because the default is 0).
- Run the following query
```
iex>  FetchHashnode.Blog.get_blogs
```
- We will get the error `" you need to supply an url"`. So, what went wrong? 🤔
- If you'll remember we registered the `url` with the `Neuron` in the shell earlier by doing something like this.
```
Neuron.Config.set(url: "https://api.hashnode.com/")
```
- We want `Neuron` should be aware of this `url` as soon as the server starts. So, we will add the above snippet in `application.ex` in the `start` function.

```
def start(_type, _args) do
    children = [
      FetchHashnode.Repo,
      ....
   ]
   opts = [strategy: :one_for_one, name: FetchHashnode.Supervisor]

   Neuron.Config.set(url: "https://api.hashnode.com/")  # This is the line
   ...
end
```
- Now, again restart the server `iex -S mix phx.server` and again query the function `FetchHashnode.Blog.get_blogs` and voila!! it's working now.
- Similarly for the second function i.e `get_detail_blog` we need to pass the `slug` of the blog. So, we will test the function with some `slug` like this 

```
FetchHashnode.Blog.get_detail_blog('writing-mix-task-in-elixir-phoenix')
```

- This will return a detailed version of the blog.

### Integrating API layer with UI
- Now, that we have a working API layer. We will add a `LiveView` page to display these fetched blogs.
- Add `blogs` endpoint in `router.ex` by adding the following code 

```
scope "/", FetchHashnodeWeb do
    pipe_through :browser

    live "/blogs", BlogsLive
end
```
- Now add a file `lib/fetch_hashnode_web/live/blogs_live.ex` and add the following `mount` callback in the code.
```
defmodule FetchHashnodeWeb.BlogsLive do
   use FetchHashnodeWeb, :live_view
   alias FetchHashnode.Blog

   def mount(_params, _session, socket) do
     {:ok, blogs} = Blog.get_blogs()
     blogs = get_in(blogs.body, ["data", "user", "publication", "posts"])
     socket = assign(socket, blogs: blogs )
     {:ok, socket}
   end
end
```
- We have aliased the `Blog` module and queried the `get_blogs` function in the mount function. 
- We have assigned the fetched blogs to the `blogs` variable using pattern matching.
- Later we have extracted all the blogs and assigned them to `socket` this will make it available in the view in the `render` callback.
- We will loop over the `blogs` variable in the view to display something like this.
```   
  def render(assigns) do
     ~L"""
        <%= for blog <- @blogs do %>
           Title: <%= blog["title"] %> 
           <hr>
        <% end %>
     """
  end
```
- It will look something like below
![Screenshot 2022-05-30 at 12.50.58 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1653852063407/DDw-OEZVN.png align="left")
- Similarly, we can fetch a particular blog and display it on a separate `LiveView` page. I'll leave this logic up to you 😊.

I hope you like this blog. Of course, there is some scope of refactoring that I intentionally didn't touch. If you have any questions then please comment below. Thanks for reading 😊.

### References
- [Neuron](https://github.com/uesteibar/neuron)
- [LiveView](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html)



