Untitled
unknown
plain_text
10 days ago
5.9 kB
7
Indexable
Never
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 };
Leave a Comment