GraphQL Federation v2 bridge for API Platform v3 — as a Symfony bundle.
This zero-config Symfony bundle decorates API Platform's SchemaBuilderInterface to inject Federation v2 infrastructure (_service, _entities, @key directives) into the composed GraphQL schema. Mark any API Platform resource as a federation entity with a single #[FederationKey] attribute — no YAML, no XML, no extra wiring required. Reference resolution delegates to API Platform's own state providers by default, and can be overridden per type.
| Dependency | Version |
|---|---|
| PHP | ^8.1 |
api-platform/core |
^3.0 |
symfony/framework-bundle |
^6.4 || ^7.0 |
toppynl/graphql-federation |
^1.0 |
composer require toppynl/api-platform-federationRegister the bundle in config/bundles.php:
Toppynl\ApiPlatformFederation\ToppynlApiPlatformFederationBundle::class => ['all' => true],use ApiPlatform\Metadata\ApiResource;
use Toppynl\ApiPlatformFederation\Attribute\FederationKey;
#[ApiResource]
#[FederationKey(fields: 'id')]
class Product
{
public string $id;
public string $name;
}That's it — _service, _entities, and @key(fields: "id") in the SDL are injected automatically.
| Attribute | Applies to | Purpose |
|---|---|---|
#[FederationKey(fields: 'id')] |
class | Mark as resolvable entity key |
#[FederationKey(fields: 'id', resolvable: false)] |
class | Reference-only stub (no resolver registered) |
#[FederationExternal] |
class / property | Mark as externally owned |
#[FederationShareable] |
class / property | Allow field sharing across subgraphs |
#[FederationInaccessible] |
class / property | Hide from composed schema |
#[FederationOverride(from: 'other-subgraph')] |
property | Override field from another subgraph |
A type can expose more than one key field set by stacking #[FederationKey] — all instances must agree on resolvable:
#[ApiResource]
#[FederationKey(fields: 'id')]
#[FederationKey(fields: 'sku')]
class Product
{
public string $id;
public string $sku;
public string $name;
}Both keys are registered; the gateway may use either to resolve a Product reference.
By default the bundle resolves _entities lookups through API Platform's state provider. To override this for a specific type, pre-register a callable in ReferenceResolverRegistry before the schema is built — the loader skips types that already have a resolver:
use Toppynl\GraphQLFederation\ReferenceResolverRegistry;
class ProductReferenceResolverRegistrar
{
public function __construct(private ReferenceResolverRegistry $registry) {}
public function boot(): void
{
$this->registry->register('Product', function (array $rep): mixed {
return $this->productRepo->findBySku($rep['sku']);
});
}
}FederatedSchemaBuilderDecoratorwraps API Platform'sSchemaBuilderInterface, calls the inner builder to obtain the base schema, then hands it off toFederatedSchemaBuilderfromtoppynl/graphql-federation.AttributeReferenceResolverLoaderreads#[FederationKey]attributes via reflection on each mapped resource class, collecting key field sets and the sharedresolvableflag.FederatedSchemaBuilder(core library) splices_service,_entities, and all@key/@external/@shareable/@inaccessible/@overridedirectives into the final schema. See toppynl/graphql-federation for the underlying mechanics.
MIT