Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs-website/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -410,15 +410,19 @@
"icon": "sign-post",
"pages": [
"federation/federation-directives-index",
"federation/directives/deprecated",
"federation/directives/specifiedby",
"federation/directives/key",
"federation/directives/external",
"federation/directives/provides",
"federation/directives/requires",
"federation/directives/shareable",
"federation/directives/authenticated",
"federation/directives/requiresscopes",
"federation/directives/connect__fieldresolver",
"federation/directives/openfed__subscriptionfilter",
"federation/directives/openfed__configuredescription"
"federation/directives/openfed__configuredescription",
"federation/directives/openfed__requirefetchreasons"
]
},
{
Expand Down
106 changes: 106 additions & 0 deletions docs-website/federation/directives/connect__fieldresolver.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
title: "@connect__fieldResolver"
icon: "function"
description: "The @connect__fieldResolver directive turns a field into a dedicated gRPC RPC call with explicit parent context."
---

## Definition

```graphql
directive @connect__fieldResolver(context: connect__FieldSet!) on FIELD_DEFINITION
```

## Arguments

| Argument | Type | Default | Description |
| --------- | -------------------- | ------- | ------------------------------------------------------------------------------------------ |
| `context` | `connect__FieldSet!` | — | A space-separated list of sibling fields from the parent type to pass as resolver context. |

## Overview

`@connect__fieldResolver` is part of the [Cosmo Connect](/connect/intro) gRPC integration.
It marks a field so that the router resolves it through a separate RPC call
instead of including it in the parent type's response.

Without this directive,
all fields on a type are resolved together in a single RPC call.
This is efficient when every field comes from the same data source,
but not when some fields are expensive to compute or served by a different backend.
For example,
`id` and `price` on a `Product` type might come from a local database,
while `shippingEstimate` requires a call to an external shipping service
with higher latency.
By annotating `shippingEstimate` with `@connect__fieldResolver`,
the router only calls the shipping service when the client actually requests that field.

The `context` argument specifies which fields from the parent type the resolver needs.
These fields are fetched first,
then passed to a generated `Resolve{TypeName}{FieldName}` RPC method.
The router batches all context entries into a single call using a `repeated` message,
eliminating N+1 problems.

For a complete guide including protobuf generation,
Go implementation examples,
and batching behavior,
see [Field Resolvers](/router/gRPC/field-resolvers).

## Example

```graphql
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
shippingEstimate(zip: String!): Float!
@connect__fieldResolver(context: "id price")
}
```

In this example,
the router first resolves `id` and `price` from the parent RPC.
It then calls `ResolveProductShippingEstimate` with those values as context
and `zip` as a field argument.

## Generated protobuf

For the `shippingEstimate` field in the example above,
the protographic tooling generates:

```protobuf
service ProductService {
rpc ResolveProductShippingEstimate(ResolveProductShippingEstimateRequest)
returns (ResolveProductShippingEstimateResponse) {}
}

message ResolveProductShippingEstimateArgs {
string zip = 1;
}

message ResolveProductShippingEstimateContext {
string id = 1;
double price = 2;
}

message ResolveProductShippingEstimateRequest {
repeated ResolveProductShippingEstimateContext context = 1;
ResolveProductShippingEstimateArgs field_args = 2;
}

message ResolveProductShippingEstimateResult {
double shipping_estimate = 1;
}

message ResolveProductShippingEstimateResponse {
repeated ResolveProductShippingEstimateResult result = 1;
}
```

The `context` message contains the fields listed in the directive (`id` and `price`).
Because the field has a GraphQL argument (`zip`),
an `Args` message and `field_args` field are added to the request.

## See also

- [Field Resolvers guide](/router/gRPC/field-resolvers) for implementation details and batching behavior.
- [`@requires`](/federation/directives/requires) for declaring cross-subgraph field dependencies.
- [Cosmo Connect](/connect/intro) for the full gRPC integration overview.
89 changes: 89 additions & 0 deletions docs-website/federation/directives/deprecated.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: "@deprecated"
icon: "triangle-exclamation"
description: "The @deprecated directive marks fields, arguments, input fields, and enum values as deprecated in the schema."
---

## Definition

```graphql
directive @deprecated(reason: String = "No longer supported") on
FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE
```

## Arguments

| Argument | Type | Default | Description |
| -------- | -------- | ---------------------- | ----------------------------------------------------------------- |
| `reason` | `String` | `"No longer supported"` | Explains why the element is deprecated and what to use instead. |

## Overview

`@deprecated` is a built-in GraphQL directive defined in the
[GraphQL specification](https://spec.graphql.org/October2021/#sec--deprecated).
It signals to API consumers that a schema element should no longer be used.

Deprecated elements remain fully functional.
The directive is a documentation and tooling signal,
not a removal mechanism.
GraphQL IDEs such as GraphiQL display deprecation warnings,
and introspection exposes `isDeprecated` and `deprecationReason` fields
so tools can surface this information automatically.

### Supported locations

- **Field definitions** on object and interface types.
- **Argument definitions** on fields and directives.
- **Input field definitions** on input object types.
- **Enum values**.

## Examples

### Deprecating a field

```graphql
type Product {
id: ID!
name: String!
sku: String! @deprecated(reason: "Use `id` instead.")
}
```

### Deprecating an enum value

```graphql
enum Status {
ACTIVE
INACTIVE @deprecated(reason: "Use ARCHIVED.")
ARCHIVED
}
```

### Deprecating a field argument

```graphql
type Query {
users(
limit: Int
first: Int @deprecated(reason: "Use `limit`.")
): [User!]!
}
```

### Deprecating an input field

```graphql
input CreateUserInput {
name: String!
username: String @deprecated(reason: "Usernames are auto-generated.")
}
```

## Federation behavior

During composition,
if the same field or enum value is marked `@deprecated` in multiple subgraphs with different reasons,
Cosmo keeps the longest reason string.

`@deprecated` is preserved in both the client-facing schema and the router schema.
It appears in introspection responses so that consumers can detect deprecated usage.
153 changes: 153 additions & 0 deletions docs-website/federation/directives/openfed__requirefetchreasons.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
title: "@openfed requireFetchReasons"
icon: "magnifying-glass-chart"
description: "The @openfed__requireFetchReasons directive enables fetch reason propagation to subgraphs for debugging and observability."
---

## Definition

```graphql
directive @openfed__requireFetchReasons repeatable on
FIELD_DEFINITION | INTERFACE | OBJECT
```

## Overview

`@openfed__requireFetchReasons` is a marker directive that tells the router to include
fetch reason metadata in subgraph requests for the annotated fields.

In a federated graph,
a field may be fetched for several reasons:
the client requested it,
another subgraph depends on it via `@key` or `@requires`,
or it is part of an entity key.
By default,
the subgraph has no visibility into why a field is being fetched.
This directive enables that visibility by adding a `fetch_reasons` extension
to the subgraph request body.

This is particularly useful for compliance-sensitive data.
In a federated graph,
any subgraph can use `@requires` to declare a dependency on a field from another subgraph.
When that happens,
the router fetches the field automatically — no explicit permission from the providing subgraph is needed.
For fields containing regulated or personally identifiable information,
this means another team's subgraph could gain access to protected data
without an explicit handshake.

With `@openfed__requireFetchReasons`,
the providing subgraph can inspect every incoming request to see whether a field
was requested by the end user or by another subgraph,
and which subgraph it was.
This allows the subgraph to maintain an allow list of subgraphs
that are permitted to access specific fields,
ensuring the data owner stays compliant with regulations.

## Router configuration

Fetch reason propagation must be enabled in the router configuration:

```yaml
engine:
enable_require_fetch_reasons: true
```

Environment variable: `ENGINE_ENABLE_REQUIRE_FETCH_REASONS` (default: `false`).

Without this setting,
the directive has no effect at runtime.

## Subgraph request format

When enabled,
the router adds a `fetch_reasons` array to `extensions` in the subgraph request:

```json
{
"query": "...",
"variables": {},
"extensions": {
"fetch_reasons": [
{
"typename": "User",
"field": "id",
"by_user": true
},
{
"typename": "User",
"field": "email",
"by_subgraphs": ["account"],
"is_requires": true
}
]
}
}
```

Each entry includes:

| Field | Type | Description |
| -------------- | ---------- | ------------------------------------------------------------------ |
| `typename` | `String` | The type containing the field. |
| `field` | `String` | The field name. |
| `by_user` | `Boolean` | `true` if the client query explicitly requested this field. |
| `by_subgraphs` | `[String]` | Subgraph names whose `@key` or `@requires` caused this fetch. |
| `is_key` | `Boolean` | `true` if the field is fetched as part of an entity key. |
| `is_requires` | `Boolean` | `true` if the field is fetched to satisfy a `@requires` dependency. |

Only fields annotated with `@openfed__requireFetchReasons`
(directly or through type-level inheritance)
have their reasons included.
Other fields in the same request are not tracked.

## Inheritance and scoping

When applied to an object or interface type,
the directive is inherited by all fields defined in that same declaration block.

```graphql
# All fields in this block inherit the directive
type User @openfed__requireFetchReasons {
id: ID!
name: String!
email: String!
}

# Fields in a separate extension do NOT inherit it
extend type User {
avatar: String!
}
```

To mark only specific fields,
apply the directive at the field level:

```graphql
type User {
id: ID!
name: String!
email: String! @openfed__requireFetchReasons
}
```

Fields in `extend` blocks require their own annotation
or a type-level directive on the extension:

```graphql
extend type User @openfed__requireFetchReasons {
phone: String!
}
```

## Use cases

- **Debugging**: Inspect why a subgraph is receiving certain fields in its requests.
- **Observability**: Log or metric fetch reasons in subgraph middleware to track
cross-subgraph dependencies.
- **Conditional logic**: Subgraphs can optimize responses based on whether a field
was requested by the client or by the federation engine.

## See also

- [`@requires`](/federation/directives/requires) for declaring cross-subgraph field dependencies.
- [`@key`](/federation/directives/key) for defining entity keys.
Loading
Loading