Federated Apollo Graph - Join Entities Across Subgraphs (Elixir/Absinthe)

42 Views Asked by At

I am using Elixir/Phoenix with Absinthe to define a federated graph in Apollo. I have looked through the Odyssey Voyage I course but cant seem to piece together what I need. Here is my scenario:

I have two services (or subgraphs). One is the ProjectLocation subgraph and the other is the Inspection subgraph. A ProjectLocation can have 1 to many Inspections. What I need is to query a ProjectLocation and get it back with all of it's Inspections based on inspection parameters. I have my modules separated out into types, mutations, queries, resolvers, and then import them all into one schema file.

ProjectLocation Subgraph Type

defmodule MyProject.Schema.Types.ProjectLocation do


use Absinthe.Schema.Notation

  object :project_location do
    field :id, :id
    field :project_id, :id
    field :address, :string
  end

  input_object :input_project_location_params do
    field :id, :id
    field :project_id, :id
    field :address, :string
  end
end

ProjectLocation Subgraph Query

defmodule MyProject.Schema.Queries.ProjectLocation do
  use Absinthe.Schema.Notation

  alias MyProject.Schema.Resolvers

  object :project_location_queries do

    @desc "Find ProjectLocation"
    field :project_location, type: :project_location do
      arg(:project_location_id, non_null(:id))
      resolve(&Resolvers.ProjectLocation.get_project_location/3)
    end
    
  end
end

Inspection Subgraph Type

defmodule MyInspectionProject.Schema.Types.Inspection do
    use Absinthe.Schema.Notation

    object :inspection do
      field :id, :id
      field :project_id, :id
      field :inspection_location, :id //This is the project location id (bad naming on my part)
      field :status, :string
    end

    @desc "Inspection"
    input_object :input_inspection_params do
      field :id, :id
      field :project_id, :id
      field :inspection_location, :id
      field :status, :string
    end
  end

Inspection Subgraph Query

defmodule MyInspectionProject.Schema.Queries.Inspection do
    use Absinthe.Schema.Notation

    alias MyInspectionProject.Schema.Resolvers

    object :inspection_queries do
      @desc "Find Inspection"
      field :inspection, type: :inspection do
        arg(:inspection, non_null(:input_inspection_params))
        resolve(&Resolvers.Inspection.get_inspection/3)
      end
    end
  end

Using a React front end we would write the query for a project location like this:

const GET_PROJECT_LOCATIONS = gql`
  query ProjectLocations($projectId: ID!) {
    projectLocations(projectId: $projectId) {
      id
      projectId
      address
    }
  }
`;

My question I guess is how do I set up the types and write the query to preload and query the inspections as a "subquery?" Is it as simple as setting up an Inspection type in the ProjectLocation subgraph and writing the query somewhere along the lines of this?

const GET_PROJECT_LOCATIONS = gql`
  query ProjectLocations($projectId: ID!) {
    projectLocations(projectId: $projectId) {
      id
      projectId
      address
      inspection($status: STRING){
         id
         project_id
         inspection_location
         status
      }
    }
  }
`;

If so what would the variables object look like to pass into this GraphQL call. Also forgot to mention I'm using the useLazyQuery hook from the Apollo Client library in React.

1

There are 1 best solutions below

2
Peaceful James On

That graphql would be valid if the inspection status was also an arg to the root query (2nd arg, after projectID). See here for an example: https://elixirforum.com/t/why-do-you-have-to-re-declare-types-in-graphql-queries-when-you-use-variables/52723/10

It would look like this for you:

  query ProjectLocations($projectId: ID!, $status: STRING) {
    projectLocations(projectId: $projectId) {

Also add an :inspection field in your project_location object definition. There you can handle the status arg by writing the field with a resolve function. It will look like this:

field :inspection, :inspection do
  resolve(fn project_location, args, resolution ->
    inspection = get_inspection(project_location, args)
    {:ok, inspection}
  end)
end