-
Notifications
You must be signed in to change notification settings - Fork 357
/
Copy pathgraphql.module.ts
142 lines (125 loc) · 4.56 KB
/
graphql.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2020-2022 OnFinality Limited authors & contributors
// SPDX-License-Identifier: Apache-2.0
import PgPubSub from '@graphile/pg-pubsub';
import {Module, OnModuleDestroy, OnModuleInit} from '@nestjs/common';
import {HttpAdapterHost} from '@nestjs/core';
import {delay} from '@subql/common';
import {
ApolloServerPluginCacheControl,
ApolloServerPluginLandingPageDisabled,
ApolloServerPluginLandingPageGraphQLPlayground,
} from 'apollo-server-core';
import {ApolloServer} from 'apollo-server-express';
import ExpressPinoLogger from 'express-pino-logger';
import {execute, GraphQLSchema, subscribe} from 'graphql';
import {Pool} from 'pg';
import {makePluginHook} from 'postgraphile';
import {getPostGraphileBuilder, PostGraphileCoreOptions} from 'postgraphile-core';
import {SubscriptionServer} from 'subscriptions-transport-ws';
import {Config} from '../configure';
import {getLogger, PinoConfig} from '../utils/logger';
import {getYargsOption} from '../yargs';
import {plugins} from './plugins';
import {PgSubscriptionPlugin} from './plugins/PgSubscriptionPlugin';
import {queryComplexityPlugin} from './plugins/QueryComplexityPlugin';
import {ProjectService} from './project.service';
const {argv} = getYargsOption();
const logger = getLogger('graphql-module');
const SCHEMA_RETRY_INTERVAL = 10; //seconds
const SCHEMA_RETRY_NUMBER = 5;
@Module({
providers: [ProjectService],
})
export class GraphqlModule implements OnModuleInit, OnModuleDestroy {
private apolloServer: ApolloServer;
constructor(
private readonly httpAdapterHost: HttpAdapterHost,
private readonly config: Config,
private readonly pgPool: Pool,
private readonly projectService: ProjectService
) {}
async onModuleInit(): Promise<void> {
if (!this.httpAdapterHost) {
return;
}
try {
this.apolloServer = await this.createServer();
} catch (e) {
throw new Error(`create apollo server failed, ${e.message}`);
}
}
async onModuleDestroy(): Promise<void> {
return this.apolloServer?.stop();
}
private async buildSchema(
dbSchema: string,
options: PostGraphileCoreOptions,
retries: number
): Promise<GraphQLSchema> {
if (retries > 0) {
try {
const builder = await getPostGraphileBuilder(this.pgPool, [dbSchema], options);
const graphqlSchema = builder.buildSchema();
return graphqlSchema;
} catch (e) {
await delay(SCHEMA_RETRY_INTERVAL);
if (retries === 1) {
logger.error(e);
}
return this.buildSchema(dbSchema, options, --retries);
}
} else {
throw new Error(`Failed to build schema ${dbSchema} ${SCHEMA_RETRY_NUMBER} times`);
}
}
private async createServer() {
const app = this.httpAdapterHost.httpAdapter.getInstance();
const httpServer = this.httpAdapterHost.httpAdapter.getHttpServer();
const dbSchema = await this.projectService.getProjectSchema(this.config.get('name'));
let options: PostGraphileCoreOptions = {
replaceAllPlugins: plugins,
subscriptions: true,
dynamicJson: true,
};
if (argv.subscription) {
const pluginHook = makePluginHook([PgPubSub]);
// Must be called manually to init PgPubSub since we're using Apollo Server and not postgraphile
options = pluginHook('postgraphile:options', options, {pgPool: this.pgPool});
options.replaceAllPlugins.push(PgSubscriptionPlugin);
while (options.appendPlugins.length) {
options.replaceAllPlugins.push(options.appendPlugins.pop());
}
}
const schema = await this.buildSchema(dbSchema, options, SCHEMA_RETRY_NUMBER);
const apolloServerPlugins = [
ApolloServerPluginCacheControl({
defaultMaxAge: 5,
calculateHttpHeaders: true,
}),
this.config.get('playground')
? ApolloServerPluginLandingPageGraphQLPlayground()
: ApolloServerPluginLandingPageDisabled(),
queryComplexityPlugin({schema, maxComplexity: argv['query-complexity']}),
];
const server = new ApolloServer({
schema,
context: {
pgClient: this.pgPool,
},
plugins: apolloServerPlugins,
debug: this.config.get('NODE_ENV') !== 'production',
});
if (argv.subscription) {
// TODO: Replace subscriptions-transport-ws with graphql-ws when support is added to graphql-playground
SubscriptionServer.create({schema, execute, subscribe}, {server: httpServer, path: '/'});
}
app.use(ExpressPinoLogger(PinoConfig));
await server.start();
server.applyMiddleware({
app,
path: '/',
cors: true,
});
return server;
}
}