Connecting Ember.js with Phoenix
Tags: elixir, ember, phoenix
Published: 2021-06-02 13:58:33

## Introduction

The aim of this article is not only to show how to connect a backend application using Phoenix and a frontend application using Ember, but also to know all the different issues that may be encountered when trying to connect any frontend or backend application.

## Versions used in this guide:
```
Elixir: 1.11.3
Erlang/OTP: 21
Phoenix: 1.5.7

Node: 12.13.0
Ember CLI: 3.24.0
```

# Creating Phoenix backend
Let's start by creating our project in Phoenix by running the following command:
```
$ mix phx.new pizza_corner_api
(...)
$ cd pizza_corner_api
$ mix ecto.create
$ iex -S mix phx.server
```
Then, let's create a migration in our application:
```
$ mix phx.gen.schema Api.Pizza pizzas name:string description:text image:string
```

This will generate the following files:
{% gist https://gist.github.com/kexposito/caae0ed3a641e3f5f359a9275fd860b4 %}

{% gist
https://gist.github.com/kexposito/cb2cb141ad93d35b95a5044e9bee1c71
%}

Now, we are going to apply the migration by running:
```
$ mix ecto.migrate
```
Nice! we have the Pizza table created, let's add some seeds:
{% gist https://gist.github.com/kexposito/7efaa81f92d42ed27070437bc1f612da %}
After this, run:
```
$ mix run priv/repo/seeds.exs
```

## Returning JSON API Responses with JaSerializer
Now we are almost ready to create the PizzaController and router, but before doing that, let's think about what we are doing. We are building the frontend in Ember and the backend in Phoenix. These technologies must communicate with each other. To do this we are going to use JSON API. This means that Phoenix and Ember apps must send and receive JSONAPI responses.

To do this in Phoenix we are going to use JaSerializer ([https://github.com/vt-elixir/ja_serializer](https://github.com/vt-elixir/ja_serializer)).

### Installing JaSerializer
```elixir
# mix.exs
defp deps do
	...
	{:ja_serializer, "~> 0.16.0"},
	{:poison, "~> 3.1"}
	...
end
```
```console
$ mix deps.get
```
```elixir
# config.exs
config :phoenix, :format_encoders,
  "json-api": Poison

config :mime, :types, %{
  "application/vnd.api+json" => ["json-api"]
}
```
And then:
```
$ mix deps.clean mime --build
$ mix deps.get
```

```elixir
# router.ex
pipeline :api do
  plug :accepts, ["json-api"]
  plug JaSerializer.ContentTypeNegotiation
  plug JaSerializer.Deserializer
end
```
Finally, we are going to create the Pizza View and Controller:
{% gist https://gist.github.com/kexposito/1a9cc234bde1cf11649b96a13d0f7f48 %}
{% gist https://gist.github.com/kexposito/b0aa56864a7560fee6d8ca654fc4eb6c %}

Don't forget to add the endpoint to the router!
```elixir
# router.ex
scope "/api", PizzaCornerApiWeb do
  pipe_through :api

  get "/pizzas", PizzaController, :index
end
```

# Creating the Ember frontend
```
$ ember new pizza_corner --no-welcome
```
## Adding the Pizza Model
{% gist https://gist.github.com/kexposito/e3246aae86e48d6fff73ef2d78a2ec12 %}
{% gist https://gist.github.com/kexposito/477552945fc0f72a9b6195d0386c7321 %}
{% gist https://gist.github.com/kexposito/d3f14818d1e1222071d7b811d080f4bb %}
{% gist https://gist.github.com/kexposito/d17dddb215f56a63e01a43d480bf2679 %}
Now, lets run the following code to see if everything is working fine:
```
$ ember s
```
and check it http://localhost:4200

# What is happening?
If we open the console, we will see something like this:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/etl11dsomjjh6s6s0dg3.png)
What does this mean? By default Ember will request the same URL it is running on, so we must specify that we want to hit our Phoenix backend. To do this, we have to override the adapter as follows:
{% gist https://gist.github.com/kexposito/0f3eb9118b4f6bdecd451710306e79e7 %}

***host*** is the url that we want to hit, and ***namespace*** is the path after the host. This is because, in the Phoenix router we specified that `pizzas` are inside `api` scope. By doing this and thanks to Ember JSONAPIAdapter, `store.findAll('pizza')` will hit `http://localhost:4000/api/pizzas` . To see more about this you can see: [https://api.emberjs.com/ember-data/release/classes/JSONAPIAdapter](https://api.emberjs.com/ember-data/release/classes/JSONAPIAdapter)

Update your `app/serializers/application.js`:
{% gist 
https://gist.github.com/kexposito/0cbf573daa8509beeb62967a6983c883
%}

Now, let's try again, hopefully we will see some magic.
And no magic happens, So.. lets see the inspector again 😡
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inum8u7rtjqa35gyd8sf.png)

## We are having a CORS issue here. Why does this happens?
This happens because of security. CORS doesn't allow communication between our Ember web app (http://localhost:4200) and our Phoenix api (http://localhost:4000) because they are not from the same path and this is not safe.

## What can we do to fix this?
In order to fix this, we should tell our Phoenix API to accept incoming requests from Ember. To do this, we will use CORS Plug https://github.com/mschae/cors_plug,  this plugin will allow us to configure that.

### Installing CORS Plug
```elixir
# mix.exs
defp deps do
	...
	{:cors_plug, "~> 2.0"},
	...
end
```
And then run:
``` 
$ mix deps.get
```

Also add:
```elixir
#lib/pizza_corner_api_web/endpoint.ex
defmodule PizzaCornerApiWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :pizza_corner_api
	...
	...
	plug CORSPlug, origin: ["http://localhost:4200"] # <- Add this
	plug PizzaCornerApiWeb.Router
end
```
And after this, restart the server:
```
$ mix phx.server
```
Now visit http://localhost:4200 one more time. You will see the pizzas from our backend being rendered! 🎉🎉🍕

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahviklgmarfs71fvwz78.png)
Phoenix Documentation:

- [https://hexdocs.pm/phoenix/up_and_running.html](https://hexdocs.pm/phoenix/up_and_running.html)
- [https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Schema.html](https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Schema.html)
- [https://hexdocs.pm/phoenix/routing.html](https://hexdocs.pm/phoenix/routing.html)

Ember Documentation:

- [https://cli.emberjs.com/release/basic-use/cli-commands/](https://cli.emberjs.com/release/basic-use/cli-commands/)
- [https://guides.emberjs.com/release/getting-started/quick-start/](https://guides.emberjs.com/release/getting-started/quick-start/)
- [https://guides.emberjs.com/release/routing/specifying-a-routes-model/](https://guides.emberjs.com/release/routing/specifying-a-routes-model/)
- [https://guides.emberjs.com/release/models/#toc_models](https://guides.emberjs.com/release/models/#toc_models)
- [https://guides.emberjs.com/release/models/customizing-adapters/](https://guides.emberjs.com/release/models/customizing-adapters/)
- [https://api.emberjs.com/ember-data/release/classes/JSONAPIAdapter](https://api.emberjs.com/ember-data/release/classes/JSONAPIAdapter)

Things we used here:

- JaSerializer ([https://github.com/vt-elixir/ja_serializer](https://github.com/vt-elixir/ja_serializer))
- Poison ([https://github.com/devinus/poison](https://github.com/devinus/poison))
- Cors Plug ([https://github.com/mschae/cors_plug](https://github.com/mschae/cors_plug))