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
npm i graphql @gqloom/core @apollo/subgraph @gqloom/federation
pnpm add graphql @gqloom/core @apollo/subgraph @gqloom/federation
yarn add graphql @gqloom/core @apollo/subgraph @gqloom/federation
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:
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> {}
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> {}
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:
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:
export const userResolver = resolver
.of(User, {
// ...
})
.directives({ key: { fields: "id", resolvable: true } })
Add Directives to the Schema
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:
{
directives: [
{
name: "validation",
args: {
regex: "/abc+/"
}
},
{
name: "required",
args: {},
}
]
}
- Using key-value pairs:
{
directives: {
validation: {
regex: "/abc+/"
},
required: {}
}
}
Resolve Reference
@gqloom/federation
provides the resolveReference
function to help you resolve references.
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.
import { FederatedSchemaLoom } from "@gqloom/federation"
import { printSubgraphSchema } from "@apollo/subgraph"
const schema = FederatedSchemaLoom.weave(userResolver)
const schemaText = printSubgraphSchema(schema)