Automatisierung der GraphQL-Schemavalidierung mit @graphql-tools und @graphql-inspector
In der modernen Webentwicklung hat sich GraphQL zu einem leistungsstarken Werkzeug für das API-Design entwickelt, das Flexibilität und Effizienz bietet. Es ist jedoch entscheidend, dass die Abfragen in deiner Frontend-Anwendung aktuell und nicht veraltet sind, während sich deine API weiterentwickelt.

Im obigen Beispiel sehen wir, dass das GraphQL-Schema eine veraltete Abfrage und ein veraltetes Feld enthält. Folglich könnte unsere Frontend-Anwendung beeinträchtigt werden und unsere Apps könnten nicht mehr aktuell sein.
Dieser Blogbeitrag zeigt dir, wie du die Validierung deiner GraphQL-Abfragen mit @graphql-tools und GraphQL Inspector automatisierst. Durch die Implementierung dieser Validierung kannst du die Integrität deiner Anwendung aufrechterhalten und sicherstellen, dass deine Abfragen stets mit den neuesten Schemaänderungen kompatibel sind.
Du kannst das offizielle Repository mit dem vollständigen Code
Überlegungen
Bevor du mit der Implementierung beginnst, solltest du die folgenden Schritte zum Einrichten deines Projekts berücksichtigen:
Projekteinrichtung:
Unabhängig davon, ob du React, Flutter oder ein anderes Framework für deine Frontend-Anwendung verwendest, erstelle einen separaten Ordner für das Validierungsskript.
Erstelle ein neues Verzeichnis namens
script
innerhalb deines Projekts.
Initialisieren eines Node.js-Projekts:
Navigiere zum Verzeichnis
script
und initialisiere ein neues Node.js-Projekt:
cd script;
npm init -y;
Erstelle eine Datei namens
validaton-script.js
Abhängigkeiten installieren:
Installiere die erforderlichen Abhängigkeiten:
npm install @graphql-inspector/core @graphql-tools/load @graphql-tools/url-loader fs graphql-tag path
Schema und Abfragen:
Stelle sicher, dass dein GraphQL-Schema von einem Endpunkt aus zugänglich ist und dass du deine GraphQL-Abfragen in einer Datei definiert hast.
React-Dateibeispiel:
const GET_USER = gql`
query getUser($id: Int!) {
getUser(id: $id) {
id
name
}
}
`;
Flutter-Dateibeispiel:
class GraphQLAPIDefinitions {
static const getUser = r'''
query getUser($id: Int!){
getUser(id: $id) {
id
name
}
}
''';
}
Nachdem du diese Schritte abgeschlossen hast, hast du deine Umgebung eingerichtet und kannst das Validierungsskript erstellen und ausführen.
Validierungsskript
Nun lass uns in den Prozess des Schreibens des Skripts eintauchen, um die Validierung unserer GraphQL-Abfragen zu automatisieren.
Werkzeuge sammeln:
Zuerst müssen wir die wesentlichen Node.js-Module und -Bibliotheken für unser Skript sammeln. Wir verwenden
@graphql-tools/load
und@graphql-tools/url-loader
, um das GraphQL-Schema zu laden,graphql-inspector
, um es zu validieren,fs.promises
undpath
, um mit dem Dateisystem zu arbeiten undgraphql-tag
, um die GraphQL-Abfragen zu parsen.
const { loadSchema } = require("@graphql-tools/load");
const { UrlLoader } = require("@graphql-tools/url-loader");
const { validate } = require("@graphql-inspector/core");
const fs = require("fs").promises;
const path = require("path");
const gql = require("graphql-tag");
Szenario einrichten:
Als nächstes definieren wir die für unsere Aufgabe benötigten Konstanten. GRAPHQL_ENDPOINT gibt die URL an unter der sich unser GraphQL-Schema befindet. DEFINITIONS_FILE_PATH gibt den Pfad zu unserer Datei mit den GraphQL-Abfragedefinitionen an.
const GRAPHQL_ENDPOINT = "https://your-url/graphql";
const DEFINITIONS_FILE_PATH = path.join(
__dirname,
"your-graphql-file-definitions-path"
);
Schema laden:
Die Funktion loadSchemaFromEndpoint ist dafür verantwortlich, das Schema vom angegebenen Endpunkt mithilfe von UrlLoader zu laden.
async function loadSchemaFromEndpoint(endpoint) {
return loadSchema(endpoint, {
loaders: [new UrlLoader()],
});
}
Definitionen lesen:
Die Funktion
readDefinitionsFromFile
liest die Abfragedefinitionen aus einer Datei. Dies ist entscheidend, da sie die in der Frontend-Anwendung verwendeten GraphQL-Abfragen enthält.
async function readDefinitionsFromFile(filePath) {
return fs.readFile(filePath, "utf-8");
}
Abfragen extrahieren:
extractQueriesFromDefinitions
verwendet einen regulären Ausdruck, um Abfragen aus der Definitionsdatei zu extrahieren. Jede Abfrage wird geparst und zur Validierung vorbereitet.
function extractQueriesFromDefinitions(data) {
const regex = /(query|mutation)\s+[a-zA-Z0-9_]+\s*\([^)]*\)\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/gm;
const sources = [];
for (const match of data.matchAll(regex)) {
const query = match[0];
const parsedQuery = gql`${query}`;
const queryName = parsedQuery.definitions[0].name?.value;
sources.push({ ...parsedQuery.loc.source, name: queryName });
}
return sources;
}
Falls du die komplexeren Abfragen verarbeiten musst, passe den regulären Ausdruck entsprechend an deine Bedürfnisse an.
Validieren und Protokollieren:
logAndExit
verarbeitet die Validierungsantwort. Es filtert veraltete Felder, protokolliert sie und beendet den Prozess mit einem Erfolgs- oder Fehlercode basierend auf den Ergebnissen.
function logAndExit(response) {
const deprecatedFields = response
.filter((item) => item.deprecated.length > 0)
.map((item) => {
item.deprecated.forEach((itemDeprecated) => {
console.log(
`Deprecated fields on: ${item.source.name || ""}`,
itemDeprecated.message
);
});
return item.deprecated;
})
.flat();
process.exit(deprecatedFields.length > 0 ? 1 : 0);
}
Skript ausführen:
In der main-Funktion koordinieren wir den gesamten Prozess. Wir beginnen mit dem Laden des Schemas vom Endpunkt, lesen dann die Abfragedefinitionen aus der Datei, extrahieren die Abfragen und validieren sie gegen das Schema. Die Ergebnisse werden protokolliert und der Prozess wird basierend auf dem Validierungsergebnis beendet.
async function main() {
try {
const schema = await loadSchemaFromEndpoint(GRAPHQL_ENDPOINT);
const definitionsData = await readDefinitionsFromFile(DEFINITIONS_FILE_PATH);
const sources = extractQueriesFromDefinitions(definitionsData);
const response = validate(schema, sources, {
strictDeprecated: true,
});
logAndExit(response);
} catch (error) {
console.error("An error occurred:", error);
process.exit(1);
}
}
Schließlich rufen wir die main-Funktion auf, um den Validierungsprozess zu starten.
main();
Wir können es mit dem folgenden Befehl testen:
node graphql-validator/graphql-validator-script.js

Vollständiger Code:
const { loadSchema } = require("@graphql-tools/load");
const { UrlLoader } = require("@graphql-tools/url-loader");
const { validate } = require("@graphql-inspector/core");
const fs = require("fs").promises;
const path = require("path");
const gql = require("graphql-tag");
const GRAPHQL_ENDPOINT = "http://localhost:3000/graphql";
const DEFINITIONS_FILE_PATH = path.join(
__dirname,
"./graphql-definitions.txt"
);
async function loadSchemaFromEndpoint(endpoint) {
return loadSchema(endpoint, {
loaders: [new UrlLoader()],
});
}
async function readDefinitionsFromFile(filePath) {
return fs.readFile(filePath, "utf-8");
}
function extractQueriesFromDefinitions(data) {
const regex = /(query|mutation)\s+[a-zA-Z0-9_]+\s*\([^)]*\)\s*\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/gm;
const sources = [];
for (const match of data.matchAll(regex)) {
const query = match[0];
const parsedQuery = gql`${query}`;
const queryName = parsedQuery.definitions[0].name?.value;
sources.push({ ...parsedQuery.loc.source, name: queryName });
}
return sources;
}
function logAndExit(response) {
const deprecatedFields = response
.filter((item) => item.deprecated.length > 0)
.map((item) => {
item.deprecated.forEach((itemDeprecated) => {
console.log(
`Deprecated fields on: ${item.source.name || ""}`,
itemDeprecated.message
);
});
return item.deprecated;
})
.flat();
process.exit(deprecatedFields.length > 0 ? 1 : 0);
}
async function main() {
try {
const schema = await loadSchemaFromEndpoint(GRAPHQL_ENDPOINT);
const definitionsData = await readDefinitionsFromFile(DEFINITIONS_FILE_PATH);
const sources = extractQueriesFromDefinitions(definitionsData);
const response = validate(schema, sources, {
strictDeprecated: true,
});
logAndExit(response);
} catch (error) {
console.error("An error occurred:", error);
process.exit(1);
}
}
main();
Verwendung der Pipeline zur Automatisierung
Integriere das Validierungsskript in deine CI/CD-Pipeline, um die Schema-Validierung zu automatisieren. Hier ist ein Beispiel mit GitHub Actions:
Erstellen Sie eine Workflow-Datei:
Füge eine Datei
.github/workflows/schema-validation.yml
zu deinem Repository hinzu.
Definieren Sie den Workflow:
name: GraphQL Schema Validation
on: [pull_request]
jobs:
validate-schema:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- run: cd graphql-validator && npm install
- run: node graphql-validator/graphql-validator-script.js
Mit diesem Setup wird bei jedem Push und Pull Request die Schema-Validierung ausgelöst, wodurch sichergestellt wird, dass alle Änderungen an deinem Schema sofort validiert werden und Fehler frühzeitig im Entwicklungsprozess erkannt werden.
Schlussfolgerung
Die Automatisierung der Validierung deiner GraphQL-Abfragen ist ein entscheidender Schritt zur Aufrechterhaltung der Integrität deiner Frontend-Anwendungen. Durch die Nutzung von @graphql-tools und GraphQL Inspector kannst du sicherstellen, dass deine Abfragen mit dem sich entwickelnden Schema kompatibel bleiben. Diese Automatisierung spart nicht nur Zeit, sondern erhöht auch die Zuverlässigkeit deiner Anwendung. Die Integration dieser Validierung in deine CI/CD-Pipeline stellt sicher, dass alle Probleme frühzeitig erkannt werden, was deinen Entwicklungsprozess reibungslos und effizient gestaltet