Skip to content

Commit 81743c4

Browse files
[managed-federation] add new plugin to integrate Apollo GraphOS supergraph (#3305)
Co-authored-by: Arda TANRIKULU <[email protected]>
1 parent 0cc235e commit 81743c4

File tree

9 files changed

+799
-24
lines changed

9 files changed

+799
-24
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Apollo Managed Federation Example
2+
3+
Run Apollo Managed Federation supergraph using GraphQL Yoga.
4+
5+
Start Gateway:
6+
7+
```bash
8+
pnpm start
9+
```
10+
11+
Then visit `http://localhost:4000`
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "example-apollo-federation",
3+
"version": "0.0.1",
4+
"private": true,
5+
"scripts": {
6+
"check": "tsc --noEmit --pretty",
7+
"start": "tsx src/index.ts"
8+
},
9+
"dependencies": {
10+
"@graphql-yoga/apollo-managed-federation": "workspace:^",
11+
"graphql-yoga": "workspace:^",
12+
"tsx": "4.11.0"
13+
},
14+
"devDependencies": {
15+
"@graphql-yoga/apollo-managed-federation": "workspace:*",
16+
"graphql-yoga": "workspace:*",
17+
"typescript": "5.4.5"
18+
}
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { createServer } from 'node:http';
2+
import { createYoga } from 'graphql-yoga';
3+
import { useManagedFederation } from '@graphql-yoga/apollo-managed-federation';
4+
5+
const yoga = createYoga({
6+
plugins: [useManagedFederation()],
7+
});
8+
9+
const server = createServer(yoga);
10+
11+
server.listen(4000, () => {
12+
console.log('Server is running on http://localhost:4000');
13+
});
14+
15+
process.on('SIGINT', () => {
16+
server.close();
17+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"rootDir": "./src",
4+
"outDir": "./dist",
5+
"target": "esnext",
6+
"module": "commonjs",
7+
"moduleResolution": "node",
8+
"esModuleInterop": true,
9+
"sourceMap": true,
10+
"declaration": true,
11+
"declarationMap": true,
12+
"skipLibCheck": true,
13+
"strict": true
14+
},
15+
"include": ["./src/**/*"]
16+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## `@graphql-yoga/apollo-managed-federation`
2+
3+
This plugin integrates Apollo Managed Federation into Yoga.
4+
5+
## Installation
6+
7+
First install required dependencies:
8+
9+
```
10+
yarn add graphql-yoga @graphql-yoga/apollo-managed-federation
11+
```
12+
13+
You will also need and API key and the graph ref you want to deploy.
14+
15+
[Please follow this instructions if you don't know where to find this values.](https://www.apollographql.com/docs/federation/v1/managed-federation/setup/#4-connect-the-gateway-to-studio)
16+
17+
## Usage Example
18+
19+
```ts
20+
import { createServer } from 'node:http'
21+
import { createYoga } from 'graphql-yoga'
22+
import { useManagedFederation } from '@graphql-yoga/apollo-managed-federation'
23+
24+
const yoga = createYoga({
25+
plugins: [useManagedFederation()]
26+
})
27+
28+
const server = createServer(yoga)
29+
30+
server.listen(4000, () => {
31+
console.log('Server is running on http://localhost:4000')
32+
})
33+
34+
process.on('SIGINT', () => {
35+
server.close()
36+
})
37+
```
38+
39+
You can then start the gateway, don't forget to provide your API key and graph ref. You can also
40+
provide this values programmatically in plugin options.
41+
42+
```bash
43+
APOLLO_KEY='<YOUR_GRAPH_API_KEY>' APOLLO_GRAPH_REF='<YOUR_GRAPH_ID>@<VARIANT>' node index.mjs
44+
```
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"name": "@graphql-yoga/apollo-managed-federation",
3+
"version": "0.0.0",
4+
"type": "module",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/dotansimha/graphql-yoga.git",
8+
"directory": "packages/plugins/apollo-inline-trace"
9+
},
10+
"author": "Valentin Cocaud <[email protected]>",
11+
"license": "MIT",
12+
"engines": {
13+
"node": ">=18.0.0"
14+
},
15+
"main": "dist/cjs/index.js",
16+
"module": "dist/esm/index.js",
17+
"exports": {
18+
".": {
19+
"require": {
20+
"types": "./dist/typings/index.d.cts",
21+
"default": "./dist/cjs/index.js"
22+
},
23+
"import": {
24+
"types": "./dist/typings/index.d.ts",
25+
"default": "./dist/esm/index.js"
26+
},
27+
"default": {
28+
"types": "./dist/typings/index.d.ts",
29+
"default": "./dist/esm/index.js"
30+
}
31+
},
32+
"./*": {
33+
"require": {
34+
"types": "./dist/typings/*.d.cts",
35+
"default": "./dist/cjs/*.js"
36+
},
37+
"import": {
38+
"types": "./dist/typings/*.d.ts",
39+
"default": "./dist/esm/*.js"
40+
},
41+
"default": {
42+
"types": "./dist/typings/*.d.ts",
43+
"default": "./dist/esm/*.js"
44+
}
45+
},
46+
"./package.json": "./package.json"
47+
},
48+
"typings": "dist/typings/index.d.ts",
49+
"peerDependencies": {
50+
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0",
51+
"graphql-yoga": "workspace:^"
52+
},
53+
"dependencies": {
54+
"@graphql-tools/federation": "^2.0.0",
55+
"tslib": "^2.5.0"
56+
},
57+
"devDependencies": {
58+
"graphql": "16.8.1",
59+
"graphql-yoga": "workspace:^",
60+
"typescript": "5.1.3"
61+
},
62+
"publishConfig": {
63+
"directory": "dist",
64+
"access": "public"
65+
},
66+
"sideEffects": false,
67+
"buildOptions": {
68+
"input": "./src/index.ts"
69+
},
70+
"typescript": {
71+
"definition": "dist/typings/index.d.ts"
72+
}
73+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { Plugin } from 'graphql-yoga';
2+
import { SupergraphSchemaManager, SupergraphSchemaManagerOptions } from '@graphql-tools/federation';
3+
4+
export type ManagedFederationPluginOptions = SupergraphSchemaManagerOptions;
5+
6+
export type Logger = {
7+
info: (message: string, ...args: unknown[]) => void;
8+
error: (message: string, error?: unknown, ...args: unknown[]) => void;
9+
};
10+
11+
export function useManagedFederation(options: ManagedFederationPluginOptions = {}): Plugin {
12+
const supergraphManager = new SupergraphSchemaManager(options);
13+
14+
// Start as soon as possible to minimize the wait time of the first schema loading
15+
supergraphManager.start();
16+
17+
const plugin: Plugin = {
18+
onPluginInit({ setSchema }) {
19+
if (supergraphManager.schema) {
20+
setSchema(supergraphManager.schema);
21+
} else {
22+
// Wait for the first schema to be loaded before before allowing requests to be parsed
23+
// We can then remove the onRequestParse hook to avoid async cost on every request
24+
const waitForInitialization = new Promise<void>(resolve => {
25+
supergraphManager.once('schema', () => {
26+
plugin.onRequestParse = undefined;
27+
resolve();
28+
});
29+
});
30+
plugin.onRequestParse = async () => {
31+
await waitForInitialization;
32+
};
33+
}
34+
supergraphManager.on('schema', setSchema);
35+
},
36+
};
37+
38+
return plugin;
39+
}

0 commit comments

Comments
 (0)