Skip to content

Commit

Permalink
AshGraphql Example
Browse files Browse the repository at this point in the history
  • Loading branch information
tyalt1 committed Jun 5, 2024
1 parent 9884701 commit 5ca4a08
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 8 deletions.
3 changes: 2 additions & 1 deletion .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
:ash_postgres,
:ash_authentication,
:ash_authentication_phoenix,
:ash_json_api
:ash_json_api,
:ash_graphql
],
subdirectories: ["priv/*/migrations"],
plugins: [Phoenix.LiveView.HTMLFormatter],
Expand Down
122 changes: 122 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ Elixir runs on the Erlang VM which has many examples of scalability
- With LiveView developers can write reactive single-page frontends in HTML and Elixir. (No Javascript)
- Ash lets you define a model and then with minimal code derive database persistance, code wrappers, REST API, GraphQL API, and more.

## PETAL Stack Q/A

### Q: Do I have to use Ash?

No. Ash is not needed. But I recommend Ash because it puts a lot of complexity up front and pays dividends later. With Ecto, the default database library for Phoenix, you declare the schema and only derive Database persistance and some validation. With Ash you declare the schema and get the following:

- Resource and Domain level functions
- Database persistance (Postgres, SQLite3)
- alternative persistance (in memory, ETS table, Mnesia table, CSV file)
- Form validation
- JSON API mapping
- GraphQL API mapping
- and more with a robust extension system ...

## Commit References

This is a summary of notable commits, and sections that go into that code in greater detail.
Expand Down Expand Up @@ -294,6 +308,8 @@ defp deps do
end
```

Note: `AshPostgres` is built on top of `Ecto`, a database library for Elixir. `Ecto` is what Phoenix uses by default for communicating with the database.

Optionally, make the changes to `.formatter.exs`
```elixir
[
Expand Down Expand Up @@ -678,3 +694,109 @@ For the sake of completeness, next I want to show how to add a GraphQL API.
We can add a GraphQL API for the Blog domain.

I followed [this getting started guide](https://ash-hq.org/docs/guides/ash_graphql/latest/tutorials/getting-started-with-graphql) for adding `AshGraphql`.

Add the `ash_json_api` dependency:
```elixir
defp deps do
[
# ...
{:ash_graphql, "~> 1.1.1"},
# ...
]
end
```

Note: `AshGraphql` is built on top of `Absinthe`, a GraphQL library for Elixir.

Add to `.formatter.exs`:
```elixir
[
import_deps: [
# ...
:ash_graphql
],
#...
]
```

Add the `AshGraphql.Domain` extension to the domain. Also, disable authorization for easy prototyping.
```elixir
defmodule PetalStackTutorial.Blog do
use Ash.Domain,
extensions: [
# ...
AshGraphql.Domain # <-- add this
]

# ...
graphql do
authorize? false
end
end
```

Add the `AshGraphql.Domain` extension to the resource. Then specify
```elixir
defmodule PetalStackTutorial.Blog.Post do
use Ash.Resource,
domain: PetalStackTutorial.Blog,
data_layer: AshPostgres.DataLayer,
extensions: [
AshJsonApi.Resource,
AshGraphql.Resource
]

graphql do
end
end
```

Create a schema.
```elixir
defmodule PetalStackTutorialWeb.GraphqlSchema do
use Absinthe.Schema
use AshGraphql, domains: [PetalStackTutorial.Blog]

query do
end

mutation do
end
end
```

Add the schema to `router.ex`. Use the `Absinthe.Plug` with your schema to serve the main GraphQL API. Also use the `Absinthe.Plug.GraphiQL` to create an interactive version of graphql
```elixir
defmodule PetalStackTutorialWeb.Router do
use PetalStackTutorialWeb, :router
# ...
pipeline :graphql do
plug AshGraphql.Plug
end
# ...

scope "/gql" do
pipe_through :graphql

forward "/playground",
Absinthe.Plug.GraphiQL,
schema: PetalStackTutorialWeb.GraphqlSchema,
interface: :playground

forward "/", Absinthe.Plug, schema: PetalStackTutorialWeb.GraphqlSchema
end
end
```

Now when I visit http://localhost:4000/api/gql/playground it shows an interactive version of GraphQL. There is a "SCHEMA" tab on the right that contains all queries and mutations.

I can make the following query:
```graphql
query {
getPost(id: "7e9cc199-7c5f-4196-bb7c-d28550d6b7c2") {
id
}
}
```

### Section 7: TBD
12 changes: 9 additions & 3 deletions lib/petal_stack_tutorial/blog/blog.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
defmodule PetalStackTutorial.Blog do
use Ash.Domain, extensions: [
AshJsonApi.Domain
]
use Ash.Domain,
extensions: [
AshJsonApi.Domain,
AshGraphql.Domain
]

resources do
resource PetalStackTutorial.Blog.Post do
Expand All @@ -12,4 +14,8 @@ defmodule PetalStackTutorial.Blog do
define :get_post, action: :get, args: [:id]
end
end

graphql do
authorize? false
end
end
18 changes: 17 additions & 1 deletion lib/petal_stack_tutorial/blog/post.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ defmodule PetalStackTutorial.Blog.Post do
domain: PetalStackTutorial.Blog,
data_layer: AshPostgres.DataLayer,
extensions: [
AshJsonApi.Resource
AshJsonApi.Resource,
AshGraphql.Resource
]

postgres do
Expand Down Expand Up @@ -57,4 +58,19 @@ defmodule PetalStackTutorial.Blog.Post do
delete :destroy
end
end

graphql do
type :post

queries do
get :get_post, :read
list :list_posts, :read
end

mutations do
create :create_post, :create
update :update_post, :update
destroy :destroy_post, :destroy
end
end
end
33 changes: 30 additions & 3 deletions lib/petal_stack_tutorial_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ defmodule PetalStackTutorialWeb.Router do
plug :load_from_bearer
end

pipeline :graphql do
plug AshGraphql.Plug
end

scope "/", PetalStackTutorialWeb do
pipe_through :browser

Expand All @@ -32,10 +36,22 @@ defmodule PetalStackTutorialWeb.Router do
reset_route []
end

scope "/api/json" do
pipe_through :api
scope "/api" do
scope "/json" do
pipe_through :api
forward "/", PetalStackTutorialWeb.JsonApiRouter
end

scope "/gql" do
pipe_through :graphql

forward "/playground",
Absinthe.Plug.GraphiQL,
schema: PetalStackTutorialWeb.GraphqlSchema,
interface: :playground

forward "/", PetalStackTutorialWeb.JsonApiRouter
forward "/", Absinthe.Plug, schema: PetalStackTutorialWeb.GraphqlSchema
end
end

# Enable LiveDashboard and Swoosh mailbox preview in development
Expand Down Expand Up @@ -68,3 +84,14 @@ defmodule PetalStackTutorialWeb.JsonApiRouter do
json_schema: "/json_schema",
open_api: "/open_api"
end

defmodule PetalStackTutorialWeb.GraphqlSchema do
use Absinthe.Schema
use AshGraphql, domains: [PetalStackTutorial.Blog]

query do
end

mutation do
end
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ defmodule PetalStackTutorial.MixProject do
{:ash_authentication_phoenix, "~> 2.0"},
{:ash_json_api, "~> 1.0"},
{:open_api_spex, "~> 3.16"},
{:ash_graphql, "~> 1.1.1"},

# phoenix
{:phoenix, "~> 1.7.12"},
Expand Down
4 changes: 4 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
%{
"absinthe": {:hex, :absinthe, "1.7.6", "0b897365f98d068cfcb4533c0200a8e58825a4aeeae6ec33633ebed6de11773b", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7626951ca5eec627da960615b51009f3a774765406ff02722b1d818f17e5778"},
"absinthe_plug": {:hex, :absinthe_plug, "1.5.8", "38d230641ba9dca8f72f1fed2dfc8abd53b3907d1996363da32434ab6ee5d6ab", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bbb04176647b735828861e7b2705465e53e2cf54ccf5a73ddd1ebd855f996e5a"},
"ash": {:hex, :ash, "3.0.9", "f98266488f9f152130a6015c7158f70ba8262939c4dd0728bb55ab48b6d282eb", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, ">= 0.8.1 and < 1.0.0-0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.1.18 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "adb0853558c13cd77a4489e755df149e0e43bde9c069811ec244dd5dcbe62cd0"},
"ash_authentication": {:hex, :ash_authentication, "4.0.0", "d723633301512ef8bb14b84294139a31562b49b7cb568636f914e12465ab27ba", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_postgres, "~> 2.0", [hex: :ash_postgres, repo: "hexpm", optional: true]}, {:assent, ">= 0.2.8 and < 1.0.0-0", [hex: :assent, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:finch, "~> 0.18.0", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.5", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}], "hexpm", "9da852e60c89596e35363f7866f6500bad1639b5e8c626a77ccad20d40db91f5"},
"ash_authentication_phoenix": {:hex, :ash_authentication_phoenix, "2.0.0", "1d2dd0abc9b9e008ea4423e902eb24825dbf4b9d1329bd079d7064ecfc45d319", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_authentication, "~> 4.0", [hex: :ash_authentication, repo: "hexpm", optional: false]}, {:ash_phoenix, "~> 2.0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_html_helpers, "~> 1.0", [hex: :phoenix_html_helpers, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:slugify, "~> 1.3", [hex: :slugify, repo: "hexpm", optional: false]}], "hexpm", "6a7c24d57ef6f7a4456d5ba139c8221df6a7ed81f15707a23fc33ad369e43a36"},
"ash_graphql": {:hex, :ash_graphql, "1.1.1", "91f15ea0da300f496f131fb36b14e1b734e465f66c20d2ba85886735b70bab9d", [:mix], [{:absinthe, "~> 1.7", [hex: :absinthe, repo: "hexpm", optional: false]}, {:absinthe_plug, "~> 1.4", [hex: :absinthe_plug, repo: "hexpm", optional: false]}, {:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "16d6fdfe911087f22968257f15e312fc597c0cc1778d56bc5a6699b55ff987f0"},
"ash_json_api": {:hex, :ash_json_api, "1.1.0", "86d38bb810ae06170bea2ead3dd558cacb5c760f7db62356d9280dc996093814", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:json_xema, "~> 0.4", [hex: :json_xema, repo: "hexpm", optional: false]}, {:open_api_spex, "~> 3.16", [hex: :open_api_spex, repo: "hexpm", optional: true]}, {:plug, "~> 1.11", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "0db08bc19ac52d3adb31df5a2090cfa0da2937b413b9f4cd7738b0a876200454"},
"ash_phoenix": {:hex, :ash_phoenix, "2.0.2", "38f62e3cfbaf027467662f32b5597627d04c39a1a5e1c59399f7712209718d1b", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "fc7ee7bf3bbc8abe36e4e940d754bd326aa360092d01f64095fbbb31fe3fe606"},
"ash_postgres": {:hex, :ash_postgres, "2.0.6", "9c504453e2327db8a27c8333b0a62c2e4940b4f769c7f5b4fa14b9cfb5debf15", [:mix], [{:ash, ">= 3.0.7 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_sql, "~> 0.2", [hex: :ash_sql, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "9b096d4ba605bcf6909fa04f4dee3f982141b89822bb1a2e52a58065615ddf88"},
Expand Down Expand Up @@ -36,6 +39,7 @@
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"mint": {:hex, :mint, "1.6.0", "88a4f91cd690508a04ff1c3e28952f322528934be541844d54e0ceb765f01d5e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "3c5ae85d90a5aca0a49c0d8b67360bbe407f3b54f1030a111047ff988e8fefaa"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"open_api_spex": {:hex, :open_api_spex, "3.19.1", "65ccb5d06e3d664d1eec7c5ea2af2289bd2f37897094a74d7219fb03fc2b5994", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "392895827ce2984a3459c91a484e70708132d8c2c6c5363972b4b91d6bbac3dd"},
"phoenix": {:hex, :phoenix, "1.7.12", "1cc589e0eab99f593a8aa38ec45f15d25297dd6187ee801c8de8947090b5a9d3", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "d646192fbade9f485b01bc9920c139bfdd19d0f8df3d73fd8eaf2dfbe0d2837c"},
Expand Down

0 comments on commit 5ca4a08

Please sign in to comment.