apollographql / apollographql/federation
Supergraph Schema is not currently lossless
- Dominant language
- TypeScript
- Stars
- 727
- Forks
- 276
- Avg merge
- 1h 47m
- Merged PRs (30d)
- 1
Description
# Description
Currently, when schema is composed into a supergraph schema, some of the schema can be omitted when the schema is merged via intersection, such as with Input Value Definitions or input enum values. This makes the supergraph schema lossy rather than lossless, and does not make it a sufficient source of truth for the current state of the supergraph.
# Example
Say I have two supergraphs with the following schemas, which I will compose into a supergraph schema (currently using rover and federation v2.1):
```gql
# Subgraph A
type Query {
booksByGenre(genre: BookGenreEnum!, address: Address): [String!]! @shareable
}
input Address {
street: String!
city: String
state: String
postalCode: Int!
}
enum BookGenreEnum {
FICTION
NON_FICTION
}
type Book @shareable {
title: String!
genre: String!
}
```
```gql
# Subgraph B
type Query {
booksByGenre(genre: BookGenreEnum!): [String!]! @shareable
}
input Address {
street: String!
postalCode: Int!
}
enum BookGenreEnum {
FICTION
}
type Book @shareable {
title: String!
}
```
# Encountered behavior
The composed supergraph schema looks like this:
```gql
# Supergraph Schema
type Query
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
booksByGenre(genre: BookGenreEnum!): [String!]! @join__field(graph: SUBGRAPH_A) @join__field(graph: SUBGRAPH_B)
}
input Address
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
street: String!
postalCode: Int!
}
enum BookGenreEnum
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
FICTION
}
type Book
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
title: String!
genre: String! @join__field(graph: SUBGRAPH_A)
}
```
Note that `Query.booksByGenre(address)`, `Address.city`, `Address.state`, and `BookGenreEnum.NON_FICTION` are all omitted from the supergraph schema.
# Expected behavior
I would expect `Query.booksByGenre.address`, `Address.city1, `Address.state`, and `BookGenreEnum.NON_FICTION` to still be present in the graph, yet somehow marked that they are inaccessible or have an inconsistent representation across the graph.
Fields perform this merging in a more lossless manner. If you look at `Book.genre`, it is in Subgraph A but not Subgraph B. This is denoted in the supergraph schema via the `@join__field` directive. I would imagine something similar (if not the same directive) could be used for these other child types:
```gql
# Suggested Supergraph Schema
type Query
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
booksByGenre(
genre: BookGenreEnum!
address: Address @join__field(graph: SUBGRAPH_A)
): [String!]! @join__field(graph: SUBGRAPH_A) @join__field(graph: SUBGRAPH_B)
}
input Address
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
street: String!
postalCode: Int!
city: String @join__field(graph: SUBGRAPH_A)
state: String @join__field(graph: SUBGRAPH_A)
}
enum BookGenreEnum
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
FICTION
NON_FICTION @join__field(graph: SUBGRAPH_A)
}
type Book
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
title: String!
genre: String! @join__field(graph: SUBGRAPH_A)
}
```
As an alternative, these child types could be included with the `@inaccessible` directive along with the `@join__type` directive (because I still think we would want to know _where_ the child type appears within the subgraphs).
```gql
enum BookGenreEnum
@join__type(graph: SUBGRAPH_A)
@join__type(graph: SUBGRAPH_B)
{
FICTION
NON_FICTION @join__type(graph: SUBGRAPH_A) @inaccessible
}
```
# Additional Notes
This is important in order to consider the composed schema as the source of truth and layer additional schema rules on top of the loose rules in Federation 2. Running Federation v2 as an organization-wide platform, we want the flexibility to consider the ownership of the schema and when new children are being added to object types (like enum values and input values).
Without a lossless supergraph schema, we would have to consider each subgraph's schema individually and compile our own concept of a "supergraph" in order to consider the lossless state of the graph.
It would be far better if the supergraph was lossless and could act as the source of truth for the current state of the graph.
Contributor guide
Research direction
Start by reproducing composition with the Subgraph A and Subgraph B schemas in the issue, using the noted Rover and Federation v2.1 setup. Compare the resulting supergraph schema with the expected lossless form, focusing on input fields, input values, and enum values. Done means those child definitions remain represented with their subgraph ownership or an agreed inaccessible representation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, typescript
- Domain
- api, backend-api-design
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100