> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sensorup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Apollo Federation Concepts

> Understanding how the SensorUp GraphQL API uses Apollo Federation to compose multiple subgraphs into a unified API.

# Apollo Federation Concepts

The SensorUp GraphQL API is built on **Apollo Federation v2.3**, which enables multiple independent GraphQL services (subgraphs) to be composed into a single, unified graph.

## What is Apollo Federation?

Apollo Federation is an architecture for building distributed GraphQL APIs. Instead of one monolithic GraphQL server, Federation allows you to:

* **Separate concerns**: Each subgraph owns a specific domain
* **Scale independently**: Subgraphs can be deployed and scaled separately
* **Compose seamlessly**: The gateway stitches subgraphs into one unified API
* **Extend types**: Subgraphs can add fields to types defined elsewhere

## Architecture Overview

```mermaid theme={null}
graph TD
    Gateway[GraphQL Gateway<br/>Apollo Router]

    Gateway --> Assets[su-assets<br/>Asset Profiles]
    Gateway --> Auth[su-auth<br/>Authentication]
    Gateway --> CAM[su-cam-assets<br/>Connected Assets]
    Gateway --> Issues[su-issues<br/>Issue Tracking]
    Gateway --> LDAR[su-methane-ldar-backend<br/>LDAR Operations]
    Gateway --> Workflow[su-workflow<br/>Workflows]
    Gateway --> Forms[su-forms<br/>XForms]
    Gateway --> Maps[su-maps<br/>Geospatial]
    Gateway --> More[... 7 more subgraphs]

    style Gateway fill:#4A90E2,stroke:#2E5C8A,stroke-width:3px,color:#fff
    style Assets fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style Auth fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style CAM fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style Issues fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style LDAR fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style Workflow fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style Forms fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style Maps fill:#7B68EE,stroke:#5A4CB8,color:#fff
    style More fill:#9B9B9B,stroke:#6B6B6B,color:#fff
```

When you query the gateway:

1. Gateway receives your GraphQL query
2. Creates a query plan across relevant subgraphs
3. Executes queries against subgraphs (in parallel where possible)
4. Stitches results together
5. Returns unified response

## Key Federation Concepts

### Entities

Entities are types that can be referenced and extended across subgraphs. They're marked with the `@key` directive.

```graphql theme={null}
type Asset @key(fields: "profile id") {
  profile: ID!
  id: ID!
  displayName: String!
}
```

The `@key` directive specifies the fields needed to uniquely identify an instance of this type.

### Type Extensions

Subgraphs can extend types defined in other subgraphs using `@extends`:

```graphql theme={null}
type Asset @key(fields: "profile id") @extends {
  profile: ID! @external
  id: ID! @external
  issueSubject: IssueSubject!  # Added by su-issues subgraph
}
```

This allows the `su-issues` subgraph to add the `issueSubject` field to the `Asset` type originally defined in `su-assets`.

### Entity Resolution

When a subgraph needs to resolve an entity, it provides a reference resolver:

```javascript theme={null}
// In su-issues subgraph
{
  Asset: {
    __resolveReference(reference) {
      // reference = { profile: "wells", id: "well-123" }
      // Return asset-specific issue information
      return {
        profile: reference.profile,
        id: reference.id,
        issueSubject: getIssueSubjectForAsset(reference)
      }
    }
  }
}
```

## SensorUp Federation Structure

### Entity Ownership

| Entity                | Owner                   | Extended By             |
| --------------------- | ----------------------- | ----------------------- |
| `Asset`               | su-assets               | su-issues, su-eventhub  |
| `User`                | su-user                 | su-auth                 |
| `Site`                | su-sites                | su-methane-ldar-backend |
| `Issue`               | su-issues               | (none)                  |
| `EmissionObservation` | su-methane-ldar-backend | (none)                  |

### Shared Types

Some types are marked `@shareable`, meaning multiple subgraphs can define them:

```graphql theme={null}
type PageInfo @shareable {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}
```

Common shareable types:

* `PageInfo` - Pagination metadata
* `GeoJSON*` types - Geospatial types
* Scalar types - `DateTime`, `JSONObject`

## Cross-Subgraph Queries

Federation enables queries that span multiple subgraphs seamlessly.

### Example: Asset with Issue Information

```graphql theme={null}
query GetAssetWithIssues($profile: ID!, $assetId: ID!) {
  assetProfiles {          # su-assets subgraph
    byId(profile: $profile) {
      assetById(id: $assetId) {
        id
        displayName      # su-assets
        properties       # su-assets
        issueSubject {   # su-issues extends Asset
          type
          reference
          issues {       # su-issues
            id
            title
            status
          }
        }
      }
    }
  }
}
```

The gateway:

1. Queries `su-assets` for asset data
2. Takes the asset reference
3. Queries `su-issues` for issue information
4. Merges the results

### Example: User with Authentication

```graphql theme={null}
query GetUserDetails($userId: ID!) {
  user(id: $userId) {        # su-user subgraph
    id
    name                     # su-user
    email                    # su-user
    devices {                # su-auth extends User
      deviceKey              # su-auth
      name                   # su-auth
      lastSignedInAt         # su-auth
    }
  }
}
```

## Federation Directives

### @key

Marks a type as an entity with a unique key:

```graphql theme={null}
type Asset @key(fields: "profile id") {
  profile: ID!
  id: ID!
  # ...
}
```

Multiple keys are supported:

```graphql theme={null}
type Asset
  @key(fields: "profile id")
  @key(fields: "catalogId dataset assetId") {
  # ...
}
```

### @extends

Indicates a type is being extended from another subgraph:

```graphql theme={null}
type Asset @key(fields: "profile id") @extends {
  profile: ID! @external
  id: ID! @external
  newField: String!
}
```

### @external

Marks fields that are defined in another subgraph:

```graphql theme={null}
type Asset @key(fields: "profile id") @extends {
  profile: ID! @external
  id: ID! @external
  computedField: String! @requires(fields: "profile id")
}
```

### @requires

Specifies fields needed from the base type to resolve a field:

```graphql theme={null}
type Asset @key(fields: "profile id") @extends {
  profile: ID! @external
  displayName: String! @external
  enrichedName: String! @requires(fields: "displayName")
}
```

### @provides

Optimizes queries by providing fields from related entities:

```graphql theme={null}
type AssetProfile @key(fields: "profile") {
  upSpecification: UpSpecification @provides(fields: "catalogId dataset")
}
```

### @shareable

Allows multiple subgraphs to define the same type/field:

```graphql theme={null}
type PageInfo @shareable {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
}
```

## Query Planning

The gateway creates an optimized query plan for each request.

### Simple Query

```graphql theme={null}
query {
  session {
    username
    authenticated
  }
}
```

Query plan:

```
QueryPlan {
  Fetch(service: "su-auth") {
    { session { username authenticated } }
  }
}
```

### Cross-Subgraph Query

```graphql theme={null}
query {
  assetProfiles {
    byId(profile: "wells") {
      assets(first: 10) {
        edges {
          node {
            id
            displayName    # su-assets
            issueSubject { # su-issues
              issues {
                id
                title
              }
            }
          }
        }
      }
    }
  }
}
```

Query plan:

```
QueryPlan {
  Sequence {
    Fetch(service: "su-assets") {
      { assetProfiles { byId(profile: "wells") {
          assets(first: 10) {
            edges {
              node { __typename profile id displayName }
            }
          }
        }
      }}
    },
    Flatten(path: "assetProfiles.byId.assets.edges.@.node") {
      Fetch(service: "su-issues") {
        { ... on Asset { __typename profile id issueSubject {
            issues { id title }
          }
        }}
      }
    }
  }
}
```

## Performance Considerations

### Parallel Execution

Federation executes independent fetches in parallel:

```graphql theme={null}
query {
  session {        # su-auth - fetched in parallel
    username
  }
  assetProfiles {  # su-assets - fetched in parallel
    all(first: 10) {
      edges {
        node {
          title
        }
      }
    }
  }
}
```

### N+1 Problem

Be aware of the N+1 problem with entity references:

```graphql theme={null}
query {
  issues {
    all(first: 100) {
      edges {
        node {
          id
          subject {
            assetReference {
              asset {              # Potentially 100 lookups to su-assets!
                displayName
              }
            }
          }
        }
      }
    }
  }
}
```

**Solution**: Federation batches entity resolutions using DataLoader-like mechanisms.

## Debugging Federation

### Query Plan Visualization

Use Apollo Studio to visualize query plans and identify performance bottlenecks.

### Subgraph Errors

When a subgraph fails, the error includes the service name:

```json theme={null}
{
  "errors": [
    {
      "message": "Failed to fetch from subgraph 'su-assets'",
      "extensions": {
        "serviceName": "su-assets",
        "code": "SUBREQUEST_HTTP_ERROR"
      }
    }
  ]
}
```

### Trace Subgraph Calls

Monitor subgraph execution times:

```json theme={null}
{
  "extensions": {
    "ftv1": "base64-encoded-trace-data"
  }
}
```

## Best Practices

1. **Design entity keys carefully**: Choose stable, immutable fields for `@key`

2. **Minimize entity hops**: Deeply nested cross-subgraph queries can be slow

3. **Use @provides sparingly**: Only when it significantly reduces fetches

4. **Batch related queries**: Combine multiple operations in one request

5. **Monitor query plans**: Use Apollo Studio to identify inefficient plans

6. **Handle partial failures**: Subgraph failures may return partial data

7. **Version subgraph changes**: Coordinate breaking changes across subgraphs

8. **Test entity resolution**: Ensure reference resolvers handle all key combinations

## Differences from Schema Stitching

| Feature        | Federation           | Schema Stitching |
| -------------- | -------------------- | ---------------- |
| Type ownership | Clear owner per type | Ambiguous        |
| Type extension | Native support       | Manual           |
| Query planning | Automatic            | Manual           |
| Performance    | Optimized            | Variable         |
| DX             | Better               | Complex          |

## Resources

* **[Apollo Federation Documentation](https://www.apollographql.com/docs/federation/)**
* **[Subgraph Reference](./subgraphs/assets)** - SensorUp subgraph schemas
* **[Performance Guide](./performance)** - Optimization strategies

## Studio Access

For detailed query planning and performance monitoring, access Apollo Studio:

[https://studio.apollographql.com/graph/su-graphql-t912yf/](https://studio.apollographql.com/graph/su-graphql-t912yf/)

(Contact your SensorUp account team for access)
