Created
March 21, 2017 10:27
-
-
Save muziyoshiz/47ff80c7b4c96b8e9432e100b00b5c70 to your computer and use it in GitHub Desktop.
Sample of GraphQL Relay server on Phoenix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# mix.exs | |
defmodule PhoenixRelaySample.Mixfile do | |
use Mix.Project | |
def project do | |
[app: :phoenix_relay_sample, | |
version: "0.0.1", | |
elixir: "~> 1.2", | |
elixirc_paths: elixirc_paths(Mix.env), | |
compilers: [:phoenix, :gettext] ++ Mix.compilers, | |
build_embedded: Mix.env == :prod, | |
start_permanent: Mix.env == :prod, | |
aliases: aliases(), | |
deps: deps()] | |
end | |
# Configuration for the OTP application. | |
# | |
# Type `mix help compile.app` for more information. | |
def application do | |
[mod: {PhoenixRelaySample, []}, | |
applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, | |
:phoenix_ecto, :postgrex, :absinthe_plug, :absinthe_relay]] | |
end | |
# Specifies which paths to compile per environment. | |
defp elixirc_paths(:test), do: ["lib", "web", "test/support"] | |
defp elixirc_paths(_), do: ["lib", "web"] | |
# Specifies your project dependencies. | |
# | |
# Type `mix help deps` for examples and options. | |
defp deps do | |
[ | |
{:phoenix, "~> 1.2.1"}, | |
{:phoenix_pubsub, "~> 1.0"}, | |
{:phoenix_ecto, "~> 3.0"}, | |
{:postgrex, ">= 0.0.0"}, | |
{:phoenix_html, "~> 2.6"}, | |
{:phoenix_live_reload, "~> 1.0", only: :dev}, | |
{:gettext, "~> 0.11"}, | |
{:cowboy, "~> 1.0"}, | |
{:absinthe_plug, "~> 1.2.0"}, | |
{:absinthe_relay, "~> 1.2.0"}, | |
] | |
end | |
# Aliases are shortcuts or tasks specific to the current project. | |
# For example, to create, migrate and run the seeds file at once: | |
# | |
# $ mix ecto.setup | |
# | |
# See the documentation for `Mix` for more info on aliases. | |
defp aliases do | |
["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], | |
"ecto.reset": ["ecto.drop", "ecto.setup"], | |
"test": ["ecto.create --quiet", "ecto.migrate", "test"]] | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# web/router.ex | |
defmodule PhoenixRelaySample.Router do | |
use PhoenixRelaySample.Web, :router | |
# pipeline :browser do | |
# plug :accepts, ["html"] | |
# plug :fetch_session | |
# plug :fetch_flash | |
# plug :protect_from_forgery | |
# plug :put_secure_browser_headers | |
# end | |
# | |
# pipeline :api do | |
# plug :accepts, ["json"] | |
# end | |
# | |
# scope "/", PhoenixRelaySample do | |
# pipe_through :browser # Use the default browser stack | |
# | |
# get "/", PageController, :index | |
# end | |
# Other scopes may use custom stacks. | |
# scope "/api", PhoenixRelaySample do | |
# pipe_through :api | |
# end | |
scope "/" do | |
if Mix.env == :dev do | |
forward "/graphql", Absinthe.Plug.GraphiQL, schema: PhoenixRelaySample.Schema | |
else | |
forward "/graphql", Absinthe.Plug, schema: PhoenixRelaySample.Schema | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# web/schema.ex | |
defmodule PhoenixRelaySample.Schema do | |
use Absinthe.Schema | |
use Absinthe.Relay.Schema | |
alias PhoenixRelaySample.UserResolver | |
alias PhoenixRelaySample.User | |
import_types PhoenixRelaySample.Schema.Types | |
query do | |
@desc """ | |
A list of users. | |
""" | |
connection field :users, node_type: :user do | |
# Argument `first`, `last`, `before` and `after` are automatically added | |
resolve &UserResolver.list/3 | |
end | |
@desc """ | |
Fetches an object given its ID. | |
""" | |
node field do | |
resolve fn | |
parent, %{type: :user, id: id}, _ -> | |
UserResolver.find(parent, %{id: id}, %{}) | |
end | |
end | |
@desc """ | |
Hack to workaround https://github.com/facebook/relay/issues/112 re-exposing the root query object | |
""" | |
field :relay, :query do | |
resolve (fn _parent, _arg, _info -> {:ok, %{}} end) | |
end | |
end | |
@desc """ | |
An object with an ID. | |
""" | |
node interface do | |
resolve_type fn | |
%User{}, _ -> | |
:user | |
_, _ -> | |
nil | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# web/schema/type.ex | |
defmodule PhoenixRelaySample.Schema.Types do | |
use Absinthe.Schema.Notation | |
use Absinthe.Relay.Schema.Notation | |
@desc "A user." | |
node object :user do | |
@desc "The user's name." | |
field :name, non_null(:string) | |
@desc "The user's gender." | |
field :gender, :string | |
@desc "The user's age." | |
field :age, :integer | |
@desc "The company the user works at." | |
field :company, :company do | |
resolve &resolve_company/3 | |
end | |
end | |
@desc "A company." | |
object :company do | |
@desc "The company's name." | |
field :name, :string | |
end | |
connection node_type: :user | |
defp resolve_company(parent, _args, _info) do | |
case parent do | |
%{company_name: company_name} -> | |
{:ok, %{name: company_name}} | |
_ -> | |
{:ok, nil} | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# web/models/user.ex | |
defmodule PhoenixRelaySample.User do | |
use PhoenixRelaySample.Web, :model | |
schema "users" do | |
field :name, :string | |
field :gender, :string | |
field :age, :integer | |
field :company_name, :string | |
timestamps() | |
end | |
@doc """ | |
Builds a changeset based on the `struct` and `params`. | |
""" | |
def changeset(struct, params \\ %{}) do | |
struct | |
|> cast(params, [:name, :gender, :age, :company_name]) | |
|> validate_required([:name]) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# web/resolvers/user_resolver.ex | |
defmodule PhoenixRelaySample.UserResolver do | |
alias PhoenixRelaySample.User | |
alias PhoenixRelaySample.Repo | |
import Ecto.Query | |
def find(_parent, %{id: id}, _info) do | |
case Repo.get(User, id) do | |
nil -> {:error, "Not found"} | |
user -> {:ok, user} | |
end | |
end | |
def list(_parent, args, _info) do | |
# Error messages same as that of GitHub GraphQL API | |
case args do | |
%{first: _first, last: _last} -> | |
{:error, "Passing both `first` and `last` values to paginate the `users` connection is not supported."} | |
%{first: _first, before: _before} -> | |
{:error, "Passing `first` with `before` is not supported."} | |
%{first: _first} -> | |
do_list(args) | |
%{last: _last, after: _after} -> | |
{:error, "Passing `last` with `after` is not supported."} | |
%{last: _last} -> | |
do_list(args) | |
_ -> | |
{:error, "You must provide a `first` or `last` value to properly paginate the `users` connection."} | |
end | |
end | |
defp do_list(args) do | |
# You must supply a count if using `last` without `before` | |
count = Repo.aggregate(User, :count, :id) | |
# args in from_query(query, repo_fun, args, opts \\ []) must have args[:first] or args[:last]. | |
# The sorting is required before from_query. | |
conn = | |
User | |
|> order_by(asc: :id) | |
|> Absinthe.Relay.Connection.from_query(&Repo.all/1, args, [count: count]) | |
{:ok, conn} | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment