Skip to content
GQLoom

Federation

Apollo 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

sh
npm i graphql @gqloom/core @apollo/subgraph @gqloom/federation
sh
pnpm add graphql @gqloom/core @apollo/subgraph @gqloom/federation
sh
yarn add graphql @gqloom/core @apollo/subgraph @gqloom/federation
sh
bun add graphql @gqloom/core @apollo/subgraph @gqloom/federation

GraphQL Directives

Apollo Federation directives (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:

ts
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
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
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
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 } })

Add 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
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
import { 
FederatedSchemaLoom
} from "@gqloom/federation"
import {
printSubgraphSchema
} from "@apollo/subgraph"
const
schema
=
FederatedSchemaLoom
.
weave
(
userResolver
)
const
schemaText
=
printSubgraphSchema
(
schema
)