Skip to content

Commit

Permalink
perf(core): Implement caching of GraphqlValueTransformer type trees
Browse files Browse the repository at this point in the history
Relates to #226. The `getOutputTypeTree()` and `getOutputTypeTree()` methods have to walk potentially deeply-nested objects to construct the type trees. This change uses a WeakMap to cache the results against a given input. Benchmarks show modest speed improvements.
  • Loading branch information
michaelbromley committed Dec 13, 2019
1 parent 3c33f33 commit ffe47b1
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
12 changes: 12 additions & 0 deletions packages/core/src/api/common/graphql-value-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export type TypeTreeNode = {
* This class is used to transform the values of input variables or an output object.
*/
export class GraphqlValueTransformer {
private outputCache = new WeakMap<DocumentNode, TypeTree>();
private inputCache = new WeakMap<OperationDefinitionNode, TypeTree>();
constructor(private schema: GraphQLSchema) {}

/**
Expand All @@ -58,6 +60,10 @@ export class GraphqlValueTransformer {
* Constructs a tree of TypeTreeNodes for the output of a GraphQL operation.
*/
getOutputTypeTree(document: DocumentNode): TypeTree {
const cached = this.outputCache.get(document);
if (cached) {
return cached;
}
const typeInfo = new TypeInfo(this.schema);
const typeTree: TypeTree = {
operation: {} as any,
Expand Down Expand Up @@ -113,13 +119,18 @@ export class GraphqlValueTransformer {
for (const operation of document.definitions) {
visit(operation, visitWithTypeInfo(typeInfo, visitor));
}
this.outputCache.set(document, typeTree);
return typeTree;
}

/**
* Constructs a tree of TypeTreeNodes for the input variables of a GraphQL operation.
*/
getInputTypeTree(definition: OperationDefinitionNode): TypeTree {
const cached = this.inputCache.get(definition);
if (cached) {
return cached;
}
const typeInfo = new TypeInfo(this.schema);
const typeTree: TypeTree = {
operation: {} as any,
Expand Down Expand Up @@ -167,6 +178,7 @@ export class GraphqlValueTransformer {
},
};
visit(definition, visitWithTypeInfo(typeInfo, visitor));
this.inputCache.set(definition, typeTree);
return typeTree;
}

Expand Down
17 changes: 14 additions & 3 deletions packages/core/src/api/middleware/id-interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { VariableValues } from 'apollo-server-core';
import { GraphQLNamedType, OperationDefinitionNode } from 'graphql';
import { GraphQLNamedType, GraphQLSchema, OperationDefinitionNode } from 'graphql';
import { Observable } from 'rxjs';

import { GraphqlValueTransformer } from '../common/graphql-value-transformer';
Expand All @@ -25,19 +25,30 @@ type TypeTreeNode = {
*/
@Injectable()
export class IdInterceptor implements NestInterceptor {
private graphQlValueTransformers = new WeakMap<GraphQLSchema, GraphqlValueTransformer>();
constructor(private idCodecService: IdCodecService) {}

intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> {
const { isGraphQL, req } = parseContext(context);
if (isGraphQL) {
const args = GqlExecutionContext.create(context).getArgs();
const info = GqlExecutionContext.create(context).getInfo();
const graphqlValueTransformer = new GraphqlValueTransformer(info.schema);
this.decodeIdArguments(graphqlValueTransformer, info.operation, args);
const transformer = this.getTransformerForSchema(info.schema);
this.decodeIdArguments(transformer, info.operation, args);
}
return next.handle();
}

private getTransformerForSchema(schema: GraphQLSchema): GraphqlValueTransformer {
const existing = this.graphQlValueTransformers.get(schema);
if (existing) {
return existing;
}
const transformer = new GraphqlValueTransformer(schema);
this.graphQlValueTransformers.set(schema, transformer);
return transformer;
}

private decodeIdArguments(
graphqlValueTransformer: GraphqlValueTransformer,
definition: OperationDefinitionNode,
Expand Down

0 comments on commit ffe47b1

Please sign in to comment.