add complete parent-bff implementation including: - GraphQL endpoint with depth/cost validation - ChildGuard越权校验 with redis cache and singleflight - parallel orchestration with partial failure fallback - three-level cache fallback strategy (Redis + LRU + downstream) - Kafka consumer for cache invalidation and notification push - opossum circuit breaker for downstream services - Prometheus metrics and SLO alerts - Helm chart for k8s deployment with multi-environment configs - Grafana dashboard for observability - complete unit and integration tests
97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
import { createYoga, type Plugin } from "graphql-yoga";
|
||
import depthLimit from "graphql-depth-limit";
|
||
import {
|
||
createComplexityRule,
|
||
simpleEstimator,
|
||
} from "graphql-query-complexity";
|
||
import type { Request, Response } from "express";
|
||
import type { IResolvers } from "@graphql-tools/utils";
|
||
import { buildSchema } from "./schema.js";
|
||
import { buildSession } from "./context.js";
|
||
import type { GraphqlContext, ParentSession } from "./context.js";
|
||
import { env } from "../config/env.js";
|
||
import { logger } from "../shared/observability/logger.js";
|
||
import {
|
||
graphqlRequestsTotal,
|
||
graphqlDurationSeconds,
|
||
} from "../shared/observability/metrics.js";
|
||
import type { DataLoaderFactory } from "../dataloader/dataloader.factory.js";
|
||
|
||
/**
|
||
* 校验规则插件:注入深度限制 + 复杂度限制(02 §9 #5)。
|
||
*/
|
||
const validationRulesPlugin: Plugin = {
|
||
onValidate: ({ addValidationRule }) => {
|
||
addValidationRule(depthLimit(env.GRAPHQL_DEPTH_LIMIT));
|
||
addValidationRule(
|
||
createComplexityRule({
|
||
maximumComplexity: env.GRAPHQL_COST_LIMIT,
|
||
estimators: [simpleEstimator({ defaultComplexity: 1 })],
|
||
onComplete: (complexity: number) => {
|
||
logger.debug({ complexity }, "GraphQL query complexity");
|
||
},
|
||
}),
|
||
);
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 指标埋点插件:onExecute 记录开始,onExecuteDone 记录完成 + 延迟。
|
||
*/
|
||
const metricsPlugin: Plugin = {
|
||
onExecute: ({ args }) => {
|
||
const opName = args.operationName ?? "anonymous";
|
||
const startedAt = Date.now();
|
||
graphqlRequestsTotal.inc({ operation: opName, status: "started" });
|
||
|
||
return {
|
||
onExecuteDone: () => {
|
||
const elapsed = (Date.now() - startedAt) / 1000;
|
||
graphqlDurationSeconds.observe({ operation: opName }, elapsed);
|
||
graphqlRequestsTotal.inc({
|
||
operation: opName,
|
||
status: "completed",
|
||
});
|
||
},
|
||
};
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 创建 GraphQL Yoga 实例(工厂模式,注入 DataLoaderFactory + Resolvers)。
|
||
*
|
||
* 对齐 02-architecture-design.md §1.1 + §9 #1 + §9 #5。
|
||
*
|
||
* 每次 GraphQL 请求构建 per-request context,包含:
|
||
* - ParentSession(从 x-user-id 等头解析)
|
||
* - Loaders(per-request DataLoader 实例,防 N+1)
|
||
*/
|
||
export function createYogaInstance(
|
||
loaderFactory: DataLoaderFactory,
|
||
resolvers: IResolvers,
|
||
) {
|
||
const schema = buildSchema(resolvers);
|
||
|
||
return createYoga<{
|
||
req: Request;
|
||
res: Response;
|
||
session: ParentSession;
|
||
}>({
|
||
schema,
|
||
graphqlEndpoint: "/graphql",
|
||
graphiql:
|
||
env.NODE_ENV === "development" && env.GRAPHQL_INTROSPECTION_ENABLED,
|
||
|
||
context: ({ req }): GraphqlContext => {
|
||
const session = buildSession(req);
|
||
const loaders = loaderFactory.createLoaders();
|
||
return { session, req, res: req.res as Response, loaders };
|
||
},
|
||
|
||
plugins: [validationRulesPlugin, metricsPlugin],
|
||
});
|
||
}
|
||
|
||
/** Yoga 实例类型(供 GraphqlModule provider 类型推导) */
|
||
export type YogaInstance = ReturnType<typeof createYogaInstance>;
|