diff --git a/dictionary.txt b/dictionary.txt index 377bcb7c39a..9b4844605e5 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -5,8 +5,10 @@ accessibilities agrc Alderaan Andi +appsettings ASPDEPR004 ASPDEPR008 +aspnet aspnetcore Astromech automagically @@ -37,6 +39,7 @@ combinators contentfiles Contoso CQRS +creds dataloaders debuggable decompile @@ -157,6 +160,7 @@ runbooks Satisfiability Senn shoooe +SHORTSHA Silmarillion skybridge Skywalker @@ -184,6 +188,7 @@ Tatooine Tengler Testcontainers Toub +Touchpoint TOWGS Trimmable Tzdb diff --git a/website/src/docs/fusion/v16/attribute-and-directive-reference.md b/website/src/docs/fusion/v16/attribute-and-directive-reference.md index f875cc3454f..2a830a79bff 100644 --- a/website/src/docs/fusion/v16/attribute-and-directive-reference.md +++ b/website/src/docs/fusion/v16/attribute-and-directive-reference.md @@ -6,28 +6,28 @@ Quick reference for all Fusion-related attributes and their GraphQL directive eq # Attribute and Directive Reference Table -| Attribute | Directive | Description | Guide Page | -|---|---|---|---| -| `[ObjectType]` | — | Maps static class as extension to entity type T | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[QueryType]` | — | Marks class as contributing Query root fields | [Getting Started](/docs/fusion/v16/getting-started) | -| `[MutationType]` | — | Marks class as contributing Mutation root fields | [Getting Started](/docs/fusion/v16/getting-started) | -| `[SubscriptionType]` | — | Marks class as contributing Subscription root fields | [Getting Started](/docs/fusion/v16/getting-started) | -| `[Lookup]` | `@lookup` | Declares field as entity lookup resolver | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[NodeResolver]` | — | Marks as Relay node resolver | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[Internal]` | `@internal` | Hides lookup from composed schema | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[Shareable]` | `@shareable` | Allows multiple subgraphs to resolve field | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[Parent(requires: "...")]` | — | Declares field requirements from parent | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[Require("...")]` | `@require` | Declares complex field requirements | [Getting Started](/docs/fusion/v16/getting-started), [Adding a Subgraph](/docs/fusion/v16/adding-a-subgraph) | -| `[EntityKey("...")]` | `@key` | Declares entity key for resolution | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[BindMember(nameof(...))]` | — | Replaces raw FK with resolved entity | [Adding a Subgraph](/docs/fusion/v16/adding-a-subgraph) | -| `[Tag("...")]` | `@tag` | Applies tag for composition filtering | [Composition](/docs/fusion/v16/composition) | -| `[DataLoader]` | — | Source-generates DataLoader interface | [Getting Started](/docs/fusion/v16/getting-started), [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[UsePaging]` | — | Enables cursor-based pagination | [Getting Started](/docs/fusion/v16/getting-started) | -| `[ID]` | — | Declares field as Relay-style ID | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[Inaccessible]` | `@inaccessible` | Hides from composite schema | [Composition](/docs/fusion/v16/composition) | -| `[Override(from: "...")]` | `@override` | Migrates field ownership | [Deployment and CI/CD](/docs/fusion/v16/deployment-and-ci-cd) | -| `[Provides("...")]` | `@provides` | Declares locally-resolvable subfields | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | -| `[External]` | `@external` | Field defined by another subgraph | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| Attribute | Directive | Description | Guide Page | +| --------------------------- | --------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `[ObjectType]` | — | Maps static class as extension to entity type T | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[QueryType]` | — | Marks class as contributing Query root fields | [Getting Started](/docs/fusion/v16/getting-started) | +| `[MutationType]` | — | Marks class as contributing Mutation root fields | [Getting Started](/docs/fusion/v16/getting-started) | +| `[SubscriptionType]` | — | Marks class as contributing Subscription root fields | [Getting Started](/docs/fusion/v16/getting-started) | +| `[Lookup]` | `@lookup` | Declares field as entity lookup resolver | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[NodeResolver]` | — | Marks as Relay node resolver | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[Internal]` | `@internal` | Hides lookup from composed schema | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[Shareable]` | `@shareable` | Allows multiple subgraphs to resolve field | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[Parent(requires: "...")]` | — | Declares field requirements from parent | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[Require("...")]` | `@require` | Declares complex field requirements | [Getting Started](/docs/fusion/v16/getting-started), [Adding a Subgraph](/docs/fusion/v16/adding-a-subgraph) | +| `[EntityKey("...")]` | `@key` | Declares entity key for resolution | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[BindMember(nameof(...))]` | — | Replaces raw FK with resolved entity | [Adding a Subgraph](/docs/fusion/v16/adding-a-subgraph) | +| `[Tag("...")]` | `@tag` | Applies tag for composition filtering | [Composition](/docs/fusion/v16/composition) | +| `[DataLoader]` | — | Source-generates DataLoader interface | [Getting Started](/docs/fusion/v16/getting-started), [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[UsePaging]` | — | Enables cursor-based pagination | [Getting Started](/docs/fusion/v16/getting-started) | +| `[ID]` | — | Declares field as Relay-style ID | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[Inaccessible]` | `@inaccessible` | Hides from composite schema | [Composition](/docs/fusion/v16/composition) | +| `[Override(from: "...")]` | `@override` | Migrates field ownership | [Deployment and CI/CD](/docs/fusion/v16/deployment-and-ci-cd) | +| `[Provides("...")]` | `@provides` | Declares locally-resolvable subfields | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | +| `[External]` | `@external` | Field defined by another subgraph | [Entities and Lookups](/docs/fusion/v16/entities-and-lookups) | # See Also diff --git a/website/src/docs/fusion/v16/coming-from-apollo-federation.md b/website/src/docs/fusion/v16/coming-from-apollo-federation.md index e0576c74fa6..1058eef2672 100644 --- a/website/src/docs/fusion/v16/coming-from-apollo-federation.md +++ b/website/src/docs/fusion/v16/coming-from-apollo-federation.md @@ -12,23 +12,23 @@ This guide maps Apollo Federation concepts to their Fusion equivalents, explains The table below maps Apollo Federation concepts to their Fusion equivalents. Some are straightforward renames; others involve meaningful behavioral changes. The "Key Difference" column flags which is which. -| Apollo Federation | HotChocolate Fusion | Key Difference | -|---|---|---| -| `@key(fields: "id")` | `[Lookup]` on a Query field | Fusion uses explicit, typed lookup fields instead of implicit `_entities`. No `@key` directive needed. | -| `__resolveReference` / `_entities` query | Regular Query fields with `[Lookup]` | Lookups are real fields you can call and test directly. | -| `@external` | `[External]` (rarely needed) | Same concept, but less frequently needed in Fusion. | -| `@requires(fields: "...")` on a field | `[Require("...")]` on an argument | Argument-level, not field-level. Required arguments are hidden from the composite schema. | -| `@provides(fields: "...")` | `[Provides("...")]` / `[Parent(requires: "...")]` | Same optimization concept. | -| `@shareable` | `[Shareable]` | Same concept and semantics. Key fields are automatically shareable. | -| `@override(from: "...")` | `[Override(from: "...")]` | Same concept. | -| `@inaccessible` | `[Inaccessible]` | Same concept. | -| `@tag` | `[Tag]` | Same concept. | -| Apollo Router / Gateway | Fusion Gateway (`AddGraphQLGateway()`) | A .NET ASP.NET Core app, not a separate binary. | -| `rover` CLI | Nitro CLI (`nitro fusion ...`) | Schema composition, validation, and delivery. | -| GraphOS managed federation | Nitro cloud or local CI/CD composition | Build-time composition. Works fully offline. | -| Supergraph schema (SDL) | Composite schema + `.far` archive | Binary archive containing the composed schema and subgraph metadata. | -| Federation subgraph library (`@apollo/subgraph`) | No equivalent needed | Subgraphs are standard HotChocolate servers. No federation library. | -| `_service { sdl }` introspection | `dotnet run -- schema export` | Schema export is a CLI command, not a runtime introspection field. | +| Apollo Federation | HotChocolate Fusion | Key Difference | +| ------------------------------------------------ | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| `@key(fields: "id")` | `[Lookup]` on a Query field | Fusion uses explicit, typed lookup fields instead of implicit `_entities`. No `@key` directive needed. | +| `__resolveReference` / `_entities` query | Regular Query fields with `[Lookup]` | Lookups are real fields you can call and test directly. | +| `@external` | `[External]` (rarely needed) | Same concept, but less frequently needed in Fusion. | +| `@requires(fields: "...")` on a field | `[Require("...")]` on an argument | Argument-level, not field-level. Required arguments are hidden from the composite schema. | +| `@provides(fields: "...")` | `[Provides("...")]` / `[Parent(requires: "...")]` | Same optimization concept. | +| `@shareable` | `[Shareable]` | Same concept and semantics. Key fields are automatically shareable. | +| `@override(from: "...")` | `[Override(from: "...")]` | Same concept. | +| `@inaccessible` | `[Inaccessible]` | Same concept. | +| `@tag` | `[Tag]` | Same concept. | +| Apollo Router / Gateway | Fusion Gateway (`AddGraphQLGateway()`) | A .NET ASP.NET Core app, not a separate binary. | +| `rover` CLI | Nitro CLI (`nitro fusion ...`) | Schema composition, validation, and delivery. | +| GraphOS managed federation | Nitro cloud or local CI/CD composition | Build-time composition. Works fully offline. | +| Supergraph schema (SDL) | Composite schema + `.far` archive | Binary archive containing the composed schema and subgraph metadata. | +| Federation subgraph library (`@apollo/subgraph`) | No equivalent needed | Subgraphs are standard HotChocolate servers. No federation library. | +| `_service { sdl }` introspection | `dotnet run -- schema export` | Schema export is a CLI command, not a runtime introspection field. | ## What Fusion Does Not Need @@ -618,11 +618,11 @@ Navigate to `http://localhost:5000/graphql` to open the Nitro IDE and run cross- Replace Apollo's `rover` commands with Nitro CLI equivalents. -| Apollo (`rover`) | Fusion (`nitro`) | -|---|---| -| `rover subgraph check` | `nitro fusion validate` | -| `rover subgraph publish` | `nitro fusion upload` + `nitro fusion publish` | -| `rover supergraph compose` | `nitro fusion compose` | +| Apollo (`rover`) | Fusion (`nitro`) | +| -------------------------- | ---------------------------------------------- | +| `rover subgraph check` | `nitro fusion validate` | +| `rover subgraph publish` | `nitro fusion upload` + `nitro fusion publish` | +| `rover supergraph compose` | `nitro fusion compose` | #### Schema Upload (Replaces `rover subgraph publish`) diff --git a/website/src/docs/fusion/v16/composition.md b/website/src/docs/fusion/v16/composition.md index 1a37b6eb571..fd5c33cf067 100644 --- a/website/src/docs/fusion/v16/composition.md +++ b/website/src/docs/fusion/v16/composition.md @@ -149,11 +149,11 @@ When the same field appears in multiple subgraphs with different nullability, th For output fields -- fields returned by resolvers -- the merged type uses the **least restrictive** nullability. Nullable wins: -| Subgraph A | Subgraph B | Composed Result | -|------------|------------|-----------------| +| Subgraph A | Subgraph B | Composed Result | +| --------------- | --------------- | --------------- | | `name: String!` | `name: String!` | `name: String!` | -| `name: String!` | `name: String` | `name: String` | -| `name: String` | `name: String` | `name: String` | +| `name: String!` | `name: String` | `name: String` | +| `name: String` | `name: String` | `name: String` | Why? If one subgraph says a field can be null, the gateway must account for that possibility. The composed schema reflects the reality that the field might be null from at least one source. @@ -161,11 +161,11 @@ Why? If one subgraph says a field can be null, the gateway must account for that For arguments and input object fields -- values provided by clients -- the merged type uses the **most restrictive** nullability. Non-nullable wins: -| Subgraph A | Subgraph B | Composed Result | -|------------|------------|-----------------| -| `limit: Int!` | `limit: Int!` | `limit: Int!` | -| `limit: Int!` | `limit: Int` | `limit: Int!` | -| `limit: Int` | `limit: Int` | `limit: Int` | +| Subgraph A | Subgraph B | Composed Result | +| ------------- | ------------- | --------------- | +| `limit: Int!` | `limit: Int!` | `limit: Int!` | +| `limit: Int!` | `limit: Int` | `limit: Int!` | +| `limit: Int` | `limit: Int` | `limit: Int` | Why? If one subgraph requires a non-nullable argument, the gateway must always provide it. Clients must supply a value that satisfies the strictest subgraph. @@ -482,10 +482,19 @@ public static partial class ProductQueries ```graphql # Orders subgraph -enum OrderStatus { PENDING, SHIPPED, DELIVERED, CANCELLED } +enum OrderStatus { + PENDING + SHIPPED + DELIVERED + CANCELLED +} # Payments subgraph (used as input) -enum OrderStatus { PENDING, SHIPPED, DELIVERED } +enum OrderStatus { + PENDING + SHIPPED + DELIVERED +} # Missing CANCELLED! ``` @@ -520,7 +529,7 @@ type DigitalProduct implements Purchasable { # Payments subgraph interface Purchasable { price: Float! - refundPolicy: String! # New field added here + refundPolicy: String! # New field added here } ``` @@ -544,7 +553,7 @@ After merging, the `Purchasable` interface has both `price` and `refundPolicy`. **Fix:** Ensure both files exist and share the same prefix: -``` +```text Products/ ├── schema.graphqls └── schema-settings.json diff --git a/website/src/docs/fusion/v16/deployment-and-ci-cd.md b/website/src/docs/fusion/v16/deployment-and-ci-cd.md index 1faf8602efa..4cdfb26c064 100644 --- a/website/src/docs/fusion/v16/deployment-and-ci-cd.md +++ b/website/src/docs/fusion/v16/deployment-and-ci-cd.md @@ -76,6 +76,7 @@ nitro fusion publish \ ``` This command: + 1. Downloads all source schemas for the specified stage 2. Composes them into a gateway configuration 3. Validates compatibility @@ -171,6 +172,7 @@ nitro fusion validate \ ``` Exit codes: + - `0`: Validation passed - Non-zero: Validation failed (breaking change detected) @@ -376,14 +378,17 @@ jobs: ## Workflow Explanation ### Version Job + Generates a timestamp-based tag and version string from the current git commit SHA. ### Build Job + 1. **Restore and publish** — `dotnet publish` produces a container image and pushes to Azure Container Registry 2. **Export schema** — `dotnet run -- schema export` generates the `.graphqls` file 3. **Upload to Nitro** — `nitro fusion upload` versions the schema in Nitro cloud ### Deploy Job + 1. **Create or update App Service** — Deploys the container image to Azure App Service 2. **Publish to Nitro** — `nitro fusion publish` triggers server-side composition and deploys the new configuration to the specified stage 3. **Gateway hot-reloads** — The gateway automatically downloads the new config from Nitro @@ -397,10 +402,10 @@ For simpler pipelines, use the pre-built action: uses: ChilliCream/nitro-fusion-publish-action@v1 with: tag: ${{ github.ref_name }} - stage: 'production' - api-id: 'QXBpCmcwMTk5MGUzNDVlMWU3MjMyYjc2MjYxYzFiNjRkMGQzYg==' + stage: "production" + api-id: "QXBpCmcwMTk5MGUzNDVlMWU3MjMyYjc2MjYxYzFiNjRkMGQzYg==" api-key: ${{ secrets.NITRO_API_KEY }} - source-schema-file: './src/Products/schema.graphqls' + source-schema-file: "./src/Products/schema.graphqls" ``` # Gateway Configuration @@ -468,6 +473,7 @@ For bidirectional subscriptions, configure WebSocket transport. Subscriptions co ## Defaults By default: + - All HTTP requests use the `"fusion"` named HTTP client - Requests are not batched unless explicitly enabled - Subscriptions use SSE if available, otherwise HTTP polling @@ -554,12 +560,14 @@ This lets you develop and test new features without exposing them to clients unt ## Breaking vs. Non-Breaking Changes ### Non-Breaking Changes + - Adding a new type - Adding a new field to an existing type (nullable or with a default) - Adding a new optional argument to a field - Marking a field as `[Shareable]` (allowing multiple subgraphs to resolve it) ### Breaking Changes + - Removing a type or field - Changing a field's return type - Changing a field's arguments diff --git a/website/src/docs/fusion/v16/entities-and-lookups.md b/website/src/docs/fusion/v16/entities-and-lookups.md index c90bb430793..0533a583449 100644 --- a/website/src/docs/fusion/v16/entities-and-lookups.md +++ b/website/src/docs/fusion/v16/entities-and-lookups.md @@ -99,11 +99,13 @@ Internal lookups typically construct a stub object from the key without checking ### When to Use Internal vs. Public Lookups Use a **public lookup** when: + - Your subgraph is the primary owner of the entity (the Products subgraph for `Product`, the Accounts subgraph for `User`) - Clients should be able to query for this entity directly from your subgraph - The lookup validates that the entity exists and returns `null` if it does not Use an **internal lookup** when: + - Your subgraph extends an entity from another subgraph (the Reviews subgraph extending `Product`) - You do not want clients to enter your subgraph through this lookup - The lookup just constructs a stub -- it does not need to validate existence @@ -231,7 +233,7 @@ Both subgraphs mark `GetName` with `[Shareable]`. This tells Fusion: "this field If you forget `[Shareable]` on any definition, composition fails: -``` +```text Error: Field "User.name" is defined in subgraphs "accounts-api" and "reviews-api" without [Shareable]. Mark the field as [Shareable] in all subgraphs that define it, or remove the duplicate definition. @@ -381,22 +383,26 @@ You can have at most one `[NodeResolver]` per entity type per subgraph. If an en Here is a summary of the patterns for the most common entity scenarios: **You own the entity (primary subgraph):** + - Define the full type with all fields - Add a public `[Lookup]` resolver (with `[NodeResolver]` if using Relay) - Use `[DataLoader]` for batch resolution **You extend the entity (secondary subgraph):** + - Create an entity stub with just the key field and your new fields - Add an internal `[Lookup, Internal]` resolver - Use `[BindMember]` if replacing a foreign key with an entity reference - Mark any duplicated non-key fields with `[Shareable]` **You need data from another subgraph in a resolver:** + - Use `[Require(...)]` on the resolver argument to declare the dependency - The gateway fetches the required data automatically - Required arguments are hidden from the composite schema **Your entity can be identified by multiple keys:** + - Add multiple `[Lookup]` resolvers (by ID, by username, by SKU, etc.) - The gateway uses whichever key is available diff --git a/website/src/docs/fusion/v16/getting-started.md b/website/src/docs/fusion/v16/getting-started.md index b2b6756e870..f9a58c35de8 100644 --- a/website/src/docs/fusion/v16/getting-started.md +++ b/website/src/docs/fusion/v16/getting-started.md @@ -34,7 +34,7 @@ This seems reasonable, but it creates a problem: your subgraphs become coupled. **How Fusion actually works:** -> "My Users subgraph knows that each User has a `tenantId`, and I declare that `user.tenant` returns a `Tenant` identified by that ID. I don't need to know *how* or *where* the Tenant gets resolved -- the gateway handles that." +> "My Users subgraph knows that each User has a `tenantId`, and I declare that `user.tenant` returns a `Tenant` identified by that ID. I don't need to know _how_ or _where_ the Tenant gets resolved -- the gateway handles that." In Fusion, your subgraph never calls another subgraph. Instead, it says: "this field returns a Tenant with this ID" and trusts the gateway to figure out the rest. The gateway knows which subgraph can resolve a Tenant by ID (using a **lookup** -- a query field that resolves an entity by its key). The Tenants subgraph provides this lookup resolver, and the gateway calls it when it needs to turn a tenant ID into a full Tenant object. The Users subgraph never talks to the Tenants subgraph directly -- the gateway handles all coordination. @@ -652,7 +652,7 @@ Composition is handled by the Nitro CLI. It reads each subgraph's `.graphqls` sc Before running composition, make sure your project looks like this: -``` +```text fusion-getting-started/ ├── Products/ │ ├── Product.cs @@ -696,7 +696,7 @@ nitro fusion compose --source-schema-file Products/schema.graphqls --source-sche If composition succeeds, you will see output similar to: -``` +```text Validating source schemas... Merging schemas... Fusion archive created: gateway.far @@ -845,7 +845,7 @@ dotnet run Wait for all three services to start. You should see output like this in each terminal: -``` +```text info: Microsoft.Hosting.Lifetime[14] Now listening on: http://localhost:5001 info: Microsoft.Hosting.Lifetime[0] @@ -922,17 +922,13 @@ You should see: "id": 2, "name": "Couch", "price": 1299.5, - "reviews": [ - { "body": "Very comfortable!", "stars": 5 } - ] + "reviews": [{ "body": "Very comfortable!", "stars": 5 }] }, { "id": 3, "name": "Chair", "price": 54, - "reviews": [ - { "body": "Good value for the price.", "stars": 4 } - ] + "reviews": [{ "body": "Good value for the price.", "stars": 4 }] } ] } diff --git a/website/src/docs/fusion/v16/index.md b/website/src/docs/fusion/v16/index.md index 7fbb055e4e9..21e8b478ef1 100644 --- a/website/src/docs/fusion/v16/index.md +++ b/website/src/docs/fusion/v16/index.md @@ -18,7 +18,7 @@ The architecture has three parts: ▼ ┌────────────────┐ │ Fusion Gateway │ - └──┬──────┬──┬──┘ + └──┬──────┬──┬───┘ │ │ │ ┌────────┘ │ └────────┐ ▼ ▼ ▼ @@ -41,6 +41,7 @@ The architecture has three parts: The result: a client sends a single query, the gateway fans it out to the relevant subgraphs, and the response comes back as if it were a monolithic API. + ```graphql # This query touches three subgraphs, but the client doesn't know or care. query { @@ -58,6 +59,7 @@ query { } } ``` + ## Three Things That Make Fusion Different @@ -82,15 +84,15 @@ public static partial class ProductQueries # Key Terminology -| Term | Definition | -|------|-----------| -| **Subgraph** | A HotChocolate server that owns a portion of the overall schema. Each subgraph manages its own data and resolvers. | -| **Source schema** | The GraphQL schema exported by a single subgraph. This is what gets fed into composition. | -| **Composite schema** | The unified, client-facing schema produced by merging all source schemas. Clients query this schema as if it were a single API. | -| **Gateway** | The entry point for client requests. It receives queries against the composite schema, plans execution across subgraphs, and assembles responses. | -| **Entity** | A type that appears in more than one subgraph. For example, both the Products and Reviews subgraphs may define a `Product` type, each contributing different fields. | -| **Lookup** | A Query field annotated with `[Lookup]` that the gateway uses to resolve an entity from a subgraph. It is a standard, callable query field -- not a hidden internal mechanism. | -| **Composition** | The offline process of validating and merging source schemas into a composite schema and gateway configuration. Runs via the Nitro CLI or Aspire, not at runtime. | +| Term | Definition | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Subgraph** | A HotChocolate server that owns a portion of the overall schema. Each subgraph manages its own data and resolvers. | +| **Source schema** | The GraphQL schema exported by a single subgraph. This is what gets fed into composition. | +| **Composite schema** | The unified, client-facing schema produced by merging all source schemas. Clients query this schema as if it were a single API. | +| **Gateway** | The entry point for client requests. It receives queries against the composite schema, plans execution across subgraphs, and assembles responses. | +| **Entity** | A type that appears in more than one subgraph. For example, both the Products and Reviews subgraphs may define a `Product` type, each contributing different fields. | +| **Lookup** | A Query field annotated with `[Lookup]` that the gateway uses to resolve an entity from a subgraph. It is a standard, callable query field -- not a hidden internal mechanism. | +| **Composition** | The offline process of validating and merging source schemas into a composite schema and gateway configuration. Runs via the Nitro CLI or Aspire, not at runtime. | # When to Use Fusion diff --git a/website/src/docs/fusion/v16/migrating-from-schema-stitching.md b/website/src/docs/fusion/v16/migrating-from-schema-stitching.md index 17924cbf9ee..0b397046732 100644 --- a/website/src/docs/fusion/v16/migrating-from-schema-stitching.md +++ b/website/src/docs/fusion/v16/migrating-from-schema-stitching.md @@ -12,17 +12,17 @@ This guide is self-contained. You can complete the migration by following the st If you have worked with Schema Stitching, you already understand the core idea: multiple GraphQL services combined into one schema. Fusion keeps that idea but changes how it works under the hood. Here is how the concepts translate: -| Schema Stitching | Fusion | What Changed | -|---|---|---| -| Remote schemas (`AddRemoteSchema()`) | Subgraphs (source schemas) | Each remote schema becomes its own standalone ASP.NET Core project with HotChocolate. No stitching middleware needed. | -| Stitching gateway (`AddGraphQLServer()` + `AddRemoteSchema()`) | Fusion gateway (`AddGraphQLGateway()`) | The gateway is stateless. No custom resolvers, no delegation logic, no type extensions in the gateway project. | -| Schema extensions (`.graphql` extension files) | Entity stubs with `[ObjectType]` | Instead of writing SDL extension files with `@delegate` directives, you define C# types in the subgraph that extends the entity. | -| Delegating resolvers (`@delegate` directive) | Lookups (`[Lookup]` attribute) | The gateway handles cross-subgraph resolution automatically. You declare a lookup field in each subgraph; composition wires them together. | -| `@delegate(path: "...")` field references | `[Require]` attribute | When a field needs data from another subgraph, you declare the dependency as a method parameter with `[Require]`. | -| Auto-stitching / runtime schema merging | Build-time composition (`nitro fusion compose`) | Schemas are merged offline by the Nitro CLI, producing a static configuration file. Conflicts are caught before deployment. | -| `PublishSchemaDefinition()` + Redis | `schema export` + `nitro fusion upload` | Schema distribution uses the Nitro CLI or .NET Aspire instead of Redis pub/sub. | -| `RenameType()` / `RenameField()` / `IgnoreType()` | Composition rules + `[Internal]` / `@inaccessible` | Type conflicts are resolved by composition rules. Fields you want to hide use `[Internal]` on lookups or `@inaccessible` on types. | -| `SchemaDefinition` / `SchemaExtension` | `schema.graphqls` + `schema-settings.json` | Exported automatically by the subgraph on startup. You do not write these by hand. | +| Schema Stitching | Fusion | What Changed | +| -------------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| Remote schemas (`AddRemoteSchema()`) | Subgraphs (source schemas) | Each remote schema becomes its own standalone ASP.NET Core project with HotChocolate. No stitching middleware needed. | +| Stitching gateway (`AddGraphQLServer()` + `AddRemoteSchema()`) | Fusion gateway (`AddGraphQLGateway()`) | The gateway is stateless. No custom resolvers, no delegation logic, no type extensions in the gateway project. | +| Schema extensions (`.graphql` extension files) | Entity stubs with `[ObjectType]` | Instead of writing SDL extension files with `@delegate` directives, you define C# types in the subgraph that extends the entity. | +| Delegating resolvers (`@delegate` directive) | Lookups (`[Lookup]` attribute) | The gateway handles cross-subgraph resolution automatically. You declare a lookup field in each subgraph; composition wires them together. | +| `@delegate(path: "...")` field references | `[Require]` attribute | When a field needs data from another subgraph, you declare the dependency as a method parameter with `[Require]`. | +| Auto-stitching / runtime schema merging | Build-time composition (`nitro fusion compose`) | Schemas are merged offline by the Nitro CLI, producing a static configuration file. Conflicts are caught before deployment. | +| `PublishSchemaDefinition()` + Redis | `schema export` + `nitro fusion upload` | Schema distribution uses the Nitro CLI or .NET Aspire instead of Redis pub/sub. | +| `RenameType()` / `RenameField()` / `IgnoreType()` | Composition rules + `[Internal]` / `@inaccessible` | Type conflicts are resolved by composition rules. Fields you want to hide use `[Internal]` on lookups or `@inaccessible` on types. | +| `SchemaDefinition` / `SchemaExtension` | `schema.graphqls` + `schema-settings.json` | Exported automatically by the subgraph on startup. You do not write these by hand. | ## What Changes Architecturally @@ -42,9 +42,15 @@ In stitching, it was common to define type extensions and delegating resolvers i # Stitching.graphql (in the gateway project) extend type Product { inStock: Boolean - @delegate(schema: "inventory", path: "inventoryInfo(upc: $fields:upc).isInStock") + @delegate( + schema: "inventory" + path: "inventoryInfo(upc: $fields:upc).isInStock" + ) shippingEstimate: Int - @delegate(schema: "inventory", path: "shippingEstimate(price: $fields:price, weight: $fields:weight)") + @delegate( + schema: "inventory" + path: "shippingEstimate(price: $fields:price, weight: $fields:weight)" + ) } ``` @@ -196,9 +202,15 @@ In stitching, when you want to add fields from one service to a type owned by an # Stitching.graphql extend type Product { inStock: Boolean - @delegate(schema: "inventory", path: "inventoryInfo(upc: $fields:upc).isInStock") + @delegate( + schema: "inventory" + path: "inventoryInfo(upc: $fields:upc).isInStock" + ) shippingEstimate: Int - @delegate(schema: "inventory", path: "shippingEstimate(price: $fields:price, weight: $fields:weight)") + @delegate( + schema: "inventory" + path: "shippingEstimate(price: $fields:price, weight: $fields:weight)" + ) } ``` diff --git a/website/src/docs/fusion/v16/nitro-cli-reference.md b/website/src/docs/fusion/v16/nitro-cli-reference.md index 8339439a212..4077ec50474 100644 --- a/website/src/docs/fusion/v16/nitro-cli-reference.md +++ b/website/src/docs/fusion/v16/nitro-cli-reference.md @@ -39,16 +39,16 @@ nitro fusion compose [options] ### Options -| Option | Description | Default | -|---|---|---| -| `--source-schema-file ` | Path to a source schema `.graphqls` file (can be repeated) | Scans working directory for all `.graphql` and `.graphqls` files | -| `--archive ` | Output path for the Fusion archive | `./gateway.far` | -| `--environment ` | Environment name for variable substitution | `ASPNETCORE_ENVIRONMENT` or `Development` | -| `--enable-global-object-identification` | Enable Relay-style node queries | `false` | -| `--include-satisfiability-paths` | Include satisfiability diagnostic paths | `false` | -| `--watch` | Watch mode: recomposes on file changes | `false` | -| `--working-directory ` | Working directory for resolving relative paths | Current directory | -| `--exclude-tag ` | Exclude fields/types by tag during composition (can be repeated) | None | +| Option | Description | Default | +| --------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------- | +| `--source-schema-file ` | Path to a source schema `.graphqls` file (can be repeated) | Scans working directory for all `.graphql` and `.graphqls` files | +| `--archive ` | Output path for the Fusion archive | `./gateway.far` | +| `--environment ` | Environment name for variable substitution | `ASPNETCORE_ENVIRONMENT` or `Development` | +| `--enable-global-object-identification` | Enable Relay-style node queries | `false` | +| `--include-satisfiability-paths` | Include satisfiability diagnostic paths | `false` | +| `--watch` | Watch mode: recomposes on file changes | `false` | +| `--working-directory ` | Working directory for resolving relative paths | Current directory | +| `--exclude-tag ` | Exclude fields/types by tag during composition (can be repeated) | None | ### Schema File Resolution @@ -108,13 +108,13 @@ nitro fusion upload [options] ### Options -| Option | Description | Required | -|---|---|---| -| `--source-schema-file ` | Path to the source schema file | Yes | -| `--tag ` | Version tag for this upload | Yes | -| `--api-id ` | Nitro API identifier | Yes | -| `--api-key ` | Nitro API key for authentication | Yes | -| `--working-directory ` | Working directory | No (defaults to current directory) | +| Option | Description | Required | +| ----------------------------- | -------------------------------- | ---------------------------------- | +| `--source-schema-file ` | Path to the source schema file | Yes | +| `--tag ` | Version tag for this upload | Yes | +| `--api-id ` | Nitro API identifier | Yes | +| `--api-key ` | Nitro API key for authentication | Yes | +| `--working-directory ` | Working directory | No (defaults to current directory) | ### Examples @@ -161,15 +161,15 @@ nitro fusion publish [options] #### Options -| Option | Description | Required | -|---|---|---| -| `--source-schema-file ` | Path to source schema files (can be repeated) | Yes (for this mode) | -| `--tag ` | Version tag | Yes | -| `--stage ` | Target deployment stage | Yes | -| `--api-id ` | Nitro API identifier | Yes | -| `--api-key ` | Nitro API key | Yes | -| `--environment ` | Environment name for variable substitution | No | -| `--enable-global-object-identification` | Enable Relay-style node queries | No | +| Option | Description | Required | +| --------------------------------------- | --------------------------------------------- | ------------------- | +| `--source-schema-file ` | Path to source schema files (can be repeated) | Yes (for this mode) | +| `--tag ` | Version tag | Yes | +| `--stage ` | Target deployment stage | Yes | +| `--api-id ` | Nitro API identifier | Yes | +| `--api-key ` | Nitro API key | Yes | +| `--environment ` | Environment name for variable substitution | No | +| `--enable-global-object-identification` | Enable Relay-style node queries | No | #### Example @@ -197,13 +197,13 @@ nitro fusion publish [options] #### Options -| Option | Description | Required | -|---|---|---| +| Option | Description | Required | +| -------------------------------- | ------------------------------------------- | ------------------- | | `--source-schema ` | Source schema identifiers (can be repeated) | Yes (for this mode) | -| `--tag ` | Version tag | Yes | -| `--stage ` | Target deployment stage | Yes | -| `--api-id ` | Nitro API identifier | Yes | -| `--api-key ` | Nitro API key | Yes | +| `--tag ` | Version tag | Yes | +| `--stage ` | Target deployment stage | Yes | +| `--api-id ` | Nitro API identifier | Yes | +| `--api-key ` | Nitro API key | Yes | #### Example @@ -243,13 +243,13 @@ nitro fusion publish [options] #### Options -| Option | Description | Required | -|---|---|---| +| Option | Description | Required | +| ------------------ | ------------------------------------- | ------------------- | | `--archive ` | Path to a pre-composed Fusion archive | Yes (for this mode) | -| `--tag ` | Version tag | Yes | -| `--stage ` | Target deployment stage | Yes | -| `--api-id ` | Nitro API identifier | Yes | -| `--api-key ` | Nitro API key | Yes | +| `--tag ` | Version tag | Yes | +| `--stage ` | Target deployment stage | Yes | +| `--api-id ` | Nitro API identifier | Yes | +| `--api-key ` | Nitro API key | Yes | #### Example @@ -337,12 +337,12 @@ nitro fusion download [options] ### Options -| Option | Description | Required | -|---|---|---| -| `--stage ` | Stage to download from | Yes | -| `--api-id ` | Nitro API identifier | Yes | -| `--api-key ` | Nitro API key | Yes | -| `--output ` | Output file path | No (defaults to `gateway.fgp`) | +| Option | Description | Required | +| ----------------- | ---------------------- | ------------------------------ | +| `--stage ` | Stage to download from | Yes | +| `--api-id ` | Nitro API identifier | Yes | +| `--api-key ` | Nitro API key | Yes | +| `--output ` | Output file path | No (defaults to `gateway.fgp`) | ### Examples @@ -373,13 +373,13 @@ nitro fusion validate [options] ### Options -| Option | Description | Required | -|---|---|---| -| `--source-schema-file ` | Path to source schema files (can be repeated) | Yes (for this mode) | -| `--archive ` | Path to a pre-composed archive | Yes (alternative mode) | -| `--stage ` | Stage to validate against | Yes | -| `--api-id ` | Nitro API identifier | Yes | -| `--api-key ` | Nitro API key | Yes | +| Option | Description | Required | +| ----------------------------- | --------------------------------------------- | ---------------------- | +| `--source-schema-file ` | Path to source schema files (can be repeated) | Yes (for this mode) | +| `--archive ` | Path to a pre-composed archive | Yes (alternative mode) | +| `--stage ` | Stage to validate against | Yes | +| `--api-id ` | Nitro API identifier | Yes | +| `--api-key ` | Nitro API key | Yes | ### Examples @@ -434,9 +434,9 @@ nitro fusion run [options] ### Options -| Option | Description | Default | -|---|---|---| -| `--port ` | Port to run the gateway on | `5000` | +| Option | Description | Default | +| ----------------- | -------------------------- | ------- | +| `--port ` | Port to run the gateway on | `5000` | ### Examples @@ -471,8 +471,8 @@ nitro fusion settings set [options] ### Options -| Option | Description | Default | -|---|---|---| +| Option | Description | Default | +| ------------------ | ------------------------------------ | --------------- | | `--archive ` | Path to the Fusion archive to modify | `./gateway.far` | ### Available Settings @@ -542,8 +542,8 @@ dotnet run -- schema export [options] ### Options -| Option | Description | Default | -|---|---|---| +| Option | Description | Default | +| ----------------- | ---------------- | --------------------------------- | | `--output ` | Output file path | `schema.graphqls` in project root | ### Examples @@ -690,11 +690,11 @@ When composing with `--environment dev`, `{{API_URL}}` resolves to the dev URL. The Nitro CLI respects these environment variables: -| Variable | Purpose | -|---|---| -| `NITRO_API_KEY` | Default API key (overrides `--api-key`) | -| `NITRO_API_ID` | Default API ID (overrides `--api-id`) | -| `NITRO_STAGE` | Default stage (overrides `--stage`) | +| Variable | Purpose | +| ------------------------ | --------------------------------------- | +| `NITRO_API_KEY` | Default API key (overrides `--api-key`) | +| `NITRO_API_ID` | Default API ID (overrides `--api-id`) | +| `NITRO_STAGE` | Default stage (overrides `--stage`) | | `ASPNETCORE_ENVIRONMENT` | Default environment for `--environment` | Set these in CI/CD to avoid passing credentials as command-line arguments.