In diesem Artikel besprechen wir, wie wir Schema Stitching über mehrere Fauna-Instanzen anwenden können. Wir werden auch besprechen, wie andere GraphQL-Dienste und Datenquellen mit Fauna in einem Graphen kombiniert werden können.
Was ist Schema Stitching?
Schema Stitching ist der Prozess der Erstellung einer einzelnen GraphQL-API aus mehreren zugrunde liegenden GraphQL-APIs.
Wo ist es nützlich?
Beim Aufbau großer Anwendungen zerlegen wir oft verschiedene Funktionalitäten und Geschäftslogiken in Microservices. Dies gewährleistet die Trennung von Belangen. Es wird jedoch eine Zeit kommen, in der unsere Client-Anwendungen Daten aus mehreren Quellen abfragen müssen. Die beste Vorgehensweise ist, allen Ihren Client-Anwendungen einen einheitlichen Graphen anzubieten. Dies kann jedoch schwierig sein, da wir nicht mit einem eng gekoppelten, monolithischen GraphQL-Server enden wollen. Wenn Sie Fauna verwenden, hat jede Datenbank ihr eigenes natives GraphQL. Idealerweise möchten wir die nativen GraphQL-Funktionen von Fauna so weit wie möglich nutzen und keinen Code auf Anwendungsebene schreiben. Wenn wir jedoch mehrere Datenbanken verwenden, muss unsere Frontend-Anwendung mehrere GraphQL-Instanzen verbinden. Eine solche Anordnung schafft eine enge Kopplung. Wir möchten dies zugunsten eines einheitlichen GraphQL-Servers vermeiden.
Um diese Probleme zu lösen, können wir Schema Stitching verwenden. Schema Stitching ermöglicht es uns, mehrere GraphQL-Dienste zu einem einheitlichen Schema zu kombinieren. In diesem Artikel werden wir Folgendes besprechen:
- Kombinieren mehrerer Fauna-Instanzen zu einem GraphQL-Service
- Kombinieren von Fauna mit anderen GraphQL-APIs und Datenquellen
- Wie man ein serverloses GraphQL-Gateway mit AWS Lambda erstellt
Kombinieren mehrerer Fauna-Instanzen zu einem GraphQL-Service
Zuerst schauen wir uns an, wie wir mehrere Fauna-Instanzen zu einem GraphQL-Service kombinieren können. Stellen Sie sich vor, wir haben drei Fauna-Datenbankinstanzen: Produkt, Inventar und Bewertung. Jede ist unabhängig von den anderen. Jede hat ihren Graphen (wir werden sie als Subgraphen bezeichnen). Wir wollen eine einheitliche Graphenschnittstelle erstellen und sie für die Client-Anwendungen freigeben. Clients können jede Kombination der nachgelagerten Datenquellen abfragen.

Wir werden den einheitlichen Graphen als Gateway-Service bezeichnen. Lasst uns diesen Service erstellen.
Wir beginnen mit einem frischen Node.js-Projekt. Wir erstellen einen neuen Ordner. Navigieren Sie dann hinein und initialisieren Sie eine neue Node.js-Anwendung mit den folgenden Befehlen.
mkdir my-gateway
cd my-gateway
npm init --yes
Als Nächstes erstellen wir einen einfachen Express-GraphQL-Server. Wir installieren also die Pakete express und express-graphql mit dem folgenden Befehl.
npm i express express-graphql graphql --save
Erstellung des Gateway-Servers
Wir erstellen eine Datei namens gateway.js. Dies ist unser Haupteinstiegspunkt für die Anwendung. Wir beginnen mit der Erstellung eines sehr einfachen GraphQL-Servers.
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
const schema = buildSchema(`
type Query {
hello: String
}
`);
// The root provides a resolver function for each API endpoint
const rootValue = {
hello: () => 'Hello world!',
};
const app = express();
app.use(
'/graphql',
graphqlHTTP((req) => ({
schema,
rootValue,
graphiql: true,
})),
);
app.listen(4000);
console.log('Running a GraphQL API server at <https://:4000/graphql>');
Im obigen Code haben wir einen einfachen express-graphql-Server mit einer Beispielabfrage und einem Resolver erstellt. Testen wir unsere App, indem wir den folgenden Befehl ausführen.
node gateway.js
Navigieren Sie zu https://:4000/graphql , und Sie können mit dem GraphQL-Playground interagieren.

Erstellung von Fauna-Instanzen
Als Nächstes erstellen wir drei Fauna-Datenbanken. Jede davon wird als GraphQL-Service fungieren. Gehen wir zu fauna.com und erstellen unsere Datenbanken. Ich werde sie Produkt, Inventar und Bewertung nennen.


Sobald die Datenbanken erstellt sind, generieren wir Admin-Schlüssel für sie. Diese Schlüssel werden benötigt, um uns mit unseren GraphQL-APIs zu verbinden.

Lassen Sie uns drei verschiedene GraphQL-Schemas erstellen und sie in die jeweiligen Datenbanken hochladen. So werden unsere Schemas aussehen.
# Schema for Inventory database
type Inventory {
name: String
description: String
sku: Float
availableLocation: [String]
}
# Schema for Product database
type Product {
name: String
description: String
price: Float
}
# Schema for Review database
type Review {
email: String
comment: String
rating: Float
}
Gehen Sie zu den entsprechenden Datenbanken, wählen Sie GraphQL aus der Seitenleiste und importieren Sie die Schemas für jede Datenbank.

Jetzt haben wir drei GraphQL-Services, die auf Fauna laufen. Wir können nun über den GraphQL-Playground innerhalb von Fauna mit diesen Services interagieren. Fügen Sie gerne einige Dummy-Daten ein, wenn Sie mitmachen. Diese werden später beim Abfragen mehrerer Datenquellen nützlich sein.
Einrichtung des Gateway-Services
Als Nächstes kombinieren wir diese mit Schema Stitching zu einem Graphen. Dazu benötigen wir einen Gateway-Server. Erstellen wir eine neue Datei gateway.js. Wir werden ein paar Bibliotheken von graphql tools verwenden, um die Graphen zusammenzufügen.
Lassen Sie uns diese Abhängigkeiten auf unserem Gateway-Server installieren.
npm i @graphql-tools/schema @graphql-tools/stitch @graphql-tools/wrap cross-fetch --save
In unserem Gateway werden wir eine neue generische Funktion namens makeRemoteExecutor erstellen. Diese Funktion ist eine Factory-Funktion, die eine weitere Funktion zurückgibt. Die zurückgegebene asynchrone Funktion ruft die GraphQL-API-Abfrage auf.
// gateway.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
function makeRemoteExecutor(url, token) {
return async ({ document, variables }) => {
const query = print(document);
const fetchResult = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
body: JSON.stringify({ query, variables }),
});
return fetchResult.json();
}
}
// Construct a schema, using GraphQL schema language
const schema = buildSchema(`
type Query {
hello: String
}
`);
// The root provides a resolver function for each API endpoint
const rootValue = {
hello: () => 'Hello world!',
};
const app = express();
app.use(
'/graphql',
graphqlHTTP(async (req) => {
return {
schema,
rootValue,
graphiql: true,
}
}),
);
app.listen(4000);
console.log('Running a GraphQL API server at https://:4000/graphql');
Wie Sie oben sehen können, hat makeRemoteExecutor zwei geparste Argumente. Das Argument url gibt die Remote-GraphQL-URL an und das Argument token gibt das Autorisierungstoken an.
Wir werden eine weitere Funktion namens makeGatewaySchema erstellen. In dieser Funktion werden wir die Proxy-Aufrufe an die Remote-GraphQL-APIs mit der zuvor erstellten Funktion makeRemoteExecutor durchführen.
// gateway.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { introspectSchema } = require('@graphql-tools/wrap');
const { stitchSchemas } = require('@graphql-tools/stitch');
const { fetch } = require('cross-fetch');
const { print } = require('graphql');
function makeRemoteExecutor(url, token) {
return async ({ document, variables }) => {
const query = print(document);
const fetchResult = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
body: JSON.stringify({ query, variables }),
});
return fetchResult.json();
}
}
async function makeGatewaySchema() {
const reviewExecutor = await makeRemoteExecutor('https://graphql.fauna.com/graphql', 'fnAEQZPUejACQ2xuvfi50APAJ397hlGrTjhdXVta');
const productExecutor = await makeRemoteExecutor('https://graphql.fauna.com/graphql', 'fnAEQbI02HACQwTaUF9iOBbGC3fatQtclCOxZNfp');
const inventoryExecutor = await makeRemoteExecutor('https://graphql.fauna.com/graphql', 'fnAEQbI02HACQwTaUF9iOBbGC3fatQtclCOxZNfp');
return stitchSchemas({
subschemas: [
{
schema: await introspectSchema(reviewExecutor),
executor: reviewExecutor,
},
{
schema: await introspectSchema(productExecutor),
executor: productExecutor
},
{
schema: await introspectSchema(inventoryExecutor),
executor: inventoryExecutor
}
],
typeDefs: 'type Query { heartbeat: String! }',
resolvers: {
Query: {
heartbeat: () => 'OK'
}
}
});
}
// ...
Wir verwenden die Funktion makeRemoteExecutor, um unsere Remote-GraphQL-Executors zu erstellen. Wir haben hier drei Remote-Executors, die auf die Dienste Produkt, Inventar und Bewertung zeigen. Da es sich um eine Demoanwendung handelt, habe ich den Admin-API-Schlüssel von Fauna direkt im Code hartkodiert. Vermeiden Sie dies in einer echten Anwendung. Diese Geheimnisse sollten zu keinem Zeitpunkt im Code offengelegt werden. Verwenden Sie bitte Umgebungsvariablen oder Secret-Manager, um diese Werte zur Laufzeit abzurufen.
Wie Sie im hervorgehobenen Code oben sehen können, geben wir das Ergebnis der Funktion switchSchemas von @graphql-tools zurück. Die Funktion hat eine Argumenteigenschaft namens subschemas. In dieser Eigenschaft können wir ein Array aller Subgraphen übergeben, die wir abrufen und kombinieren möchten. Wir verwenden auch eine Funktion namens introspectSchema von graphql-tools. Diese Funktion ist dafür verantwortlich, die Anfrage vom Gateway zu transformieren und die Proxy-API-Anfrage an die nachgelagerten Dienste zu stellen.
Sie können mehr über diese Funktionen auf der Dokumentationsseite von graphql-tools erfahren.
Schließlich müssen wir makeGatewaySchema aufrufen. Wir können das zuvor hartkodierte Schema aus unserem Code entfernen und durch das gestitchte Schema ersetzen.
// gateway.js
// ...
const app = express();
app.use(
'/graphql',
graphqlHTTP(async (req) => {
const schema = await makeGatewaySchema();
return {
schema,
context: { authHeader: req.headers.authorization },
graphiql: true,
}
}),
);
// ...
Wenn wir unseren Server neu starten und zu localhost zurückkehren, sehen wir, dass Abfragen und Mutationen von allen Fauna-Instanzen in unserem GraphQL-Playground verfügbar sind.


Schreiben wir eine einfache Abfrage, die Daten aus allen Fauna-Instanzen gleichzeitig abruft.


Stitch von Drittanbieter-GraphQL-APIs
Wir können auch Drittanbieter-GraphQL-APIs in unser Gateway einfügen. Für diese Demo werden wir die offene SpaceX-GraphQL-API mit unseren Diensten kombinieren.
Der Prozess ist derselbe wie oben. Wir erstellen einen neuen Executor und fügen ihn unserem Subgraphen-Array hinzu.
// ...
async function makeGatewaySchema() {
const reviewExecutor = await makeRemoteExecutor('https://graphql.fauna.com/graphql', 'fnAEQdRZVpACRMEEM1GKKYQxH2Qa4TzLKusTW2gN');
const productExecutor = await makeRemoteExecutor('https://graphql.fauna.com/graphql', 'fnAEQdSdXiACRGmgJgAEgmF_ZfO7iobiXGVP2NzT');
const inventoryExecutor = await makeRemoteExecutor('https://graphql.fauna.com/graphql', 'fnAEQdR0kYACRWKJJUUwWIYoZuD6cJDTvXI0_Y70');
const spacexExecutor = await makeRemoteExecutor('https://api.spacex.land/graphql/')
return stitchSchemas({
subschemas: [
{
schema: await introspectSchema(reviewExecutor),
executor: reviewExecutor,
},
{
schema: await introspectSchema(productExecutor),
executor: productExecutor
},
{
schema: await introspectSchema(inventoryExecutor),
executor: inventoryExecutor
},
{
schema: await introspectSchema(spacexExecutor),
executor: spacexExecutor
}
],
typeDefs: 'type Query { heartbeat: String! }',
resolvers: {
Query: {
heartbeat: () => 'OK'
}
}
});
}
// ...
Bereitstellung des Gateways
Um dies zu einer echten serverlosen Lösung zu machen, sollten wir unser Gateway in einer serverlosen Funktion bereitstellen. Für diese Demo werde ich das Gateway in eine AWS Lambda-Funktion bereitstellen. Netlify und Vercel sind die beiden anderen Alternativen zu AWS Lambda.
Ich werde das Serverless Framework verwenden, um den Code nach AWS zu deployen. Lassen Sie uns die Abhängigkeiten dafür installieren.
npm i -g serverless # if you don't have the serverless framework installed already
npm i serverless-http body-parser --save
Als Nächstes müssen wir eine Konfigurationsdatei namens serverless.yaml erstellen.
# serverless.yaml
service: my-graphql-gateway
provider:
name: aws
runtime: nodejs14.x
stage: dev
region: us-east-1
functions:
app:
handler: gateway.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
Innerhalb der serverless.yaml definieren wir Informationen wie den Cloud-Provider, die Laufzeitumgebung und den Pfad zu unserer Lambda-Funktion. Werfen Sie gerne einen Blick in die offizielle Dokumentation des Serverless Frameworks für detailliertere Informationen.
Wir müssen einige kleinere Änderungen an unserem Code vornehmen, bevor wir ihn nach AWS deployen können.
npm i -g serverless # if you don't have the serverless framework installed already
npm i serverless-http body-parser --save
Beachten Sie den hervorgehobenen Code oben. Wir haben die Bibliothek body-parser hinzugefügt, um JSON-Bodies zu parsen. Wir haben auch die Bibliothek serverless-http hinzugefügt. Das Umschließen der Express-App-Instanz mit der serverlosen Funktion kümmert sich um die gesamte zugrunde liegende Lambda-Konfiguration.
Wir können den folgenden Befehl ausführen, um dies nach AWS Lambda zu deployen.
serverless deploy
Dies wird ein oder zwei Minuten dauern, um zu deployen. Sobald die Bereitstellung abgeschlossen ist, sehen wir die API-URL in unserem Terminal.

Stellen Sie sicher, dass Sie /graphql am Ende der generierten URL hinzufügen. (z. B. https://gy06ffhe00.execute-api.us-east-1.amazonaws.com/dev/graphql).
Da haben Sie es. Wir haben die vollständige serverlose Erlösung erreicht 😉. Wir betreiben nun drei unabhängige Fauna-Instanzen, die mit einem GraphQL-Gateway miteinander verbunden sind.
Sie können sich den Code für diesen Artikel hier ansehen.
Fazit
Schema Stitching ist eine der beliebtesten Lösungen, um Monolithen aufzubrechen und eine Trennung der Zuständigkeiten zwischen Datenquellen zu erreichen. Es gibt jedoch auch andere Lösungen wie Apollo Federation, die im Grunde genauso funktionieren. Wenn Sie einen Artikel wie diesen mit Apollo Federation sehen möchten, lassen Sie es uns bitte im Kommentarbereich wissen. Das war's für heute, bis zum nächsten Mal.