Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save tugascript/7c7f2df8510303bfbbd6a30d1e3bc421 to your computer and use it in GitHub Desktop.
Save tugascript/7c7f2df8510303bfbbd6a30d1e3bc421 to your computer and use it in GitHub Desktop.
Mercurius drivers with plugins
import { loadPackage } from '@nestjs/common/utils/load-package.util';
import { transformSchema } from '@nestjs/graphql';
import { BuildFederatedSchemaOptions } from '@nestjs/graphql';
import { GraphQLSchema, isObjectType, buildASTSchema } from 'graphql';
import { forEach } from 'lodash';
export function buildMercuriusFederatedSchema({
typeDefs,
resolvers,
}: BuildFederatedSchemaOptions) {
const { buildSubgraphSchema, printSubgraphSchema } = loadPackage(
'@apollo/subgraph',
'MercuriusFederation',
() => require('@apollo/subgraph'),
);
let executableSchema: GraphQLSchema = buildSubgraphSchema({
typeDefs,
resolvers,
});
const subscriptionResolvers = resolvers.Subscription;
executableSchema = transformSchema(executableSchema, (type) => {
if (isObjectType(type)) {
const isSubscription = type.name === 'Subscription';
forEach(type.getFields(), (value, key) => {
if (isSubscription && subscriptionResolvers) {
const resolver = subscriptionResolvers[key];
if (resolver && !value.subscribe) {
value.subscribe = resolver.subscribe;
}
} else if (key === '_service') {
// Workaround for https://github.com/mercurius-js/mercurius/issues/273
value.resolve = function resolve() {
return {
sdl: printSubgraphSchema(
buildASTSchema(typeDefs, {
assumeValid: true,
}),
)
.replace('type Query {', 'type Query @extends {')
.replace('type Mutation {', 'type Mutation @extends {')
.replace('type Subscription {', 'type Subscription @extends {'),
};
};
}
});
}
return type;
});
return executableSchema;
}
import { Injectable } from '@nestjs/common';
import {
AbstractGraphQLDriver,
GraphQLFederationFactory,
} from '@nestjs/graphql';
import { FastifyInstance, FastifyLoggerInstance } from 'fastify';
import { printSchema } from 'graphql';
import { IncomingMessage, Server, ServerResponse } from 'http';
import mercurius from 'mercurius';
import { MercuriusExtendedDriverConfig } from '../interfaces/mercurius-extended-driver-config.interface';
import { buildMercuriusFederatedSchema } from '../utils/build-mercurius-federated-schema.util';
import { registerMercuriusPlugins } from '../utils/register-mercurius-plugins.util';
@Injectable()
export class GraphQLFederationDriver extends AbstractGraphQLDriver<MercuriusExtendedDriverConfig> {
get instance(): FastifyInstance<
Server,
IncomingMessage,
ServerResponse,
FastifyLoggerInstance
> {
return this.httpAdapterHost?.httpAdapter?.getInstance?.();
}
constructor(
private readonly graphqlFederationFactory: GraphQLFederationFactory,
) {
super();
}
public async start(options: MercuriusExtendedDriverConfig) {
const plugins = options.plugins;
delete options.plugins;
const adapterOptions = await this.graphqlFederationFactory.mergeWithSchema(
options,
buildMercuriusFederatedSchema,
);
if (adapterOptions.definitions && adapterOptions.definitions.path) {
await this.graphQlFactory.generateDefinitions(
printSchema(adapterOptions.schema),
adapterOptions,
);
}
const httpAdapter = this.httpAdapterHost.httpAdapter;
const platformName = httpAdapter.getType();
if (platformName !== 'fastify') {
throw new Error(`No support for current HttpAdapter: ${platformName}`);
}
const app = httpAdapter.getInstance<FastifyInstance>();
await app.register(mercurius, {
...adapterOptions,
});
await registerMercuriusPlugins(app, plugins);
}
public async stop(): Promise<void> {
return;
}
}
import { MercuriusGatewayDriver } from '@nestjs/mercurius';
import { FastifyInstance } from 'fastify';
import mercurius from 'mercurius';
import { MercuriusExtendedDriverConfig } from '../interfaces/mercurius-extended-driver-config.interface';
import { registerMercuriusPlugins } from '../utils/register-mercurius-plugins.util';
export class GraphQLGatewayDriver extends MercuriusGatewayDriver {
constructor() {
super();
}
public async start(options: MercuriusExtendedDriverConfig) {
const httpAdapter = this.httpAdapterHost.httpAdapter;
const platformName = httpAdapter.getType();
if (platformName !== 'fastify') {
throw new Error(`No support for current HttpAdapter: ${platformName}`);
}
const app = httpAdapter.getInstance<FastifyInstance>();
const plugins = options.plugins;
delete options.plugins;
await app.register(mercurius, {
...options,
});
await registerMercuriusPlugins(app, plugins);
}
}
import { MercuriusDriver } from '@nestjs/mercurius';
import { FastifyInstance } from 'fastify';
import { printSchema } from 'graphql';
import mercurius from 'mercurius';
import { MercuriusExtendedDriverConfig } from '../interfaces/mercurius-extended-driver-config.interface';
import { registerMercuriusPlugins } from '../utils/register-mercurius-plugins.util';
export class GraphQLDriver extends MercuriusDriver {
constructor() {
super();
}
public async start(
mercuriusOptions: MercuriusExtendedDriverConfig,
): Promise<void> {
const options =
await this.graphQlFactory.mergeWithSchema<MercuriusExtendedDriverConfig>(
mercuriusOptions,
);
if (options.definitions && options.definitions.path) {
await this.graphQlFactory.generateDefinitions(
printSchema(options.schema),
options,
);
}
const httpAdapter = this.httpAdapterHost.httpAdapter;
const platformName = httpAdapter.getType();
if (platformName !== 'fastify') {
throw new Error(`No support for current HttpAdapter: ${platformName}`);
}
const app = httpAdapter.getInstance<FastifyInstance>();
const plugins = options.plugins;
delete options.plugins;
await app.register(mercurius, {
...options,
});
await registerMercuriusPlugins(app, plugins);
}
}
import {
FastifyPluginAsync,
FastifyPluginCallback,
FastifyPluginOptions,
FastifyRegisterOptions,
} from 'fastify';
export interface MercuriusDriverPlugin<
Options extends FastifyPluginOptions = any,
> {
plugin:
| FastifyPluginCallback<Options>
| FastifyPluginAsync<Options>
| Promise<{
default: FastifyPluginCallback<Options>;
}>;
options?: FastifyRegisterOptions<Options>;
}
import { GqlModuleOptions } from '@nestjs/graphql';
import { MercuriusOptions } from 'mercurius';
import { MercuriusDriverPlugin } from './mercurius-driver-plugin.interface';
interface MercuriusPlugins {
plugins?: MercuriusDriverPlugin[];
}
export type MercuriusExtendedDriverConfig = GqlModuleOptions &
MercuriusOptions &
MercuriusPlugins;
import { FastifyInstance } from 'fastify';
import { MercuriusDriverPlugin } from '../interfaces/mercurius-driver-plugin.interface';
export async function registerMercuriusPlugins(
app: FastifyInstance,
plugins?: MercuriusDriverPlugin[],
): Promise<void> {
if (!plugins || plugins.length === 0) return;
for (let i = 0; i < plugins.length; i++) {
const plugin = plugins[i];
await app.register(plugin.plugin, plugin.options);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment