How to use a common enum type in multiple subgraphs

I encountered a scenario where a common Enum type needs to be shared across multiple subgraphs. According to the documentation and my testing, the Enum type must be defined separately in each subgraph. However, this approach leads to issues during schema evolution. If the Enum type is used as both an input and output type, and we need to modify it (e.g., by adding a new value), publishing a subgraph with the updated Enum definition fails due to conflicts with other subgraphs that still use the old definition in Apollo Studio.

To resolve this conflict, we need to delete the affected subgraphs first, update their schemas with the new Enum definition, and then republish them. Unfortunately, this process causes downtime in the production system due to the subgraph deletion operation. Does anyone have suggestions for addressing this issue? Sharing Enum types across multiple subgraphs seems like a common requirement.

Thank you!

Because of the difference between input enums and return value enums, I typically recommend defining them separately so their different composition methods (combined for return enums, intersection for input enums) can be safely respected in graphs.

As far as solving the sort of conflict you’re talking about, if you’re on Federation version 2 you can add the new enum value to the first subgraph with the @inaccessible directive (only apply this directive in the first subgraph). This will “add” it without including it in the schema client applications see and should solve the conflicting definition problem. Then, once all the subgraphs with the enum add the new value, you can remove the @inaccessible directive from the value in that first subgraph and it will become available in the API.

The risks with an @inaccessible enum value that is part of an enum that is both returned and used as an input is a subgraph could return the @inaccessible enum value before it is available in the graph (which would be bad). You could feature flag the code to return it to protect against that from happening (admittedly, this adds complexity to the change). This is a big reason why I recommend differentiating between return enums and input enums.

1 Like

Thanks, Greg! Can you explain a little bit more on how other subgraphs add the new enum value. I understand that we need to put @inaccessible in the first subgraph. Do we need to do that too when we add the new enum value in other subgraphs?

This assumes you’re using Apollo GraphQL Federation and are on version 2.

You just have to add @inaccessible in the first subgraph. It tells composition not to include the value in the supergraph’s API schema that’s built from your subgraphs. Note: it will be in the Supergraph schema and Apollo Studio, but it will be marked that it is inaccessible. Clients won’t be able to see it in the API schema.

in the other subgraphs, you add the enum value without @inaccessible. It only takes one instance of the enum value having @inaccessible applied for it not to be included in the supergraph’s API schema.

Once all subgraphs have the new enum value, you’ll remove @inaccessible from the one instance of the enum value it was applied to and then the value will appear in the supergraph API schema.

Something like:

# Subgraph 1
enum Color {
  RED
}

# Subgraph 2
enum Color {
  RED
}

Step 1:

Add new enum value without @inaccessible directive to first subgraph.

# Subgraph 1
enum Color {
  RED
  BLUE @inaccessible
}

# Subgraph 2
enum Color {
  RED
}

Step 2:

Add new enum value without directive to all other subgraphs.

# Subgraph 1
enum Color {
  RED
  BLUE @inaccessible
}

# Subgraph 2
enum Color {
  RED
  BLUE

Step 3:

Once the new enum value has been added to all subgraphs, remove the @inaccessible directive from the enum value.

# Subgraph 1
enum Color {
  RED
  BLUE
}

# Subgraph 2
enum Color {
  RED
  BLUE

It’s also worth noting that this is really only necessary when on Apollo Federation version 2 if the enum is both a return type enum and an input argument enum. That’s when combining mismatched definitions becomes a problem.

If the enum is only a return type enum (returned by a field), then mismatched definitions will just be combined because each subgraph will only return the values they know about. The supergraph API schema can safely say “all values are possible.”

For argument enums, mismatched enum definitions are merged by intersection. Only the values all subgraphs have are included in the supergraph’s API schema. That way only enum values all subgraphs can handle can be provided by the client using the API. When all subgraphs add a new enum value, then the supergraph will include it in the api schema.

It’s only when the enum is used as both a return type and an argument that composition can’t really merge them and complains.