The purpose of this example is to provide details as to how one would go about using GraphQL with the Phoenix Web Framework. Thus, I have created two major sections which should be self explanatory: Quick Installation and Tutorial Installation.
-
PostgreSQL 13.1 or newer
Note: This tutorial was updated on macOS 11.2.
- If you need help, use Stack Overflow. (Tag 'graphql')
- If you'd like to ask a general question, use Stack Overflow.
- If you found a bug, open an issue.
- If you have a feature request, open an issue.
- If you want to contribute, submit a pull request.
-
clone this repository
git clone git@github.com:conradwt/zero-to-graphql-using-phoenix.git
-
change directory location
cd /path/to/zero-to-graphql-using-phoenix -
install dependencies
mix do deps.get, deps.compile -
create, migrate, and seed the database
mix ecto.create mix ecto.migrate mix ecto.seed
-
start the server
mix phx.server
-
navigate to our application within the browser
open http://localhost:4000/graphiql
-
enter and run GraphQL query
{ person(id: 1) { firstName lastName username email friends { firstName lastName username email } } } -
run the GraphQL query
Control + EnterNote: The GraphQL query is responding with same response but different shape within the GraphiQL browser because Elixir Maps perform no ordering on insertion.
-
create the project
mix phx.new zero-to-graphql-using-phoenix --app zero_phoenix --module ZeroPhoenix --no-webpack
Note: Just answer 'y' to all the prompts that appear.
-
switch to the project directory
cd zero-to-graphql-using-phoenix -
update
usernameandpassworddatabase credentials which appears at the bottom of the following files:config/dev.exs config/test.exs -
create the database
mix ecto.create
-
generate contexts, schemas, and migrations for
Personresourcemix phx.gen.context Account Person people first_name:string last_name:string username:string email:string
-
replace the generated
Personmodel with the following:lib/zero_phoenix/account/person.ex:defmodule ZeroPhoenix.Account.Person do use Ecto.Schema import Ecto.Changeset alias ZeroPhoenix.Account.Person alias ZeroPhoenix.Account.Friendship schema "people" do field(:email, :string) field(:first_name, :string) field(:last_name, :string) field(:username, :string) has_many(:friendships, Friendship) has_many(:friends, through: [:friendships, :friend]) timestamps() end @doc false def changeset(%Person{} = person, attrs) do person |> cast(attrs, [:first_name, :last_name, :username, :email]) |> validate_required([:first_name, :last_name, :username, :email]) end end
-
migrate the database
mix ecto.migrate
-
generate contexts, schemas, and migrations for
Friendshipresourcemix phx.gen.context Account Friendship friendships person_id:references:people friend_id:references:people
-
replace the generated
Friendshipmodel with the following:lib/zero_phoenix/account/friendship.ex:defmodule ZeroPhoenix.Account.Friendship do use Ecto.Schema import Ecto.Changeset alias ZeroPhoenix.Account.Person alias ZeroPhoenix.Account.Friendship @required_fields [:person_id, :friend_id] schema "friendships" do belongs_to(:person, Person) belongs_to(:friend, Person) timestamps() end @doc false def changeset(%Friendship{} = friendship, attrs) do friendship |> cast(attrs, @required_fields) |> validate_required(@required_fields) end end
Note: We want
friend_idto reference thepeopletable because ourfriend_idreally represents aPersonmodel. -
migrate the database
mix ecto.migrate
-
create the seeds file
priv/repo/seeds.exs:alias ZeroPhoenix.Repo alias ZeroPhoenix.Account.Person alias ZeroPhoenix.Account.Friendship # reset the datastore Repo.delete_all(Person) # insert people me = Repo.insert!(%Person{ first_name: "Conrad", last_name: "Taylor", email: "conradwt@gmail.com", username: "conradwt" }) dhh = Repo.insert!(%Person{ first_name: "David", last_name: "Heinemeier Hansson", email: "dhh@37signals.com", username: "dhh" }) ezra = Repo.insert!(%Person{ first_name: "Ezra", last_name: "Zygmuntowicz", email: "ezra@merbivore.com", username: "ezra" }) matz = Repo.insert!(%Person{ first_name: "Yukihiro", last_name: "Matsumoto", email: "matz@heroku.com", username: "matz" }) me |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: me.id, friend_id: matz.id } ) |> Repo.insert dhh |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: dhh.id, friend_id: ezra.id } ) |> Repo.insert dhh |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: dhh.id, friend_id: matz.id } ) |> Repo.insert ezra |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: ezra.id, friend_id: dhh.id } ) |> Repo.insert ezra |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: ezra.id, friend_id: matz.id } ) |> Repo.insert matz |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: matz.id, friend_id: me.id } ) |> Repo.insert matz |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: matz.id, friend_id: ezra.id } ) |> Repo.insert matz |> Ecto.build_assoc(:friendships) |> Friendship.changeset( %{ person_id: matz.id, friend_id: dhh.id } ) |> Repo.insert
-
seed the database
mix run priv/repo/seeds.exs
-
add
absinthe_plugpackage to yourmix.exsdependencies as follows:defp deps do [ {:phoenix, "~> 1.5.7"}, {:phoenix_ecto, "~> 4.2.1"}, {:ecto_sql, "~> 3.5.3"}, {:postgrex, "~> 0.15.7"}, {:phoenix_html, "~> 2.14.2"}, {:phoenix_live_reload, "~> 1.3.0", only: :dev}, {:phoenix_live_dashboard, "~> 0.4.0"}, {:telemetry_metrics, "~> 0.6.0"}, {:telemetry_poller, "~> 0.5.1"}, {:gettext, "~> 0.18.2"}, {:jason, "~> 1.2.2"}, {:plug_cowboy, "~> 2.4.1"}, {:absinthe_plug, "~> 1.5.1"} ] end
-
update our projects dependencies:
mix do deps.get, deps.compile -
add the GraphQL schema which represents our entry point into our GraphQL structure:
lib/zero_phoenix_web/graphql/schema.ex:defmodule ZeroPhoenixWeb.Graphql.Schema do use Absinthe.Schema import_types ZeroPhoenix.Graphql.Types.Person alias ZeroPhoenix.Repo query do field :person, type: :person do arg :id, non_null(:id) resolve fn %{id: id}, _info -> case ZeroPhoenix.Person|> Repo.get(id) do nil -> {:error, "Person id #{id} not found"} person -> {:ok, person} end end end end end
-
add our Person type which will be performing queries against:
lib/zero_phoenix_web/graphql/types/person.ex:defmodule ZeroPhoenixWeb.Graphql.Types.Person do use Absinthe.Schema.Notation import Ecto alias ZeroPhoenix.Repo @desc "a person" object :person do @desc "unique identifier for the person" field :id, non_null(:string) @desc "first name of a person" field :first_name, non_null(:string) @desc "last name of a person" field :last_name, non_null(:string) @desc "username of a person" field :username, non_null(:string) @desc "email of a person" field :email, non_null(:string) @desc "a list of friends for our person" field :friends, list_of(:person) do resolve fn _, %{source: person} -> {:ok, Repo.all(assoc(person, :friends))} end end end end
-
add route for mounting the GraphiQL browser endpoint:
lib/zero_phoenix_web/router.ex:scope "/graphiql" do pipe_through :api forward "/", Absinthe.Plug.GraphiQL, schema: ZeroPhoenixWeb.Graphql.Schema, json_codec: Jason, interface: :playground end
-
start the server
mix phx.server
-
navigate to our application within the browser
open http://localhost:4000/graphiql
-
enter the GraphQL query on the left side of the browser
{ person(id: 1) { firstName lastName username email friends { firstName lastName username email } } } -
run the GraphQL query
Control + EnterNote: The GraphQL query is responding with same response but different shape within the GraphiQL browser because Elixir Maps perform no ordering on insertion.
Ready to run in production? Please check our deployment guides.
- Official website: http://www.phoenixframework.org/
- Guides: http://phoenixframework.org/docs/overview
- Docs: https://hexdocs.pm/phoenix
- Mailing list: http://groups.google.com/group/phoenix-talk
- Source: https://github.com/phoenixframework/phoenix
- Official Website: http://graphql.org
- Absinthe GraphQL Elixir: http://absinthe-graphql.org
Bug reports and feature requests can be filed with the rest for the Phoenix project here:
Zero to GraphQL Using Phoenix is released under the MIT license.
copyright:: (c) Copyright 2018 - 2021 Conrad Taylor. All Rights Reserved.