npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

graphql-federation-subgraph

v0.4.0

Published

GraphQL Federation Spec directives for any TypeScript GraphQL server — a server-agnostic alternative to @apollo/subgraph.

Downloads

614

Readme

graphql-federation-subgraph

Federation directives from the GraphQL Federation Spec for any TypeScript/JavaScript GraphQL server.

This package plays the same role for the GraphQL Federation Spec that @apollo/subgraph plays for Apollo Federation: it lets a service use the federation directives (@key, @lookup, @shareable, …) in its schema without defining them, and export its schema for composition. Unlike @apollo/subgraph, it is not tied to any particular server — the result is a plain GraphQLSchema, so it works with GraphQL Yoga, Apollo Server, Mercurius, graphql-http, and anything else that accepts one. It has zero runtime dependencies; graphql itself is the only peer dependency.

Why there are no reference resolvers

In Apollo Federation, a subgraph implements a side-channel protocol: the router calls Query._entities with opaque representations, and each entity needs a __resolveReference resolver. The GraphQL Federation Spec has no such protocol. Entity resolution happens through ordinary fields annotated with @lookup:

type Query {
  productById(id: ID!): Product @lookup
}

productById is a regular field with a regular resolver — the distributed executor simply calls it. That means this package needs no _entities, _Any, _service, or __resolveReference machinery at all; it only manages directive definitions and schema export.

Installation

npm install graphql-federation-subgraph graphql

graphql ^16.11.0 || ^17.0.0 is a peer dependency.

Quick start

import { buildSubgraphSchema } from "graphql-federation-subgraph";

const typeDefs = /* GraphQL */ `
  type Query {
    productById(id: ID!): Product @lookup
    productBySku(sku: String! @is(field: "sku")): Product @lookup
  }

  type Product @key(fields: "id") @key(fields: "sku") {
    id: ID!
    sku: String!
    name: String!
  }
`;

const products = [
  { id: "1", sku: "A-1", name: "Chair" },
  { id: "2", sku: "B-2", name: "Table" },
];

const resolvers = {
  Query: {
    productById: (_parent: unknown, args: { id: string }) =>
      products.find((product) => product.id === args.id),
    productBySku: (_parent: unknown, args: { sku: string }) =>
      products.find((product) => product.sku === args.sku),
  },
};

const schema = buildSubgraphSchema({ typeDefs, resolvers });

All federation directive and scalar definitions are added automatically; definitions you provide yourself take precedence and are never duplicated.

Each server below exposes two things: the GraphQL endpoint itself, and the source schema document at /graphql/schema.graphql for composition tooling (see Exporting the schema for composition), served by a handler from this package:

import { createSourceSchemaHandler } from "graphql-federation-subgraph";

const schemaHandler = createSourceSchemaHandler(schema);

GraphQL Yoga

import { createServer } from "node:http";
import { createYoga } from "graphql-yoga";

const yoga = createYoga({ schema });

createServer((req, res) => {
  if (req.url?.split("?")[0] === "/graphql/schema.graphql") {
    schemaHandler(req, res);

    return;
  }

  yoga(req, res);
}).listen(4000);

Apollo Server

startStandaloneServer answers every path itself and cannot mount additional routes, so the schema document route uses Apollo's Express integration:

import express from "express";
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@as-integrations/express5";

const server = new ApolloServer({ schema });
await server.start();

const app = express();
app.get("/graphql/schema.graphql", schemaHandler);
app.use("/graphql", express.json(), expressMiddleware(server));
app.listen(4000);

Mercurius (Fastify)

import Fastify from "fastify";
import mercurius from "mercurius";

const app = Fastify();
app.register(mercurius, { schema });

// hijack() hands the raw request/response pair to the handler, keeping
// Fastify from sending its own response on top.
app.get("/graphql/schema.graphql", (request, reply) => {
  reply.hijack();
  schemaHandler(request.raw, reply.raw);
});

await app.listen({ port: 4000 });

graphql-http

import { createHandler } from "graphql-http/lib/use/http";
import { createServer } from "node:http";

const handler = createHandler({ schema });

createServer((req, res) => {
  if (req.url?.split("?")[0] === "/graphql/schema.graphql") {
    schemaHandler(req, res);

    return;
  }

  handler(req, res);
}).listen(4000);

NestJS

With schema-first drivers, contribute the federation definitions alongside your own type definitions:

import { Module } from "@nestjs/common";
import { GraphQLModule } from "@nestjs/graphql";
import { ApolloDriver, type ApolloDriverConfig } from "@nestjs/apollo";
import { federationTypeDefsSDL } from "graphql-federation-subgraph";

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      typeDefs: [federationTypeDefsSDL, typeDefs].join("\n\n"),
      resolvers,
    }),
  ],
})
export class AppModule {}

Code-first (decorator-based) schemas need directive support from the schema builder itself.

The schema Nest builds becomes available through GraphQLSchemaHost once the application has initialized, while the schema document route must be registered before init so it precedes the Apollo middleware, which is mounted with a prefix match on /graphql and would swallow the route — so the handler is created lazily on the first request:

import { NestFactory } from "@nestjs/core";
import { GraphQLSchemaHost } from "@nestjs/graphql";
import {
  createSourceSchemaHandler,
  type SourceSchemaHandler,
} from "graphql-federation-subgraph";

const app = await NestFactory.create(AppModule);

let schemaHandler: SourceSchemaHandler | undefined;

app.use("/graphql/schema.graphql", (req, res) => {
  schemaHandler ??= createSourceSchemaHandler(
    app.get(GraphQLSchemaHost).schema,
  );
  schemaHandler(req, res);
});

await app.listen(4000);

Exporting the schema for composition

Composition tooling needs your schema with the federation directives applied — which the standard printSchema from graphql-js silently drops, and which standard introspection cannot express at all. This package exports it two ways.

As a file, with printSourceSchema:

import { writeFileSync } from "node:fs";
import { printSourceSchema } from "graphql-federation-subgraph";

writeFileSync("products.graphql", printSourceSchema(schema));

By default the output includes the definitions of the spec directives you actually use, plus the spec scalars the printed document references (federationDefinitions: "used") — as long as the GraphQL Federation Spec is not officially released, tooling can't be assumed to know these definitions as built-ins, so the used ones travel with the document. That makes the output self-contained SDL that plain buildSchema accepts (a used directive the schema itself never registered — say, applied through extensions.directives — still prints with its definition), and it still round-trips cleanly through buildSubgraphSchema. Pass { federationDefinitions: "all" } to emit every spec definition regardless of use, or { federationDefinitions: "none" } to emit only your own definitions with the directives applied — the built-in treatment the released spec will mandate. Definitions you customized (say, @key with an extra argument, which the spec allows) always stay in the output, so the printed SDL always describes the schema faithfully.

Over HTTP, with createSourceSchemaHandler — the handler the server examples above mount at /graphql/schema.graphql. It serves printSourceSchema(schema) as application/graphql (and takes the same options), answering GET and HEAD and rejecting other methods with 405. The request and response types are structural, so it plugs into node:http, Express, and NestJS directly, and into Fastify via reply.hijack(); routing stays the server's job, so mount it wherever fits — /graphql/schema.graphql is just the convention used here.

Like introspection, the endpoint reveals the full schema. A deployment that disables introspection in production should gate or omit this endpoint the same way.

Directive reference

| Directive | Definition | Purpose | | --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | | @lookup | on FIELD_DEFINITION | Marks a field the distributed executor can use to resolve an entity by key. | | @internal | on OBJECT \| FIELD_DEFINITION | Hides a member from the composite schema and from merging; usable only by the executor (e.g. internal lookups). | | @inaccessible | on FIELD_DEFINITION \| OBJECT \| … | Globally hides a member from the client-facing composite schema. | | @is(field: FieldSelectionMap!) | on ARGUMENT_DEFINITION | Maps a lookup argument to fields of the entity it resolves. | | @require(field: FieldSelectionMap!) | on ARGUMENT_DEFINITION | Declares an argument the executor fulfills with data from other source schemas. | | @key(fields: FieldSelectionSet!) | repeatable on OBJECT \| INTERFACE | Declares a stable key that identifies an entity across source schemas. | | @shareable | repeatable on OBJECT \| FIELD_DEFINITION | Allows a field to be contributed by multiple source schemas. | | @provides(fields: FieldSelectionSet!) | on FIELD_DEFINITION | Declares subfields of the return type this field can resolve locally. | | @external | on FIELD_DEFINITION | Marks a field recognized but not resolved by this source schema. | | @override(from: String!) | on FIELD_DEFINITION | Migrates a field from another source schema to this one. | | @interfaceObject ⚠️ | on OBJECT | Object type standing in for an interface owned by another source schema. | | @implement ⚠️ | on FIELD_DEFINITION | Explicit replacement for a field projected from an @interfaceObject stand-in. |

⚠️ @interfaceObject and @implement are provisional: they come from the open spec PR graphql/composite-schemas-spec#233 and their names or semantics may change before the PR is merged.

API

  • buildSubgraphSchema(options) — builds an executable GraphQLSchema from typeDefs (SDL string, DocumentNode, or nested arrays of either) and resolvers (a map or array of maps), injecting any federation definitions the document doesn't already define. Resolver maps support field resolvers (plain functions or { resolve, subscribe }), __resolveType / __isTypeOf, custom scalar configs or GraphQLScalarType instances, and enum internal values ({ Color: { RED: "#f00" } }). assumeValid / assumeValidSDL are forwarded to graphql-js.
  • printSourceSchema(schema, options?) — prints SDL including applied federation directives. options.federationDefinitions controls which spec directive/scalar definitions are emitted: "used" (default) exports the ones the schema applies plus the spec scalars the output references, "all" exports every spec definition, "none" omits them.
  • createSourceSchemaHandler(schema, options?) — creates an HTTP handler (request, response) => void serving printSourceSchema(schema, options) as application/graphql. Structurally typed (SourceSchemaRequest / SourceSchemaResponse), so it works with node:http, Express, and NestJS directly and with Fastify via reply.hijack(); answers GET/HEAD, 405 otherwise.
  • federationTypeDefs / federationTypeDefsSDL — the definitions as a DocumentNode / SDL string, for wiring into your own schema-building pipeline (e.g. makeExecutableSchema({ typeDefs: [federationTypeDefs, typeDefs] })).
  • federationDirectives, lookupDirective, keyDirective, … — GraphQLDirective instances for code-first schemas (new GraphQLSchema({ …, directives: [...specifiedDirectives, ...federationDirectives] })).
  • fieldSelectionMapScalar / fieldSelectionSetScalar — the spec's scalar types.
  • federationDirectiveNames / federationScalarNames — the injected names.

Comparison with @apollo/subgraph

| | graphql-federation-subgraph | @apollo/subgraph | | ---------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | | Specification | GraphQL Federation Spec (GraphQL Foundation) | Apollo Federation | | Entity resolution | Ordinary @lookup fields with ordinary resolvers | Query._entities + __resolveReference | | Schema exposure | printSourceSchema → file, createSourceSchemaHandler/graphql/schema.graphql | Runtime Query._service { sdl } | | Injected runtime types | None | _Service, _Entity, _Any, _service, _entities | | Spec linking | None needed (bare directive names) | @link imports (Federation 2) |

License

MIT © Copyright (c) 2018 - present ChilliCream Inc.