---
url: /docs/advanced/federation.md
---
# Federation

[Apollo Federation](https://www.apollographql.com/docs/federation/) allows you to declaratively combine multiple GraphQL APIs into a single federated graph. A federated graph enables clients to interact with multiple APIs through a single request.

GQLoom Federation provides GQLoom's support for Apollo Federation.

## Installation

::: code-group

```sh [npm]
npm i graphql @gqloom/core @apollo/subgraph @gqloom/federation
```

```sh [pnpm]
pnpm add graphql @gqloom/core @apollo/subgraph @gqloom/federation
```

```sh [yarn]
yarn add graphql @gqloom/core @apollo/subgraph @gqloom/federation
```

```sh [bun]
bun add graphql @gqloom/core @apollo/subgraph @gqloom/federation
```

:::

## GraphQL Directives

Apollo Federation directives ([Directives](https://www.apollographql.com/docs/federation/federated-schemas/federated-directives/)) are used to describe how to combine multiple GraphQL APIs into a federated graph.

### Declare Directives on Objects

In GQLoom, we can declare GraphQL directives in the `directives` field of the `extensions` property of objects and fields:

::: code-group

```ts twoslash [valibot]
import * as v from "valibot"
import { asObjectType } from "@gqloom/valibot"

export const User = v.pipe(
  v.object({
    id: v.string(),
    name: v.string(),
  }),
  asObjectType({
    name: "User",
    extensions: {
      directives: { key: { fields: "id", resolvable: true } },
    },
  })
)

export interface IUser extends v.InferOutput<typeof User> {}
```

```ts [zod]
import * as z from "zod"
import { asObjectType } from "@gqloom/zod"

export const User = z
  .object({
    id: z.string(),
    name: z.string(),
  })
  .register(asObjectType, {
    name: "User",
    extensions: {
      directives: { key: { fields: "id", resolvable: true } },
    },
  })

export interface IUser extends z.infer<typeof User> {}
```

```ts twoslash [graphql.js]
import { silk } from "@gqloom/core"
import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"

export interface IUser {
  id: string
  name: string
}

export const User = silk(
  new GraphQLObjectType<IUser>({
    name: "User",
    fields: {
      id: { type: new GraphQLNonNull(GraphQLString) },
      name: { type: new GraphQLNonNull(GraphQLString) },
    },
    extensions: {
      directives: { key: { fields: "id", resolvable: true } },
    },
  })
)
```

:::

In the above example, we declared a `@key` directive, which marks the `id` field of the `User` object as a resolvable field. We will get the following Schema:

```graphql [GraphQL Schema]
type User
  @key(fields: "id", resolvable: true)
{
  id: String!
  name: String!
}
```

### Declare Directives on Resolvers

We can also use resolvers to declare directives for objects:

```ts
export const userResolver = resolver
  .of(User, {
    // ...
  })
  .directives({ key: { fields: "id", resolvable: true } })
```

### Adding Directives to the Schema

```ts
const schema = FederatedSchemaLoom.weave(
  userResolver,
  FederatedSchemaLoom.config({
    extensions: {
      directives: {
        link: [
          {
            url: "https://specs.apollo.dev/federation/v2.6",
            import: ["@extends", "@external", "@key", "@shareable"],
          },
        ],
      },
    },
  })
)
```

### Directive Formats

We have two formats for declaring directives:

* Using an array:

```json
{
  directives: [
    {
      name: "validation",
      args: {
        regex: "/abc+/"
      }
    },
    {
      name: "required",
      args: {},
    }
  ]
}
```

* Using key-value pairs:

```json
{
  directives: {
    validation: {
      regex: "/abc+/"
    },
    required: {}
  }
}
```

## Resolve Reference

`@gqloom/federation` provides the `resolveReference` function to help you resolve references.

```ts twoslash
import { silk } from "@gqloom/core"
import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"

export interface IUser {
  id: string
  name: string
}

export const User = silk<IUser>(
  new GraphQLObjectType({
    name: "User",
    fields: {
      id: { type: new GraphQLNonNull(GraphQLString) },
      name: { type: new GraphQLNonNull(GraphQLString) },
    },
    extensions: {
      directives: { key: { fields: "id", resolvable: true } },
    },
  })
)

function getUserByID(id: string): IUser {
  return { id, name: "Jane Smith" }
}
// ---cut---
import { query } from "@gqloom/core"
import { resolveReference, resolver } from "@gqloom/federation"

export const userResolver = resolver
  .of(User, {
    user: query(User, () => ({ id: "1", name: "John" })),
  })
  .directives({ key: { fields: "id", resolvable: true } })
  .resolveReference((user) => getUserByID(user.id))
```

## Weaving

The `FederatedSchemaWeaver.weave` function imported from `@gqloom/federation` is used to weave the Federation Schema. Compared with `@gqloom/core`, the `FederatedSchemaWeaver.weave` function in `@gqloom/federation` will output a Schema with directives.

It's also worth noting that we need to use the `printSubgraphSchema` function imported from `@apollo/subgraph` to convert the Schema to text format to preserve the directives.

```ts twoslash
import { silk } from "@gqloom/core"
import { GraphQLObjectType, GraphQLString, GraphQLNonNull } from "graphql"

export interface IUser {
  id: string
  name: string
}

export const User = silk<IUser>(
  new GraphQLObjectType({
    name: "User",
    fields: {
      id: { type: new GraphQLNonNull(GraphQLString) },
      name: { type: new GraphQLNonNull(GraphQLString) },
    },
    extensions: {
      directives: { key: { fields: "id", resolvable: true } },
    },
  })
)

function getUserByID(id: string): IUser {
  return { id, name: "Jane Smith" }
}
import { resolver, query } from "@gqloom/core"
import { resolveReference } from "@gqloom/federation"

export const userResolver = resolver.of(
  User,
  {
    user: query(User, () => ({ id: "1", name: "John" })),
  },
  {
    extensions: {
      ...resolveReference<IUser, "id">((user) => getUserByID(user.id)),
    },
  }
)
// ---cut---
import { FederatedSchemaLoom } from "@gqloom/federation"
import { printSubgraphSchema } from "@apollo/subgraph"

const schema = FederatedSchemaLoom.weave(userResolver)
const schemaText = printSubgraphSchema(schema)
```
