| Crates.io | graphql-composition |
| lib.rs | graphql-composition |
| version | 0.12.1 |
| created_at | 2023-10-31 15:33:15.55831+00 |
| updated_at | 2025-09-25 16:16:54.05239+00 |
| description | An implementation of GraphQL federated schema composition |
| homepage | |
| repository | https://github.com/grafbase/grafbase/tree/main/engine/crates/composition |
| max_upload_size | |
| id | 1019933 |
| size | 910,652 |
An implementation of GraphQL federated schema composition, and work-in-progress implementation of the Composite Schemas spec.
On top of Federation v2 support and Composite Schemas spec support, this crate also includes information about directives imported from Grafbase extension definitions with @link in the composed schema.
For a directive to be composed as an extension directive, it must be imported from an @linked schema, and that schema's URL must either:
file: scheme.url that starts with https://grafbase.com/extensionsIn the following example, all @link directives would be interpreted as linking to extensions, and all directives from these extensions would be composed as extension directives:
extend schema
@link(url: "file:///path/to/extension", import: ["@test"])
@link(url: "https://grafbase.com/extensions/kafka/0.2.1")
@link(url: "file:///path/to/another/extension", as: "extension-name")
@link(
url: "https://grafbase.com/extensions/rest/0.5.0"
import: ["@restEndpoint", "@rest"]
)
use graphql_composition::{Subgraphs, compose, render_federated_sdl};
let user_subgraph = r#"
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key"])
type Query {
findUserByEmail(email: String!): User
}
type User @key(fields: "id") {
id: ID!
name: String!
}
"#;
let cart_subgraph = r#"
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3",
import: ["@key", "@shareable"])
type User @key(fields: "id") {
id: ID!
cart: Cart
}
type Cart @shareable {
items: [String!]!
}
"#;
let mut subgraphs = Subgraphs::default();
subgraphs.ingest_str(&user_subgraph, "users-service", "http://users.example.com").unwrap();
subgraphs.ingest_str(&cart_subgraph, "carts-service", "http://carts.example.com").unwrap();
let composed = compose(&subgraphs).into_result().unwrap();
let composed = render_federated_sdl(&composed).unwrap();
let expected = r#"
directive @core(feature: String!) repeatable on SCHEMA
directive @join__owner(graph: join__Graph!) on OBJECT
directive @join__type(
graph: join__Graph!
key: join__FieldSet
resolvable: Boolean = true
) repeatable on OBJECT | INTERFACE
directive @join__field(
graph: join__Graph
requires: join__FieldSet
provides: join__FieldSet
) on FIELD_DEFINITION
directive @join__graph(name: String!, url: String!) on ENUM_VALUE
directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE
directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION
scalar join__FieldSet
enum join__Graph {
USERS_SERVICE @join__graph(name: "users-service", url: "http://users.example.com")
CARTS_SERVICE @join__graph(name: "carts-service", url: "http://carts.example.com")
}
type User
@join__type(graph: USERS_SERVICE, key: "id")
@join__type(graph: CARTS_SERVICE, key: "id")
{
cart: Cart @join__field(graph: CARTS_SERVICE)
id: ID!
name: String! @join__field(graph: USERS_SERVICE)
}
type Cart
@join__type(graph: CARTS_SERVICE)
{
items: [String!]!
}
type Query
{
findUserByEmail(email: String!): User @join__field(graph: USERS_SERVICE)
}
"#;
assert_eq!(expected.trim(), composed.trim());
The crate is being actively developed and maintained.