Multiple endpoints/schemas with Absinthe?

The format to register an Absinthe endpoint in Phoenix is forward "/graphql", MyAppWeb.GraphQL.Plug and you can only forward to a plug once. I’m wondering if there is a way around this.

Some background:

I currently have two schemas. One for public access and one for authenticated “CMS” access. I choose the schema depending on if there is an authenticated user or not.

defmodule MyAppWeb.GraphQL.Plug do
  alias MyAppWeb.GraphQL

  @guest_schema Absinthe.Plug.init(schema: GraphQL.Public.Schema)
  @admin_schema Absinthe.Plug.init(schema: GraphQL.Admin.Schema)

  def init(opts), do: opts

  def call(conn, _opts) do
    case conn.private[:absinthe].context do
      %{authenticated_user: _user} -> Absinthe.Plug.call(conn, @admin_schema)
      _ -> Absinthe.Plug.call(conn, @guest_schema)
    end
  end
end

The problem: Through API discovery (graphiql etc.) only the public schema is visible. Is there a way to achieve this through multiple API endpoints or is there a different, preferred approach?

1 Like

This is how I ended up solving it: I made one schema with all queries. All publicly accessible fields get a published_ prefix so they are easily distinguishable. All admin queries are protected by a RequireAuthentication middleware that checks for an authenticated user and bails if it isn’t there.

There are separate types as well as a published podcast may have different attributes exposed as a nonpublic podcast.

Query definitions look like this:

defmodule MyAppWeb.GraphQL.Schema do
  use Absinthe.Schema

  # ...

  query do
    # Public queries

    @desc "Get all published podcasts"
    field :published_podcasts, list_of(:published_podcast) do
      resolve &Public.Resolvers.Directory.list_podcasts/3
    end

    @desc "Get one published podcast"
    field :published_podcast, :published_podcast do
      arg :id, non_null(:id)

      resolve &Public.Resolvers.Directory.find_podcast/3
    end

    @desc "Get one published episode"
    field :published_episode, :published_episode do
      arg :id, non_null(:id)

      resolve &Public.Resolvers.Directory.find_episode/3
    end

    # Admin queries

    @desc "Get all podcasts"
    field :podcasts, list_of(:podcast) do
      middleware Middleware.RequireAuthentication
      resolve &Admin.Resolvers.Editor.list_podcasts/3
    end

    @desc "Get one podcast"
    field :podcast, :podcast do
      arg :id, non_null(:id)

      middleware Middleware.RequireAuthentication
      resolve &Admin.Resolvers.Editor.find_podcast/3
    end

    @desc "Get one episode"
    field :episode, :episode do
      arg :id, non_null(:id)

      middleware Middleware.RequireAuthentication
      resolve &Admin.Resolvers.Editor.find_episode/3
    end
  end
end
1 Like

Thanks for following up your own question.

I’m having the same issue, and though your solution shall work, it feels like a workaround. This is unfortunate that it is not possible to handle this case properly.

If this is about the error when trying to forward to a plug twice. Writing a small wrapper plug fixes it:

1 Like