graphql-hive / graphql-hive/federation-composition

Cannot compose `_service.sdl` from `@apollo/subgraph` on federation v2.8+: duplicate `@federation__context` / `@federation__fromContext`

Open
#326 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
53
Forks
8
Avg merge
20h 50m
Merged PRs (30d)
10

Description

## Summary

A subgraph built with `@apollo/subgraph` that links federation v2.8 or later cannot be composed from its own `_service.sdl`. Composition fails with:

```
There can be only one directive named "@federation__context".
There can be only one directive named "@federation__fromContext".
```

`@apollo/composition` accepts the same input.

## Reproduction

```js
// npm i @apollo/subgraph @theguild/federation-composition graphql
import { buildSubgraphSchema } from '@apollo/subgraph';
import { composeServices, compositionHasErrors } from '@theguild/federation-composition';
import { graphqlSync, parse } from 'graphql';

const typeDefs = parse(`
extend schema @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key"])
type Product @key(fields: "slug") { slug: String! name: String! }
type Query { product(slug: String!): Product }
`);

// The SDL a running subgraph serves.
const sdl = graphqlSync({
schema: buildSubgraphSchema([{ typeDefs, resolvers: {} }]),
source: '{ _service { sdl } }',
}).data._service.sdl;

const result = composeServices([{ name: 's', url: 'http://localhost:4000/graphql', typeDefs: parse(sdl) }]);
console.log(compositionHasErrors(result) ? result.errors.map((e) => e.message) : 'OK');
```

Output:

```
[
'[s] There can be only one directive named "@federation__context".',
'[s] There can be only one directive named "@federation__fromContext".'
]
```

## Affected versions

Reproduced on 0.24.1 and 0.25.0.

The boundary is the linked federation version. Same repro, varying only the `@link` url:

| federation | directive definitions in `_service.sdl` | result |
| ---------- | --------------------------------------- | ------ |
| v2.0 | 10 | OK |
| v2.3 | 12 | OK |
| v2.5 | 14 | OK |
| v2.7 | 15 | OK |
| v2.8 | 17 | FAIL |
| v2.9 | 19 | FAIL |

v2.8 is where `@context` and `@fromContext` entered the spec.

## Comparison with @apollo/composition

Same SDL, same call shape:

```
@apollo/composition 2.14.4 : OK
@theguild/federation-composition 0.25.0 : FAIL (2)
```

## Why it matters

`_service.sdl` is what a running subgraph serves, so it is the SDL you get by introspecting a live service instead of reading an authored file. Anything composing from a live subgraph hits this: `hive dev --url ...`, registry ingestion via introspection, and CI gates that compose a service's emitted SDL.

Note that only these two collide. The other 17 definitions `_service.sdl` prints are tolerated, which is what makes this look like an oversight rather than a deliberate rejection of subgraph-supplied definitions.

## Workaround

Strip directive definitions from the composer's input before calling `composeServices`. Dropping only the two colliding definitions is enough. In our case, dropping the whole `link` / `key` / `federation__` family produced a byte-identical supergraph, so the coarser filter is safe too.

## Possibly related

#85 tracks `@context` and `@fromContext` composition support and may share a root cause.

Contributor guide

No contributing guide indexed for this repository

Research direction

Run the supplied reproduction using buildSubgraphSchema, graphqlSync, and composeServices, then compare the emitted SDL and composition behavior across federation v2.7 and v2.8. Trace how composeServices handles the @federation__context and @federation__fromContext definitions. Done means composing the emitted _service.sdl succeeds for federation v2.8 and later without duplicate-directive errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, typescript
Domain
api, backend-api-design
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.