diff --git a/website/src/docs/docs.json b/website/src/docs/docs.json index 7f3975f1221..74d06f61a25 100644 --- a/website/src/docs/docs.json +++ b/website/src/docs/docs.json @@ -148,6 +148,45 @@ } ] }, + { + "path": "fusion", + "title": "Fusion", + "description": "Federated GraphQL Gateway", + "metaDescription": "Fusion is a powerful, open-source GraphQL gateway that helps developers to build a unified API for their services.", + "latestStableVersion": "v14", + "versions": [ + { + "path": "v14", + "title": "v14", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "quick-start", + "title": "Quick Start" + }, + { + "path": "entities", + "title": "Entities" + }, + { + "path": "lookups", + "title": "Lookups" + }, + { + "path": "devops", + "title": "DevOps" + }, + { + "path": "cli", + "title": "CLI" + } + ] + } + ] + }, { "path": "hotchocolate", "title": "Hot Chocolate", @@ -381,16 +420,6 @@ } ] }, - { - "path": "fusion", - "title": "Fusion", - "items": [ - { - "path": "index", - "title": "Overview" - } - ] - }, { "path": "performance", "title": "Performance", @@ -719,16 +748,6 @@ } ] }, - { - "path": "fusion", - "title": "Fusion", - "items": [ - { - "path": "index", - "title": "Overview" - } - ] - }, { "path": "performance", "title": "Performance", diff --git a/website/src/docs/fusion/v14/assets/fusion-overview-1.png b/website/src/docs/fusion/v14/assets/fusion-overview-1.png new file mode 100644 index 00000000000..de6fde40720 Binary files /dev/null and b/website/src/docs/fusion/v14/assets/fusion-overview-1.png differ diff --git a/website/src/docs/fusion/v14/assets/nitro-0.webp b/website/src/docs/fusion/v14/assets/nitro-0.webp new file mode 100644 index 00000000000..d359a4d5194 Binary files /dev/null and b/website/src/docs/fusion/v14/assets/nitro-0.webp differ diff --git a/website/src/docs/fusion/v14/assets/quick-start-1.png b/website/src/docs/fusion/v14/assets/quick-start-1.png new file mode 100644 index 00000000000..2d1aa341435 Binary files /dev/null and b/website/src/docs/fusion/v14/assets/quick-start-1.png differ diff --git a/website/src/docs/fusion/v14/assets/quick-start-2.png b/website/src/docs/fusion/v14/assets/quick-start-2.png new file mode 100644 index 00000000000..69aa0174053 Binary files /dev/null and b/website/src/docs/fusion/v14/assets/quick-start-2.png differ diff --git a/website/src/docs/fusion/v14/assets/quick-start-3.png b/website/src/docs/fusion/v14/assets/quick-start-3.png new file mode 100644 index 00000000000..c9366bc2ec8 Binary files /dev/null and b/website/src/docs/fusion/v14/assets/quick-start-3.png differ diff --git a/website/src/docs/fusion/v14/assets/telemetry-0.webp b/website/src/docs/fusion/v14/assets/telemetry-0.webp new file mode 100644 index 00000000000..df153e101cc Binary files /dev/null and b/website/src/docs/fusion/v14/assets/telemetry-0.webp differ diff --git a/website/src/docs/fusion/v14/cli.md b/website/src/docs/fusion/v14/cli.md new file mode 100644 index 00000000000..fa236827f35 --- /dev/null +++ b/website/src/docs/fusion/v14/cli.md @@ -0,0 +1,136 @@ +--- +title: "Fusion CLI Documentation" +--- + +The Fusion Command Line Interface is a tool designed to assist the development and management of APIs using Fusion. This documentation provides a comprehensive guide to the Fusion CLI, covering installation, commands, options, and usage examples to help you effectively utilize the tool in your projects. + +## Introduction + +Fusion CLI is a command-line tool that assists developers in composing, packaging, and managing federated schemas for Fusion gateways. + +## Installation + +To install the Fusion CLI, ensure you have the .NET SDK installed on your machine. Then, install the CLI globally using the .NET tool command: + +```bash +dotnet tool install -g HotChocolate.Fusion.CommandLine +``` + +This command installs the Fusion CLI globally, making it accessible from any directory in your command line. + +## Commands + +### `fusion compose` + +The `compose` command is used to create or update a Fusion gateway package (`.fgp` file) by composing multiple APIs packages (`.fsp` files). This package is then used by the Fusion gateway to serve a unified schema. + +**Description:** + +Composes multiple APIs into a Fusion gateway package. + +**Usage:** + +```bash +fusion compose [options] +``` + +**Options:** + +- `-p`, `--package`, `--package-file ` (REQUIRED): Specifies the path to the Fusion gateway package file (`gateway.fgp`). This file will be created or updated by the compose command. +- `-s`, `--subgraph`, `--subgraph-package-file `: Specifies the path to a subgraph package file (`.fsp`) to include in the composition. This option can be used multiple times to add multiple subgraphs. +- `--package-settings`, `--package-settings-file`, `--settings `: Specifies the path to a Fusion package settings file (`fusion-subgraph.json`). This file contains additional settings for the composition. +- `-w`, `--working-directory `: Sets the working directory for the command. Defaults to the current executing directory. +- `--enable-nodes`: Enables the Node interface feature in the gateway, allowing it to understand `node(id: ...)` queries. +- `-r`, `--remove `: Removes a specified subgraph from the existing composition in the gateway package. +- `-?`, `-h`, `--help`: Shows help and usage information for the command. + +--- + +### `fusion subgraph` + +The `subgraph` command group contains commands related to subgraph management, such as packaging subgraphs for composition. + +#### `fusion subgraph pack` + +The `pack` command creates a Fusion subgraph package (`.fsp` file) from a subgraph's schema and configuration. This package is then used in the composition process. + +**Description:** + +Creates a Fusion subgraph package from a subgraph's schema and configuration files. + +**Usage:** + +```bash +fusion subgraph pack [options] +``` + +**Options:** + +- `-p`, `--package`, `--package-file `: Specifies the output path for the subgraph package file (`YourService.fsp`). Defaults to `.fsp` if not specified. +- `-s`, `--schema`, `--schema-file `: Specifies the path to the subgraph's schema file (`schema.graphql`). +- `-c`, `--config`, `--config-file `: Specifies the path to the subgraph's configuration file (`subgraph-config.json`). +- `-e`, `--extension`, `--extension-file `: Specifies paths to any schema extension files to include. This option can be used multiple times for multiple files. +- `-w`, `--working-directory `: Sets the working directory for the command. Defaults to the current executing directory. +- `-?`, `-h`, `--help`: Shows help and usage information for the command. + +## Examples + +This section provides practical examples of using the Fusion CLI commands in common scenarios. + +### Example 1: Packing a Downstream Service + +Suppose you have a subgraph named `Products` with a schema file `schema.graphql` and a configuration file `subgraph-config.json`. To create a subgraph package, navigate to the subgraph's directory and run: + +```bash +fusion subgraph pack +``` + +In case your schema and configuration files have different names or are located in a different directory, you can specify them using the `-s` and `-c` options: + +```bash +fusion subgraph pack -s other-schema.graphql -c config.json +``` + +This command generates a `Products.fsp` package file in the current directory. + +### Example 2: Composing a Gateway Package + +To compose a Fusion gateway package from multiple subgraph packages, use the `compose` command. For example, to compose the `Products` and `Orders` subgraphs into a gateway package named `gateway.fgp`, run: + +```bash +fusion compose -p gateway.fgp -s ../Products/Products.fsp -s ../Orders/Orders.fsp +``` + +This command creates or updates the `gateway.fgp` file with the composed schema from both subgraphs. + +### Example 3: Removing a Downstream Service from the Composition + +If you need to remove the `Orders` subgraph from an existing gateway package, use the `--remove` option: + +```bash +fusion compose -p gateway.fgp -r Orders +``` + +This command updates `gateway.fgp`, removing the `Orders` subgraph from the composition. + +### Example 4: Enabling Node Interface Support + +To enable the Node interface feature in your gateway, allowing it to handle `node` queries, include the `--enable-nodes` flag during composition: + +```bash +fusion compose -p gateway.fgp -s ../Products/Products.fsp --enable-nodes +``` + +### Example 5: Specifying Working Directory + +If your schema and configuration files are located in a different directory, you can specify the working directory using `-w`: + +```bash +fusion subgraph pack -s schema.graphql -c subgraph-config.json -w /path/to/subgraph +``` + +## Additional Resources + +- **Fusion Documentation:** Explore the [official Fusion documentation](/docs/fusion/v14) for in-depth guides and references. +- **Fusion Quick Start Guide:** Get started with Fusion by following the [Quick Start Guide](/docs/fusion/v14/quick-start). +- **ChilliCream Community:** Join the [ChilliCream community](https://slack.chillicream.com) to ask questions, share experiences, and contribute to the project. diff --git a/website/src/docs/fusion/v14/devops.md b/website/src/docs/fusion/v14/devops.md new file mode 100644 index 00000000000..916953c5a21 --- /dev/null +++ b/website/src/docs/fusion/v14/devops.md @@ -0,0 +1,302 @@ +--- +title: "DevOps Integration with Nitro" +--- + +Fusion, can be seamlessly integrated into your DevOps processes using **Nitro**. Nitro acts as an orchestrator for your Fusion gateway, deeply integrating with your development workflow. It enables you to publish, validate, consume, and monitor your Fusion gateway efficiently. + +This section provides a comprehensive guide on how to integrate Fusion with Nitro into your DevOps pipeline, covering gateway and subgraph configuration, CI/CD integration, monitoring, and caching. + +# Overview of Nitro and Fusion + +**Nitro** enhances your Fusion gateway by: + +- Automating configuration updates. +- Validating schema changes against client applications. +- Providing a dashboard for monitoring your gateway and subgraphs. +- Enabling distributed telemetry for performance insights. +- Offering caching mechanisms for improved resilience. + +By integrating Nitro with Fusion, you can streamline the management and deployment of your federated GraphQL services. + +# Setting Up the Fusion Gateway with Nitro + +## 1. Install the Nitro Package + +To integrate your Fusion gateway with Nitro, install the `ChilliCream.Nitro` package: + +```bash +dotnet add package ChilliCream.Nitro +``` + +## 2. Configure the Gateway Services + +In your gateway's startup configuration, set up the services to connect with Nitro: + +```csharp +builder.Services + .AddFusionGatewayServer() + .ConfigureFromCloud(options => + { + options.ApiKey = "<>"; + options.ApiId = "<>"; + options.Stage = "dev"; // Replace with your stage + }); +``` + +### Tip: Using Environment Variables + +- `NITRO_API_KEY` for `ApiKey` +- `NITRO_API_ID` for `ApiId` +- `NITRO_STAGE` for `Stage` + +Then, configure the gateway without explicit parameters: + +```csharp +builder.Services + .AddFusionGatewayServer() + .ConfigureFromCloud(); +``` + +## 3. Enable Telemetry and Instrumentation + +To monitor your gateway, enable instrumentation and configure telemetry export to Nitro: + +```csharp +builder.Services + .AddFusionGatewayServer() + .ConfigureFromCloud() + .CoreBuilder + .AddInstrumentation(); + +builder.Services + .AddOpenTelemetry() + .WithTracing(builder => + { + builder.AddHttpClientInstrumentation(); + builder.AddAspNetCoreInstrumentation(); + builder.AddNitroExporter(); + // Add additional instrumentation as needed + }); +``` + +This setup allows your gateway to send telemetry data to Nitro for monitoring and analysis. + +# Configuring Your Subgraphs with Nitro + +To integrate your subgraphs with Nitro and the Fusion gateway, follow these steps: + +## 1. Install the Nitro Package in Subgraphs + +In each subgraph project, install the `ChilliCream.Nitro` package: + +```bash +dotnet add package ChilliCream.Nitro +``` + +## 2. Configure Services in Subgraphs + +In the startup configuration of each subgraph, set up the Nitro services and enable instrumentation: + +```csharp +services + .AddGraphQLServer() + .AddQueryType() + .AddNitro(options => + { + options.ApiKey = "<>"; + options.ApiId = "<>"; + options.Stage = "dev"; // Replace with your stage + }) + .AddInstrumentation(); // Enable GraphQL telemetry + +services + .AddOpenTelemetry() + .WithTracing(builder => + { + builder.AddHttpClientInstrumentation(); + builder.AddAspNetCoreInstrumentation(); + builder.AddNitroExporter(); + // Add additional instrumentation as needed + }); +``` + +## 3. Create Subgraph Configuration File + +Each subgraph requires a `subgraph-config.json` file in the project's root directory: + +```json +{ + "subgraph": "Order", // Name of your subgraph + "http": { + "baseAddress": "http://localhost:5000/graphql" // Update as necessary + }, + "extensions": { + "nitro": { + "apiId": "<>" + } + } +} +``` + +This file is essential for the Fusion gateway to recognize and connect to your subgraphs correctly. + +# Integrating Nitro and Fusion into Your CI/CD Pipeline + +Automate the deployment of your Fusion gateway and subgraphs by integrating Nitro into your CI/CD pipeline. + +## 1. Install Nitro CLI and Fusion Command Line Tools + +In your CI/CD environment, install the necessary command-line tools: + +```bash +dotnet new tool-manifest +dotnet tool install ChilliCream.Nitro.CLI +dotnet tool install HotChocolate.Fusion.CommandLine +``` + +## 2. Pack the Subgraph + +Before deployment, pack your subgraph to create a package containing the schema, extensions, and configuration: + +```bash +dotnet run -- schema export --output schema.graphql +dotnet fusion subgraph pack +``` + +This step is typically part of your build process in the CI/CD pipeline. + +## 3. Coordinate Deployment Slots with Nitro + +To manage concurrent deployments and avoid conflicts, use Nitro to coordinate deployment slots: + +```bash +dotnet nitro fusion-configuration publish begin \ + --stage <> \ + --tag <> \ + --api-id <> \ + --subgraph-name <> \ + --api-key <> +``` + +This command registers your intent to deploy and waits until it's your turn. + +## 4. Start the Deployment + +Once you have a deployment slot, confirm your deployment: + +```bash +dotnet nitro fusion-configuration publish start --api-key <> +``` + +## 5. Configure the Subgraph URL + +Set the environment-specific URL for your subgraph: + +```bash +dotnet fusion subgraph config set http \ + --url <> \ + -c path/to/your/subgraph/config.fsp +``` + +## 6. Compose the Gateway Configuration + +Fetch the latest gateway configuration and compose it with your subgraph: + +```bash +dotnet nitro fusion-configuration download \ + --api-id <> \ + --stage <> \ + --output-file ./gateway.fgp \ + --api-key <> + +dotnet fusion compose -p ./gateway.fgp -s path/to/your/subgraph/config.fsp +``` + +## 7. Validate the Configuration (Optional) + +Ensure your changes won't break existing clients or introduce conflicts: + +```bash +dotnet nitro fusion-configuration publish validate \ + --configuration ./gateway.fgp \ + --api-key <> +``` + +If validation fails, cancel the deployment: + +```bash +dotnet nitro fusion-configuration publish cancel --api-key <> +``` + +## 8. Deploy the Subgraph + +Deploy your subgraph to your infrastructure using your standard deployment tools. + +## 9. Commit the Deployment + +After successful deployment, commit the configuration to notify Nitro and update the Fusion gateway: + +```bash +dotnet nitro fusion-configuration publish commit \ + --configuration ./gateway.fgp \ + --api-key <> +``` + +This finalizes the deployment and allows the gateway to pull the latest configuration. + +--- + +# Monitoring and Telemetry with Nitro + +Nitro provides monitoring and telemetry capabilities for your Fusion gateway and subgraphs. + +![Nitro Telemetry](/assets/telemetry-0.webp) + +Checkout the [Nitro Distributed Telemetry](/docs/nitro/apis/fusion#distributed-telemetry) documentation for more details. + +--- + +# Implementing Caching with Nitro + +Nitro offers caching mechanisms of persisted operations and configurations to improve system performance and reduce dependencies on real-time server communications. **Using the cache is considered a best practice.** + +## File System Cache + +The default caching mechanism uses the file system. + +```csharp +services + .AddGraphQLServer() + .AddFileSystemAssetCache(options => + { + options.CacheDirectory = "cache"; // Specify your cache directory + }); +``` + +## Azure Blob Storage Cache + +For distributed caching across multiple servers, use Azure Blob Storage. + +Install the `ChilliCream.Nitro.Azure` package: + +```bash +dotnet add package ChilliCream.Nitro.Azure +``` + +```csharp +services + .AddGraphQLServer() + .AddBlobStorageAssetCache(options => + { + options.ContainerName = "your-container-name"; + options.Client = new BlobServiceClient( + new Uri("https://yourblobstorage.blob.core.windows.net/"), + new DefaultAzureCredential()); + }); +``` + +## Custom Caching Implementations + +Implement the `IAssetCache` interface for custom caching strategies tailored to your specific needs. + +[Learn more about caching with Nitro](/docs/nitro/apis/fusion). diff --git a/website/src/docs/fusion/v14/entities.md b/website/src/docs/fusion/v14/entities.md new file mode 100644 index 00000000000..33b98d99ac3 --- /dev/null +++ b/website/src/docs/fusion/v14/entities.md @@ -0,0 +1,105 @@ +--- +title: "Introduction to Entities" +--- + +In Fusion, entities are a fundamental concept that enable multiple services to collaboratively define and resolve fields for shared object types. This guide explains how entities work in Fusion and how you can use them to build a unified GraphQL API from multiple services. + +# Entity Overview + +In a federated schema managed by Fusion, an entity is an object type who is identified by a unique key and therefore whose fields can be resolved across multiple services. +Each service can contribute different fields to the entity and is responsible for resolving only the fields it defines. +This approach adheres to the separation of concerns principle, allowing teams to work independently while contributing to a cohesive API. + +For example, consider a `Product` entity whose fields are defined and resolved across two services: + +- **Products:** Defines core product information like `id`, `name`, and `price`. +- **Reviews:** Adds fields like `rating` and `reviews` to the `Product` entity. + +# Defining Entities in Fusion + +## Implicitly Shareable Types + +In Fusion, all object types are sharable by default. This means you do not need to annotate types or fields to make them available across services. Any type defined in one service can be referenced and extended in another service without additional directives or annotations. + +Field overlap between services is resolved automatically by the Fusion gateway and can be used to optimize data fetching and reduce network round trips. + +## Implicit Keys via Lookups + +Unlike other federated systems, Fusion does not require you to explicitly define keys for your entities. Instead, keys are defined implicitly through lookups. A lookup is any field or operation that can uniquely identify an instance of a type based on certain arguments. + +For example, if a service defines a field `productById(id: ID!): Product`, Fusion understands that `Product` instances can be identified by the `id` field. If another field `productBySku(sku: String!): Product` exists, Fusion knows that `Product` can also be identified by the `sku` field. These lookups inform the gateway how to fetch and resolve entities across services. + +## Using Entities Across Services + +When building a federated schema with Fusion, entities allow different services to collaborate on shared types. Here's how you can use entities in your schema: + +1. **Define the Entity in One Service** + + In the product service, define the entity with its core fields: + + ```graphql + # Products + type Product { + id: ID! + name: String! + description: String + price: Float! + } + + type Query { + productById(id: ID!): Product + } + ``` + +2. **Reference and Extend the Entity in Another Service** + + In another service, reference the entity and add additional fields: + + ```graphql + # Reviews Service + type Product { + id: ID! + # Additional fields can be added here + rating: Float + reviews: [Review!]! + } + + type Review { + id: ID! + content: String! + author: User! + } + + type Query { + productById(id: ID!): Product @lookup + } + ``` + + In this example, the `Reviews` service references the `Product` type and adds fields like `rating` and `reviews`. + +3. **Use Lookups to Resolve Entities** + + Fusion uses lookups defined in the services to resolve entities across services. When a client queries for fields that span multiple services, the gateway orchestrates the request using the available lookups. + +## Querying Across Services + +Clients can now query for product information and reviews in a single request: + +```graphql +query { + productById(id: "123") { + id + name + price + rating + reviews { + content + author { + name + } + } + } +} +``` + +The Fusion gateway handles the orchestration between subgraphs, resolving the `Product` entity across both services using the `id` field. diff --git a/website/src/docs/fusion/v14/index.md b/website/src/docs/fusion/v14/index.md new file mode 100644 index 00000000000..b6f205ebd4f --- /dev/null +++ b/website/src/docs/fusion/v14/index.md @@ -0,0 +1,102 @@ +--- +title: "Introduction to Fusion" +--- + +![Fusion Logo](./assets/nitro-0.webp) + +Fusion combines different source schemas into a single unified schema. This allows multiple GraphQL services to work together seamlessly, providing a unified API experience while preserving the independence of each service. + +At its core, Fusion adopts a distributed architecture that combines multiple GraphQL services, into a single, cohesive API. This architecture allows each source to own its domain-specific schema and logic, keeping autonomy and independent development across different teams. + +In an era where microservices and distributed systems are the norm, managing and scaling APIs efficiently becomes a significant challenge. Fusion addresses these challenges by allowing teams to develop, deploy, and scale their GraphQL services independently while still contributing to a cohesive and unified API. + +Fusion introduces a gateway component that acts as the single entry point for client requests. When a client queries the gateway, Fusion intelligently routes parts of the query to the appropriate domain services, aggregates the results, and returns a unified response. This process is transparent to the client, which interacts with the supergraph as if it were a monolithic GraphQL API. + +![Fusion Overview](./assets/fusion-overview-1.png) + +# Benefits of Using Fusion + +## Autonomous Team Development + +Fusion makes it easy for teams to work independently on their own services while still contributing to a unified schema. By clearly separating schemas, Fusion allows each team to develop, test, and deploy their services without being tightly connected to what other teams are doing. This reduces the need for constant coordination and makes it easier for teams to focus on their own goals. + +Schema boundaries act like agreements between teams, ensuring that everything works together without requiring teams to be tightly linked. This approach speeds up development, simplifies teamwork, and helps teams deliver their services without unnecessary delays. + +## Scalability and Modularity + +Fusion is designed to support the modularity of your domain. By enabling the composition of different bounded contexts into a unified schema, Fusion allows each service to reflect its specific domain while remaining independently developed, deployed, and scaled. This modular approach ensures that teams can evolve their services to meet unique performance and domain needs without disrupting the broader system. + +## Unified API Experience + +By consolidating multiple GraphQL APIs into a single endpoint, Fusion allows clients to fetch all the required data with a single request. This eliminates the need for clients to manage multiple endpoints or coordinate several requests, leading to improved performance and a simplified client-side codebase. + +Clients interact with a single, unified GraphQL API, simplifying the development of frontend applications. Fusion handles the complexity of distributed systems behind the scenes, providing a seamless API experience. + +## Alignment with Industry Standards + +Fusion is a key contributor to the Composite Schema Specification, an open standard under the GraphQL Foundation that aims to standardize the composition and execution of distributed GraphQL services. Backed by industry leaders, this specification seeks to create a unified approach for federated GraphQL schemas. + +Although the specification is still in development, Fusion actively aligns with its evolving guidelines and is committed to full compliance once finalized. This ensures that Fusion remains interoperable, up-to-date with industry best practices, and at the forefront of GraphQL innovation. + +# When to Use Fusion + +Fusion is a powerful choice for organizations looking to streamline and enhance their GraphQL architecture. Here are some scenarios where Fusion shines: + +- **Operate Multiple Domains**: If your application spans multiple domains, each managed by separate teams or departments, Fusion simplifies integration by unifying these services under a single GraphQL schema. This allows teams to focus on their specific domain without worrying about breaking the overall API. + +- **Embrace Microservices**: Fusion is an excellent fit for organizations adopting a microservices architecture. It enables each service to be independently developed, deployed, and maintained, while still offering a seamless, unified API for clients. This reduces complexity and promotes clear service boundaries. + +- **Require Scalability**: Applications often have components with different scaling needs. Fusion lets you scale individual services independently based on their specific load requirements, avoiding the overhead of scaling the entire system unnecessarily. + +- **Seek Team Autonomy**: For organizations with large or decentralized teams, Fusion empowers each team to work autonomously on their own services. Teams can adopt their preferred technologies, workflows, and development schedules while Fusion ensures everything integrates smoothly. This minimizes interdependencies, speeds up development, and reduces coordination challenges. + +- **Simplify API Maintenance**: Fusion helps reduce the operational overhead of managing multiple APIs by consolidating them into a single, cohesive GraphQL schema. This makes it easier to track changes, troubleshoot issues, and evolve the API as business requirements grow. + +Whether you're looking to unify a complex domain, scale individual services, or enable independent team workflows, Fusion provides the tools to achieve these goals effectively. + +# Composition + +Composition in Fusion refer to the process of combining multiple GraphQL schemas from different services into a single, unified schema. This involves merging types, resolving conflicts, and establishing relationships between types defined in different services. + +This process occurs at build time rather than runtime. If there are any issues with the composition, Fusion provides feedback during the build phase, allowing you to identify and resolve conflicts or inconsistencies before deploying the composed schema. + +Consider an e-commerce platform with separate services for users, products, and orders. Each service defines its own GraphQL schema: + +- **Users Service**: Defines the User type with fields like id, name, and orders. +- **Products Service**: Defines the Product type with fields like id, title, price, and inStock. +- **Orders Service**: Defines the Order type and references the Product type. + +During composition, Fusion merges these schemas into a unified schema that clients can query seamlessly. The gateway understands how to fetch and assemble data across these services, making cross-service relationships transparent to the client. + +A client might execute a query like: + +```graphql +query { + user(id: "123") { + name + orders { + id + products { + title + price + } + } + } +} +``` + +The Fusion gateway routes parts of this query to the appropriate services, collects the responses, and assembles them into a single, cohesive result. + +# Thinking in Fusion + +Fusion introduces a new way of GraphQL API development by emphasizing entities and their relationships across different services. + +Entities are central to Fusion’s approach. They are types that can be extended across multiple services, identified by a unique key, and can be referenced and resolved by different services. This allows services to extend types defined elsewhere, adding fields or resolvers specific to their domain. + +For example, the Orders service might define an Order type and reference a Product type defined in the Products service. Through entities, the gateway understands how to fetch and assemble data for Product when included in an Order. + +# Relationship with Schema Stitching + +Schema stitching was an early method of combining multiple GraphQL schemas, but it has some significant limitations. Changes to a service often require corresponding updates to the central gateway, increasing maintenance overhead. Additionally, schema stitching is resolver-based rather than entity-based. Without the concept of entities, schema stitching cannot optimize data fetching effectively, often leading to less efficient query execution and increased complexity. + +Fusion addresses these limitations by introducing automated schema composition and leveraging an entity-based approach. This enables optimized data fetching and better handling of relationships between types across services. diff --git a/website/src/docs/fusion/v14/lookups.md b/website/src/docs/fusion/v14/lookups.md new file mode 100644 index 00000000000..a16e09c6c0b --- /dev/null +++ b/website/src/docs/fusion/v14/lookups.md @@ -0,0 +1,96 @@ +--- +title: "Lookups" +--- + +Lookups are essential for resolving entities in Fusion. They inform the gateway how to obtain an instance of a type based on certain identifiers. + +# Defining Lookups + +A lookup is defined by any field or operation that returns an entity and takes arguments that can uniquely identify it. For example: + +```graphql +# Products Subgraph +type Query { + productById(id: ID!): Product @lookup + productBySku(sku: String!): Product @lookup +} +``` + +In this case, `productById` and `productBySku` are lookups for the `Product` entity, using `id` and `sku` as keys, respectively. + +## How Lookups Work + +When the gateway needs to resolve an entity, it uses the available lookups to fetch the required data. The process is as follows: + +1. **Identify the Required Entity Fields** + + The gateway determines which fields of the entity are requested by the client and which subgraphs can provide them. + +2. **Select the Appropriate Lookup** + + Based on the available lookups and the data at hand, the gateway chooses the most suitable lookup to retrieve the entity. + +3. **Fetch Data Across Subgraphs** + + The gateway orchestrates calls to the relevant subgraphs, using the lookups to fetch and assemble the entity's data. + +# Implicit vs. Explicit Lookups + +By default in Fusion, lookups are implicit, meaning you don't need to explicitly annotate fields as lookups. Instead, the gateway infers lookups based on the arguments of root fields. + +It's good practice to explicitly mark fields as lookups using the `@lookup` directive to make your schema more readable and maintainable. In the future, explicitly defining lookups will be the standard. + +# Internal Lookups + +By default all lookups are public, meaning they can not only be used to resolve additional fields by the gateway, but also used as entry points in a query. Sometimes this is not what you want. + +Let say you have a Product and a Review service. The Review service has a lookup `productById` to resolve the reviews for a product. If a product does not have any reviews, this subgraph does not know this product. If we execute the query `productById(id: 1) {review {id}}` the gateway will plan the request in the most efficient way and will call the Review service with the lookup `productById` with the argument `id: 1`. The Review service will not find any reviews and will return null. This is not what we want. + +To prevent this, you can mark the lookup as internal. This means that the lookup can only be used by the gateway to resolve additional fields, but not as an entry point in a query. To mark a lookup as internal, you can use the `@internal` directive. + +```graphql +# Reviews Subgraph +type Query { + productById(id: ID!): Product @lookup @internal +} +``` + +To annotate lookups in your source code you need the `HotChocolate.Fusion.SourceSchema` NuGet package. This package provides you with the `[Lookup]` and `[Internal]` attributes to mark your lookups as internal. + +```csharp +public static class ProductOperations +{ + [Query] + [Lookup] + [Internal] + public static Product GetProductById(int id) + { + return new Product + { + Id = id, + Name = $"Product {id}", + Sku = $"SKU{id}", + Description = $"Description {id}", + Price = id + }; + } +} +``` + +# The `@is` Directive + +The `@is` directive is used on lookup fields to define how arguments map to the fields of the entity type that the lookup field resolves. This mapping creates semantic equivalence between different members of the type system across source schemas, particularly in cases where arguments do not directly align with the fields on the entity type. + +In the example below, the `@is` directive specifies that the `id` argument on the field `Query.personById` is semantically equivalent to the `id` field on the `Person` type returned by the field: + +```graphql +extend type Query { + personById(id: ID! @is(field: "id")): Person @lookup +} +``` + +Here, the `@is` directive maps the `id` argument of `Query.personById` to the `id` field on the `Person` type. This ensures that the lookup resolves correctly by establishing the equivalence. + +## Optional Use of `@is` + +In cases where the argument name and the field name match (as in this example), the `@is` directive can be omitted, as the mapping is implied. However, the directive is essential when the argument name does not directly correspond to a field on the entity type. diff --git a/website/src/docs/fusion/v14/quick-start.md b/website/src/docs/fusion/v14/quick-start.md new file mode 100644 index 00000000000..ce300282e1d --- /dev/null +++ b/website/src/docs/fusion/v14/quick-start.md @@ -0,0 +1,382 @@ +--- +title: "Quick Start Guide to Fusion" +--- + +All example code can be found in the [Fusion Quick Start repository](https://github.com/ChilliCream/hotchocolate-examples/tree/master/fusion) + +# Introduction + +This quick start guide will walk you through setting up a federated GraphQL API using Fusion. By the end of this tutorial, you'll have a gateway that unifies two independent GraphQL services—`Products` and `Orders`—into a single, cohesive API. This unified API will allow clients to query data from both services seamlessly, demonstrating the power and flexibility of Fusion in orchestrating microservices. + +You can find the complete code for this tutorial in the [Fusion Quick Start repository](https://github.com/ChilliCream/hotchocolate-examples/tree/master/fusion/complete/quick-start) + +## Prerequisites + +- **.NET SDK** installed on your machine +- [Checkout the initial code](https://github.com/ChilliCream/hotchocolate-examples/tree/master/fusion/initial/quick-start) + +# Step 1: Understanding the Source Schemas + +We start with two separate GraphQL services, each with its own schema and responsibilities: + +## Orders Service + +The `Orders` service manages order-related data. Its schema defines two primary types: `Order` and `LineItem`. + +```graphql +type LineItem { + id: Int! + quantity: Int! + productId: Int! +} + +type Order { + id: Int! + name: String! + description: String! + items: [LineItem!]! +} + +type Query { + orders: [Order!]! @cost(weight: "10") +} +``` + +- **LineItem** represents an individual item within an order, including the `productId` that references a product. +- **Order** encapsulates order details and contains a list of `LineItem` entries. +- The root `Query` allows clients to fetch all orders. + +## Products Service + +The `Products` service handles product-related data. Its schema defines the `Product` type. + +```graphql +type Product { + id: Int! + name: String! + sku: String! + description: String! + price: Decimal! +} + +type Query { + products: [Product!]! @cost(weight: "10") +} +``` + +- **Product** represents an item available for purchase. +- The root `Query` enables clients to fetch all products. + +These two services are completely independent and unaware of each other's existence. Our goal is to use Fusion to create a gateway that unifies these schemas into a single API. + +# Step 2: Setting Up the Fusion Gateway + +The Fusion gateway will act as a single entry point for clients, hosting the unified schema that combines the `Products` and `Orders` services. + +## Creating the Gateway Project + +Begin by creating a new web project for the gateway: + +```bash +dotnet new web -n quick-start.Gateway +cd quick-start.Gateway +``` + +Add the Fusion package to your project: + +```bash +dotnet add package HotChocolate.Fusion +``` + +## Configuring the Gateway + +Update the `Program.cs` file to set up the Fusion gateway: + +```csharp +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddHttpClient("Fusion"); + +builder.Services + .AddFusionGatewayServer() + .ConfigureFromFile("gateway.fgp") + // Note: AllowQueryPlan is enabled for demonstration purposes. Disable in production environments. + .ModifyFusionOptions(x => x.AllowQueryPlan = true); + +var app = builder.Build(); + +app.MapGraphQL(); + +app.Run(); +``` + +In this configuration: + +- `AddFusionGatewayServer()` registers the Fusion gateway services. +- `ConfigureFromFile("gateway.fgp")` instructs the gateway to load its configuration from the `gateway.fgp` file, which we will create later. +- `ModifyFusionOptions()` allows you to modify Fusion-specific settings, such as enabling query plan visualization for debugging purposes. + +# Step 3: Preparing the Source Schemas for Composition + +Before the gateway can unify the schemas, we need to prepare each service by creating configuration files and packaging them as Fusion subgraph packages. + +## Installing the Fusion CLI Tool + +Install the Fusion command-line tool, which we'll use to pack and compose the subgraphs: + +```bash +dotnet tool install -g HotChocolate.Fusion.CommandLine +``` + +## Creating Configuration Files + +For each service, create a `subgraph-config.json` file that specifies the subgraph name and its endpoint. + +### Products Service Configuration + +In the `quick-start.Products` directory, create a file named `subgraph-config.json` with the following content: + +```json +{ + "subgraph": "Products", + "http": { + "baseAddress": "http://localhost:5003/graphql" + } +} +``` + +### Orders Service Configuration + +In the `quick-start.Orders` directory, create a `subgraph-config.json` file: + +```json +{ + "subgraph": "Orders", + "http": { + "baseAddress": "http://localhost:5004/graphql" + } +} +``` + +Ensure that the `baseAddress` matches the actual endpoints where each service is running. + +## Exporting the Schemas + +Each service needs to export its schema to a `schema.graphql` file. Assuming you are using `HotChocolate.AspNetCore.CommandLine` and have included `app.RunWithGraphQLCommands(args);` in your `Program.cs`, you can generate the schema file by running: + +```bash +dotnet run -- schema export --output schema.graphql +``` + +Execute this command in both the `quick-start.Products` and `quick-start.Orders` directories to generate their respective schema files. + +### Packing the Subgraphs + +With the configuration and schema files in place, package each service into a Fusion subgraph package (`.fsp` file) by running the following command in each service directory: + +```bash +fusion subgraph pack +``` + +This command creates an `.fsp` file containing the schema and configuration for each subgraph, preparing them for composition. + +# Step 4: Composing the Gateway Schema + +Now that we have packaged the subgraphs, we can compose them into a unified schema that the gateway will serve. + +## Composing Subgraphs into a Gateway Package + +Navigate to the `quick-start.Gateway` project directory and compose the subgraphs into a Fusion gateway package (`.fgp` file): + +```bash +fusion compose -p gateway.fgp -s ../quick-start.Orders +fusion compose -p gateway.fgp -s ../quick-start.Products +``` + +Here: + +- `-p gateway.fgp` specifies the output package file for the gateway. +- `-s` points to each subgraph package you want to include. + +This process merges the schemas from both services into a single schema that the gateway can serve to clients. + +## Starting the Gateway + +With the composition complete, start the gateway by running: + +```bash +dotnet run +``` + +The gateway is now running and ready to accept queries from clients, providing a unified API that represents both the `Products` and `Orders` services. + +# Step 5: Querying the Unified API + +You can now query both services through the gateway's single endpoint, demonstrating how Fusion seamlessly integrates multiple GraphQL services. + +## Example Query + +Try running the following query against the gateway: + +```graphql +query { + products { + id + name + price + } + orders { + id + name + items { + id + quantity + productId + } + } +} +``` + +This query fetches data from both the `Products` and `Orders` services. The gateway handles the distribution of the query to the appropriate services and assembles the results before returning them to the client. + +## Inspecting the Query Plan + +Using Nitro, you can inspect the query plan to see how the gateway orchestrates the requests. You'll observe that the gateway fetches data from the `products` and `orders` services in parallel, optimizing performance and reducing latency. + +![Query Plan](./assets/quick-start-1.png) +![Query Plan 2](./assets/quick-start-2.png) +![Query Plan 3](./assets/quick-start-3.png) + +# Step 6: Implementing Lookups for Cross-Service References + +Currently, the `LineItem` type in the `Orders` service includes a `productId` field that references a product but doesn't provide detailed product information. We can enhance the schema to allow clients to fetch product details directly through the `LineItem` type, leveraging Fusion's lookup capabilities. + +## Modifying the Orders Service + +We need to adjust the `LineItem` type in the `Orders` service to include a `Product` field instead of just `productId`. This involves adding a `Product` type and resolving it using the existing `productId`. + +In your `Orders` service, update the `LineItemType` class as follows: + +```csharp +[ObjectType] +public static partial class LineItemType +{ + static partial void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Ignore(x => x.ProductId); + } + + public static Product GetProduct([Parent] LineItem lineItem) + => new Product(lineItem.ProductId); +} + +public sealed record Product(int Id); +``` + +- **Ignoring `productId`:** The `Configure` method tells the schema to ignore the `productId` field, preventing it from appearing in the GraphQL schema. +- **Resolving `Product`:** The `GetProduct` method returns a new `Product` instance with the `Id` set to the `productId` from the `LineItem`. + +## Updating the Schema + +After making these changes, regenerate the `schema.graphql` file for the `Orders` service: + +```bash +dotnet run -- schema export --output schema.graphql +``` + +## Modifying the Products Service + +To enable the gateway to resolve `Product` details by `id`, we need to add a query to the `Products` service that allows fetching a product by its `id`. + +In the `Products` service, add the following code: + +```csharp +public static class ProductOperations +{ + [Query] + [Lookup] + [Internal] + public static Product GetProductById(int id) + { + // Replace with actual data retrieval logic + return new Product + { + Id = id, + Name = $"Product {id}", + Sku = $"SKU{id}", + Description = $"Description {id}", + Price = id + }; + } +} +``` + +The `[Lookup]` and `[Internal]` attributes are from the `HotChocolate.Fusion.SourceSchema` package. Technically, these attributes are not required, yet, it's a good practice to mark all lookup methods with `[Lookup]`. In case the lookup should not be used as an entry point in a query, you can mark it as `[Internal]`. + +You can learn more about lookups in the [Fusion documentation](/docs/fusion/v14/lookups). + +This method provides a way for the gateway to fetch a `Product` by its `id`, which is essential for resolving cross-service references. + +## Regenerating and Repacking Subgraphs + +After updating both services, export their schemas and pack them into subgraph packages again. For each service, run: + +```bash +dotnet run -- schema export --output schema.graphql +fusion subgraph pack +``` + +## Recomposing the Gateway Schema + +With the updated subgraphs, recompose the gateway schema: + +```bash +fusion compose -p gateway.fgp -s ../quick-start.Products +fusion compose -p gateway.fgp -s ../quick-start.Orders +``` + +## Restarting the Gateway + +Restart the gateway to apply the new schema: + +```bash +dotnet run +``` + +The gateway now understands how to resolve `Product` details when encountered within `LineItem` objects. + +# Step 7: Querying Cross-Service Data + +With the services updated and the gateway reconfigured, you can now write queries that fetch detailed product information through the `LineItem` type in orders. + +Try running the following query: + +```graphql +query { + orders { + id + name + items { + id + quantity + product { + id + name + price + } + } + } +} +``` + +This query fetches orders and, for each line item, retrieves detailed information about the associated product, even though the product data resides in a different service. + +# Step 8: Setting up Nitro + +TODO + +## Next Steps + +- **Lookups:** Learn more about resolving entities in Fusion using lookups. [Read the Lookups documentation](/docs/fusion/v14/lookups) +- **Entities:** Understand how Fusion handles entities and relationships across services. [Explore the Entity Relationships guide](/docs/fusion/v14/entities) +- **Engage with the Community:** Join the ChilliCream community to share your experiences, seek support, and contribute to the ongoing development of Fusion. [Joint the Slack Community](https://slack.chillicream.com/) diff --git a/website/src/docs/hotchocolate/v14/fusion/index.md b/website/src/docs/hotchocolate/v14/fusion/index.md deleted file mode 100644 index d1ee0be745c..00000000000 --- a/website/src/docs/hotchocolate/v14/fusion/index.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: "Fusion" ---- -