Untitled
unknown
plain_text
a year ago
5.9 kB
13
Indexable
import Website from "@src/structure/Website.js";
import { LogType } from "@src/utils/logger.js";
import { readdir } from "fs/promises";
import p from "path";
import { Request, Response, NextFunction } from "express";
import { fileURLToPath, pathToFileURL } from "url";
import Server from "./Server.js";
import { buildSubgraphSchema } from "@apollo/subgraph";
import { GraphQLSchema } from "graphql";
import { ApolloGateway, IntrospectAndCompose } from "@apollo/gateway";
import { useServer } from "graphql-ws/lib/use/ws";
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import { execute, subscribe } from "graphql";
import { exec } from "child_process";
import fs from "fs/promises";
import { printSchema } from 'graphql';
import { getStitchedSchemaFromSupergraphSdl } from '@graphql-tools/federation'
interface Params {
server: Server;
website: Website;
}
interface Context {
req?: Request;
server: Server;
website: Website;
}
interface WsContext {
req?: Request;
server: Server;
website: Website;
}
class GraphQL {
server: Server;
website: Website;
subgraphs: Map<string, GraphQLSchema>;
services: { name: string; url: string, schemaSDLPath: string }[];
dir: string;
constructor(website: Website) {
this.website = website;
this.server = this.website.server;
this.subgraphs = new Map();
this.services = [];
this.dir = p.join(p.dirname(fileURLToPath(import.meta.url)), "..",
"graphql",)
}
async context(...d: any): Promise<Context> {
return { server: this.server, website: this.website, ...d };
}
async wsContext(...d: any): Promise<WsContext> {
console.log(d);
return { server: this.server, website: this.website, ...d };
}
async loadSubGraphs() {
const subgraphDirectory = p.join(
this.dir,
"subgraphs"
);
const subgraphNames = await readdir(subgraphDirectory);
for (const subgraphName of subgraphNames) {
const schemaModule = await import(
pathToFileURL(p.join(subgraphDirectory, subgraphName, "schema.js"))
.href
).catch(() => null);
const params: Params = {
server: this.server,
website: this.website,
};
const schema =
schemaModule && schemaModule.default && schemaModule.default(params);
if (schema && schema instanceof GraphQLSchema) {
this.subgraphs.set(subgraphName, schema);
this.website.logger.log(
LogType.Info,
`Loaded subgraph ${subgraphName}.`
);
} else {
this.website.logger.log(
LogType.Error,
`Failed to load subgraph ${subgraphName}.`
);
}
}
}
async startSubgraphs() {
const subgraphDirectory = p.join(
this.dir,
"subgraphs"
);
for (const [subgraphName, subgraph] of this.subgraphs.entries()) {
const schema = subgraph;
const path = `/${subgraphName}/graphql`;
const httpServer = this.server.server;
// Initialize Apollo Server for HTTP requests
const server = new ApolloServer<Context>({
schema,
csrfPrevention: false,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
const wsServer = this.server.wsServer.createNamespace(path);
useServer(
{
schema,
execute,
subscribe,
context: this.wsContext.bind(this),
},
wsServer
);
this.server.express.use(
path,
expressMiddleware(server, {
context: this.context.bind(this),
})
);
const schemaSDLPath = p.join(subgraphDirectory, subgraphName, "schema.graphql");
await fs.writeFile(
schemaSDLPath,
printSchema(schema)
);
this.services.push({
name: subgraphName,
url: this.server.website.config.apiDomain + path,
schemaSDLPath,
});
this.website.logger.log(
LogType.Info,
`Initialized service ${subgraphName} on ${path} route.`
);
this.website.logger.log(
LogType.Info,
`Initialized service websocket ${subgraphName} on ${path} route.`
);
}
}
async loadGateway() {
const subgraphs = Array.from(this.subgraphs.values());
const services = this.services;
const httpServer = this.server.server;
const gateway = new ApolloGateway({
supergraphSdl: new IntrospectAndCompose({
subgraphs: services.map((service) => ({
name: service.name,
url: service.url,
})),
}),
});
const path = `/graphql`;
const server = new ApolloServer<Context>({
gateway,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
this.server.express.use(
path,
expressMiddleware(server, {
context: this.context.bind(this),
})
);
this.website.logger.log(
LogType.Info,
`Loaded gateway with with ${subgraphs.length} subgraphs on ${path} route.`
);
this.website.logger.log(
LogType.Info,
`Loaded websocket gateway with ${subgraphs.length} subgraphs on ${path} route.`
);
}
public async start(): Promise<void> {
this.website.logger.log(LogType.Info, `Starting GraphQL...`);
await this.loadSubGraphs();
await this.startSubgraphs();
await this.loadGateway();
// await this.loadGateway();
this.website.logger.log(
LogType.Ready,
`GraphQL is ready with Apollo Router.`
);
}
}
export default GraphQL;
export { Params, Context };
Editor is loading...
Leave a Comment