@0xobelisk/graphql-server
Version:
Tookit for interacting with dubhe graphql server
183 lines (148 loc) • 6.27 kB
text/typescript
import { QueryFilterPlugin } from './query-filter';
import { SimpleNamingPlugin } from './simple-naming';
import { AllFieldsFilterPlugin } from './all-fields-filter-plugin';
import { createEnhancedPlayground } from './enhanced-playground';
import ConnectionFilterPlugin from 'postgraphile-plugin-connection-filter';
import PgSimplifyInflectorPlugin from '@graphile-contrib/pg-simplify-inflector';
import { makePluginHook } from 'postgraphile';
import PgPubSub from '@graphile/pg-pubsub';
export interface PostGraphileConfigOptions {
port: string | number;
nodeEnv: string;
graphqlEndpoint: string;
enableSubscriptions: string;
enableCors: string;
databaseUrl: string;
availableTables: string[];
// Additional configuration from CLI
disableQueryLog: boolean;
enableQueryLog: boolean;
queryTimeout: number;
}
// Create PostGraphile configuration
export function createPostGraphileConfig(options: PostGraphileConfigOptions) {
const { port, nodeEnv, graphqlEndpoint, enableSubscriptions, enableCors, availableTables } =
options;
// Build GraphQL and WebSocket endpoint URLs
const baseUrl = `http://localhost:${port}`;
const _graphqlUrl = `${baseUrl}${graphqlEndpoint}`;
const _subscriptionUrl =
enableSubscriptions === 'true' ? `ws://localhost:${port}${graphqlEndpoint}` : undefined;
// Create plugin hook to support WebSocket and subscriptions
const pluginHook = makePluginHook([PgPubSub]);
const config = {
// Basic configuration - disable default GraphiQL
graphiql: false,
enhanceGraphiql: false,
showErrorStack: nodeEnv === 'development',
extendedErrors: nodeEnv === 'development' ? ['hint', 'detail', 'errcode'] : [],
// Feature configuration - enable subscriptions
subscriptions: enableSubscriptions === 'true',
live: enableSubscriptions === 'true', // Enable live functionality to support subscriptions
enableQueryBatching: true,
enableCors: enableCors === 'true',
// Add plugin hook to support WebSocket
pluginHook,
// Disable all mutation functionality - only keep queries and subscriptions
disableDefaultMutations: true,
// Schema configuration
dynamicJson: true,
setofFunctionsContainNulls: false,
ignoreRBAC: false,
ignoreIndexes: true,
// Log control configuration
// Control SQL query logs through CLI parameters
disableQueryLog:
options.disableQueryLog || (nodeEnv === 'production' && !options.enableQueryLog),
// Enable query execution plan explanation (development environment only)
allowExplain: nodeEnv === 'development',
// Monitor PostgreSQL changes (development environment only)
watchPg: nodeEnv === 'development',
// GraphQL query timeout setting
queryTimeout: options.queryTimeout,
// GraphQL endpoint - explicitly specify route
graphqlRoute: graphqlEndpoint,
graphiqlRoute: '/graphiql', // GraphiQL interface route
// Add custom plugins
appendPlugins: [
QueryFilterPlugin, // Must execute before SimpleNamingPlugin
PgSimplifyInflectorPlugin, // Simplify field names, remove ByXxxAndYyy suffixes
SimpleNamingPlugin, // Fixed field loss issue
ConnectionFilterPlugin,
AllFieldsFilterPlugin
],
// Advanced configuration options for Connection Filter plugin
graphileBuildOptions: {
// Enable logical operators (and, or, not)
connectionFilterLogicalOperators: true,
// Enable relationship filtering
connectionFilterRelations: true,
// Enable computed column filtering
connectionFilterComputedColumns: true,
// Enable array filtering
connectionFilterArrays: true,
// Enable function filtering
connectionFilterSetofFunctions: true,
// Allow null input and empty object input
connectionFilterAllowNullInput: true,
connectionFilterAllowEmptyObjectInput: true
},
// Only include detected tables
includeExtensionResources: false,
// Exclude unnecessary tables
ignoreTable: (tableName: string) => {
// If no tables detected, allow all tables
if (availableTables.length === 0) {
return false;
}
// Otherwise only include detected tables
return !availableTables.includes(tableName);
},
// Export schema (development environment)
exportGqlSchemaPath: nodeEnv === 'development' ? 'sui-indexer-schema.graphql' : undefined
};
// If subscriptions are enabled, add additional PostgreSQL subscription configuration
if (enableSubscriptions === 'true') {
return {
...config,
// Use dedicated subscription connection pool
ownerConnectionString: options.databaseUrl,
// WebSocket configuration
websocketMiddlewares: [],
// PostgreSQL settings - optimized for long-running subscriptions
pgSettings: {
statement_timeout: '0', // No timeout for subscription queries
idle_in_transaction_session_timeout: '0', // Allow long transactions
default_transaction_isolation: 'read committed'
},
// Retry on connection failure
retryOnInitFail: true,
// Performance optimization for subscriptions
pgDefaultRole: undefined,
jwtSecret: undefined,
// Additional configuration for development environment
...(nodeEnv === 'development' && {
queryCache: false, // Disable cache for real-time data
allowExplain: true
})
};
}
return config;
}
// Export enhanced playground HTML generator
export function createPlaygroundHtml(options: PostGraphileConfigOptions): string {
const { graphqlEndpoint, enableSubscriptions, availableTables } = options;
// Use relative URLs so playground connects to the same domain as the server
const graphqlUrl = graphqlEndpoint;
const subscriptionUrl = enableSubscriptions === 'true' ? graphqlEndpoint : undefined;
return createEnhancedPlayground({
url: graphqlUrl,
subscriptionUrl,
title: 'Sui Indexer GraphQL Playground',
subtitle: `Powerful GraphQL API | ${availableTables.length} tables discovered | ${
enableSubscriptions === 'true'
? 'Real-time subscriptions supported'
: 'Real-time subscriptions disabled'
}`
})(null as any, null as any, {});
}