diff --git a/README.md b/README.md index ef1fa70..fc8e47a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,30 @@ -# AshGraphQL +![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) +![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only) -![Ash CI](https://github.com/ash-project/ash_graphql/workflows/Ash%20CI/badge.svg) +![Elixir CI](https://github.com/ash-project/ash_graphql/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_graphql.svg)](https://hex.pm/packages/ash_graphql) +[![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](https://hexdocs.pm/ash_graphql) -AshGraphQL is a [GraphQL](https://graphql.org/) extension for the [Ash framework](https://ash-hq.org/). It gives you an efficient way to expose your application resources over a GraphQL API. You configure what you want to expose and AshGraphQL will generate an [Absinthe](https://github.com/absinthe-graphql/absinthe) schema which will resolve data straight from your Ash resources. +# AshGraphql -See the [getting started guide](https://ash-hq.org/docs/guides/ash_graphql/latest/tutorials/getting-started-with-graphql.md) for information on setting it up, and see the `AshGraphql.Resource` documentation for docs on its DSL. See the [hexdocs](https://ash-hq.org/docs/dsl/ash_graphql/latest/ashgraphql-resource) for more information. +Welcome! This is the extension for building GraphQL APIs with Ash. The generated GraphQL APIs are powered by [Absinthe](http://hexdocs.pm/absinthe). Generate a powerful Graphql API in minutes! + +## Tutorials + +- [Getting Started with GraphQL](documentation/tutorials/getting-started-with-graphql.md) + +## Topics + +- [Authorize with GraphQL](documentation/topics/authorize-with-graphql.md) +- [Handle Errors](documentation/topics/handle-errors.md) +- [Monitoring](documentation/topics/monitoring.md) +- [Upgrade](documentation/topics/upgrade.md) +- [Use JSON with GraphQL](documentation/topics/use-json-with-graphql.md) +- [Use Subscriptions with GraphQL](documentation/topics/use-subscriptions-with-graphql.md) +- [GraphQL Generation](documentation/topics/graphql-generation.md) +- [Modifying the Resolution](documentation/topics/modifying-the-resolution.md) +- [Relay](documentation/topics/relay.md) +- [Use Enums with GraphQL](documentation/topics/use-enums-with-graphql.md) +- [Use Maps with GraphQL](documentation/topics/use-maps-with-graphql.md) +- [Use Unions with GraphQL](documentation/topics/use-unions-with-graphql.md) diff --git a/documentation/how_to/authorize-with-graphql.md b/documentation/topics/authorize-with-graphql.md similarity index 83% rename from documentation/how_to/authorize-with-graphql.md rename to documentation/topics/authorize-with-graphql.md index 12dad5c..7c40896 100644 --- a/documentation/how_to/authorize-with-graphql.md +++ b/documentation/topics/authorize-with-graphql.md @@ -2,9 +2,9 @@ AshGraphql uses three special keys in the `absinthe` context: -* `:actor` - the current actor, to be used for authorization/preparations/changes -* `:tenant` - a tenant when using [multitenancy](https://ash-hq.org/docs/guides/ash/latest/topics/multitenancy.md). -* `:ash_context` - a map of arbitrary context to be passed into the changeset/query. Accessible via `changeset.context` and `query.context` +- `:actor` - the current actor, to be used for authorization/preparations/changes +- `:tenant` - a tenant when using [multitenancy](https://hexdocs.pm/ash/multitenancy.html). +- `:ash_context` - a map of arbitrary context to be passed into the changeset/query. Accessible via `changeset.context` and `query.context` By default, `authorize?` in the domain is set to true. To disable authorization for a given domain in graphql, use: @@ -17,9 +17,7 @@ end If you are doing authorization, you'll need to provide an `actor`. To set the `actor` for authorization, you'll need to add an `actor` key to the -absinthe context. Typically, you would have a plug that fetches the current user -and uses `Ash.PlugHelpers.set_actor/2` to set the actor in the `conn` (likewise -with `Ash.PlugHelpers.set_tenant/2`). +absinthe context. Typically, you would have a plug that fetches the current user and uses `Ash.PlugHelpers.set_actor/2` to set the actor in the `conn` (likewise with `Ash.PlugHelpers.set_tenant/2`). Just add `AshGraphql.Plug` somewhere _after_ that in the pipeline and the your GraphQL APIs will have the correct authorization. @@ -77,9 +75,7 @@ config :ash_graphql, :policies, show_policy_breakdowns?: true } ], "message": "MyApp.Authentication.User.read\n\n\n\n\nPolicy Breakdown\n Policy | ⛔:\n forbid unless: actor is active | ✓ | ⬇ \n authorize if: actor is Executive | ✘ | ⬇", - "path": [ - "attendanceRecords" - ], + "path": ["attendanceRecords"], "short_message": "forbidden", "vars": {} } diff --git a/documentation/topics/graphql-generation.md b/documentation/topics/graphql-generation.md index 2d20de2..6aceec5 100644 --- a/documentation/topics/graphql-generation.md +++ b/documentation/topics/graphql-generation.md @@ -1,9 +1,8 @@ # GraphQL Query Generation -## Fetch Data by ID +Following where we left off from [Getting Started with GraphQL](/documentation/tutorials/getting-started-with-graphql.md), this guide explores what the GraphQL requests and responses look like for different queries defined with the AshGraphql DSL. -Following where we left off from [Getting Started with GraphQL](/documentation/tutorials/getting-started-with-graphql.md), we'll explore what the GraphQL -requests and responses look like for different queries defined with the AshGraphql DSL. +## Fetch Data by ID ```elixir defmodule Helpdesk.Support.Ticket do @@ -31,7 +30,7 @@ defmodule Helpdesk.Support.Ticket do queries do # create a field called `get_ticket` that uses the `read` read action to fetch a single ticket - get :get_ticket, :read + get :get_ticket, :read end end end @@ -40,7 +39,7 @@ end For the `get_ticket` query defined above, the corresponding GraphQL would look like this: ```graphql -query($id: ID!) { +query ($id: ID!) { getTicket(id: $id) { id subject @@ -69,10 +68,10 @@ Let's look at an example of querying a list of things. queries do # create a field called `get_ticket` that uses the `read` read action to fetch a single ticket - get :get_ticket, :read + get :get_ticket, :read # create a field called `list_tickets` that uses the `read` read action to fetch a list of tickets - list :list_tickets, :read + list :list_tickets, :read end end ``` @@ -120,7 +119,7 @@ Consider `list :list_tickets, :read` and the `actions` section: queries do # create a field called `list_tickets` that uses the `read` read action to fetch a list of tickets - list :list_tickets, :read + list :list_tickets, :read end end ``` @@ -134,7 +133,7 @@ We'll call this action `:query_tickets`: ```elixir actions do defaults [:read, :update, :destroy] - + read :query_tickets do argument :representative_id, :uuid @@ -161,7 +160,7 @@ In the `graphql` section, the `list/2` call has been changed, replacing the `:re The GraphQL request would look something like this: ```graphql -query($representative_id: ID) { +query ($representative_id: ID) { list_tickets(representative_id: $representative_id) { id representative_id @@ -199,9 +198,9 @@ defmodule Helpdesk.Support.Ticket do type :ticket queries do - get :get_ticket, :read + get :get_ticket, :read end - + mutations do create :create_ticket, :create end @@ -223,7 +222,7 @@ See [Use Enums with GraphQL](/documentation/guides/use-enums-with-graphql.md) fo We can now create a ticket with the `createTicket` mutation: ```graphql -mutation($input: CreateTicketInput!) { +mutation ($input: CreateTicketInput!) { createTicket(input: $input) { result { id @@ -279,20 +278,20 @@ Notice that the `status` attribute is set to `"OPEN"` and not `"open"`. It is im This is required by GraphQL enums. AshGraphql will automatically convert the value to the correct case. The response will look something like this: - - ```json - { - "data": { - "createTicket": { - "result": { - "id": "b771e433-0979-4d07-a280-4d12373849aa", - "subject": "My Ticket", - "status": "OPEN" - } + +```json +{ + "data": { + "createTicket": { + "result": { + "id": "b771e433-0979-4d07-a280-4d12373849aa", + "subject": "My Ticket", + "status": "OPEN" } } } - ``` +} +``` Again, AshGraphql will automatically convert the `status` value from `:open` to `"OPEN"`. diff --git a/documentation/how_to/handle-errors.md b/documentation/topics/handle-errors.md similarity index 100% rename from documentation/how_to/handle-errors.md rename to documentation/topics/handle-errors.md diff --git a/documentation/topics/modifying-the-resolution.md b/documentation/topics/modifying-the-resolution.md index db63a39..2d0e6ce 100644 --- a/documentation/topics/modifying-the-resolution.md +++ b/documentation/topics/modifying-the-resolution.md @@ -1,10 +1,11 @@ # Modifying the Resolution -Using the `modify_resolution` option, you can alter the Absinthe resolution. +Using the `modify_resolution` option, you can alter the `Absinthe.Resolution`. -`modify_resoltion` is an MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. Must return a new absinthe resolution. +`modify_resolution` is an MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. Must return a new `Absinthe.Resolution`. -This can be used to implement things like setting cookies based on resource actions. A method of using resolution context -for that is documented here: https://hexdocs.pm/absinthe_plug/Absinthe.Plug.html#module-before-send +This can be used to implement things like setting cookies based on resource actions. A method of using resolution context for that is documented [in Absinthe.Plug](https://hexdocs.pm/absinthe_plug/Absinthe.Plug.html#module-before-send) -*Important* if you are modifying the context in a query, then you should also set `as_mutation?` to true and represent this in your graphql as a mutation. See `as_mutation?` for more. +> ### as_mutation? {: .warning} +> +> If you are modifying the context in a query, then you should also set `as_mutation?` to true and represent this in your graphql as a mutation. See `as_mutation?` for more. diff --git a/documentation/topics/relay.md b/documentation/topics/relay.md index b489436..027f303 100644 --- a/documentation/topics/relay.md +++ b/documentation/topics/relay.md @@ -1,18 +1,22 @@ # Relay -Enabling relay for a resource sets it up to follow the [relay specification](https://relay.dev/graphql/connections.htm). +Enabling Relay for a resource sets it up to follow the [Relay specification](https://relay.dev/graphql/connections.htm). The two changes that are made currently are: -* the type for the resource will implement the `Node` interface -* pagination over that resource will behave as a Connection. +- the type for the resource will implement the `Node` interface +- pagination over that resource will behave as a `Connection`. -## Using with Absinthe.Relay +## Using Ash's built-in Relay support -Use the following option when calling `use AshGraphql` +Set `relay? true` on the resource: ```elixir -use AshGraphql, define_relay_types?: false +graphql do + relay? true + + ... +end ``` ## Relay Global IDs @@ -56,3 +60,11 @@ mutations do end end ``` + +## Using with Absinthe.Relay instead of Ash's relay type + +Use the following option when calling `use AshGraphql` + +```elixir +use AshGraphql, define_relay_types?: false +``` diff --git a/documentation/how_to/upgrade.md b/documentation/topics/upgrade.md similarity index 100% rename from documentation/how_to/upgrade.md rename to documentation/topics/upgrade.md diff --git a/documentation/how_to/use-enums-with-graphql.md b/documentation/topics/use-enums-with-graphql.md similarity index 100% rename from documentation/how_to/use-enums-with-graphql.md rename to documentation/topics/use-enums-with-graphql.md diff --git a/documentation/how_to/use-json-with-graphql.md b/documentation/topics/use-json-with-graphql.md similarity index 100% rename from documentation/how_to/use-json-with-graphql.md rename to documentation/topics/use-json-with-graphql.md diff --git a/documentation/how_to/use-maps-with-graphql.md b/documentation/topics/use-maps-with-graphql.md similarity index 100% rename from documentation/how_to/use-maps-with-graphql.md rename to documentation/topics/use-maps-with-graphql.md diff --git a/documentation/how_to/use-subscriptions-with-graphql.md b/documentation/topics/use-subscriptions-with-graphql.md similarity index 100% rename from documentation/how_to/use-subscriptions-with-graphql.md rename to documentation/topics/use-subscriptions-with-graphql.md diff --git a/documentation/how_to/use-unions-with-graphql.md b/documentation/topics/use-unions-with-graphql.md similarity index 100% rename from documentation/how_to/use-unions-with-graphql.md rename to documentation/topics/use-unions-with-graphql.md diff --git a/documentation/tutorials/getting-started-with-graphql.md b/documentation/tutorials/getting-started-with-graphql.md index 0f05aad..bdd7988 100644 --- a/documentation/tutorials/getting-started-with-graphql.md +++ b/documentation/tutorials/getting-started-with-graphql.md @@ -123,7 +123,7 @@ end > ### Whats up with `Module.concat/1`? {: .info} > -> This `Module.concat/1` prevents a [compile-time dependency](https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects) from this router module to the schema module. It is an implementation detail of how `forward/2` works that you end up with a compile-time dependency on the schema, but there is no need for this dependency, and that dependency can have *drastic* impacts on your compile times in certain scenarios. +> This `Module.concat/1` prevents a [compile-time dependency](https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects) from this router module to the schema module. It is an implementation detail of how `forward/2` works that you end up with a compile-time dependency on the schema, but there is no need for this dependency, and that dependency can have _drastic_ impacts on your compile times in certain scenarios. If you started with `mix new ...` instead of `mix phx.new ...` and you want to still use phoenix, the fastest path that way is typically to just create a new @@ -161,9 +161,9 @@ Topics: How Tos: -- [Authorize With GraphQL](/documentation/how_to/authorize-with-graphql.md) -- [Handle Errors](/documentation/how_to/handle-errors.md) -- [Use Enums with GraphQL](/documentation/how_to/use-enums-with-graphql.md) -- [Use JSON with GraphQL](/documentation/how_to/use-json-with-graphql.md) +- [Authorize With GraphQL](/documentation/topics/authorize-with-graphql.md) +- [Handle Errors](/documentation/topics/handle-errors.md) +- [Use Enums with GraphQL](/documentation/topics/use-enums-with-graphql.md) +- [Use JSON with GraphQL](/documentation/topics/use-json-with-graphql.md) [Monitoring](/documentation/monitoring.md) diff --git a/lib/type.ex b/lib/type.ex index a63f047..a0d8f93 100644 --- a/lib/type.ex +++ b/lib/type.ex @@ -58,7 +58,7 @@ defmodule AshGraphql.Type do @doc """ Used for map/embedded types embedded in unions, to avoid nesting them in a key by their name. - See [the unions guide](/documentation/how_to/use-unions-with-graphql.md) for more. + See [the unions guide](/documentation/topics/use-unions-with-graphql.md) for more. """ @callback graphql_unnested_unions(Ash.Type.constraints()) :: [atom()] diff --git a/mix.exs b/mix.exs index 6bbb438..38c3b85 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule AshGraphql.MixProject do use Mix.Project @description """ - An absinthe-backed graphql extension for Ash + The extension for building GraphQL APIs with Ash """ @version "1.0.0-rc.5" @@ -35,7 +35,7 @@ defmodule AshGraphql.MixProject do defp docs do [ - main: "getting-started-with-graphql", + main: "readme", source_ref: "v#{@version}", logo: "logos/small-logo.png", extra_section: "GUIDES", @@ -55,18 +55,20 @@ defmodule AshGraphql.MixProject do end end, extras: [ + {"README.md", title: "Home"}, "documentation/tutorials/getting-started-with-graphql.md", - "documentation/how_to/authorize-with-graphql.md", - "documentation/how_to/handle-errors.md", - "documentation/how_to/use-enums-with-graphql.md", - "documentation/how_to/use-json-with-graphql.md", - "documentation/how_to/use-subscriptions-with-graphql.md", - "documentation/how_to/use-unions-with-graphql.md", - "documentation/how_to/use-maps-with-graphql.md", + "documentation/topics/authorize-with-graphql.md", + "documentation/topics/handle-errors.md", + "documentation/topics/use-enums-with-graphql.md", + "documentation/topics/use-json-with-graphql.md", + "documentation/topics/use-subscriptions-with-graphql.md", + "documentation/topics/use-unions-with-graphql.md", + "documentation/topics/use-maps-with-graphql.md", "documentation/topics/monitoring.md", "documentation/topics/graphql-generation.md", "documentation/topics/modifying-the-resolution.md", "documentation/topics/relay.md", + "documentation/topics/upgrade.md", "documentation/dsls/DSL:-AshGraphql.Domain.md", "documentation/dsls/DSL:-AshGraphql.Resource.md" ],