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:
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:
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).
Installing JaSerializer
# mix.exs
defp deps do
...
{:ja_serializer, "~> 0.16.0"},
{:poison, "~> 3.1"}
...
end
$ mix deps.get
# 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
# 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:
Don't forget to add the endpoint to the router!
# 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
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:
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:
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
Update your app/serializers/application.js
:
Now, let's try again, hopefully we will see some magic.
And no magic happens, So.. lets see the inspector again 😡
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
# mix.exs
defp deps do
...
{:cors_plug, "~> 2.0"},
...
end
And then run:
$ mix deps.get
Also add:
#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! 🎉🎉🍕
- https://hexdocs.pm/phoenix/up_and_running.html
- https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Schema.html
- https://hexdocs.pm/phoenix/routing.html
Ember Documentation:
- https://cli.emberjs.com/release/basic-use/cli-commands/
- https://guides.emberjs.com/release/getting-started/quick-start/
- https://guides.emberjs.com/release/routing/specifying-a-routes-model/
- https://guides.emberjs.com/release/models/#toc_models
- https://guides.emberjs.com/release/models/customizing-adapters/
- https://api.emberjs.com/ember-data/release/classes/JSONAPIAdapter
Things we used here:
- JaSerializer (https://github.com/vt-elixir/ja_serializer)
- Poison (https://github.com/devinus/poison)
- Cors Plug (https://github.com/mschae/cors_plug)
Top comments (3)
TIL
--no-welcome
is a thing, thank you sirPhoenix!? Time to search it up 😄😄
very helpful!