Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/server/src/ApolloServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import { UnreachableCaseError } from './utils/UnreachableCaseError.js';
import { computeCoreSchemaHash } from './utils/computeCoreSchemaHash.js';
import { isDefined } from './utils/isDefined.js';
import { SchemaManager } from './utils/schemaManager.js';
import { GraphQLExecutor } from './externalTypes/requestPipeline.js';

const NoIntrospection: ValidationRule = (context: ValidationContext) => ({
Field(node) {
Expand Down Expand Up @@ -184,6 +185,7 @@ export interface ApolloServerInternals<TContext extends BaseContext> {
stringifyResult: (
value: FormattedExecutionResult,
) => string | Promise<string>;
customExecutor?: GraphQLExecutor;
}

function defaultLogger(): Logger {
Expand Down Expand Up @@ -330,6 +332,7 @@ export class ApolloServer<in out TContext extends BaseContext = BaseContext> {
stopOnTerminationSignals: config.stopOnTerminationSignals,

gatewayExecutor: null, // set by _start
customExecutor: config.customExecutor,

csrfPreventionRequestHeaders:
config.csrfPrevention === true || config.csrfPrevention === undefined
Expand Down
3 changes: 3 additions & 0 deletions packages/server/src/externalTypes/constructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type { GatewayInterface } from '@apollo/server-gateway-interface';
import type { ApolloServerPlugin } from './plugins.js';
import type { BaseContext } from './index.js';
import type { GraphQLExperimentalIncrementalExecutionResults } from '../incrementalDeliveryPolyfill.js';
import { GraphQLExecutor } from './requestPipeline.js';

export type DocumentStore = KeyValueCache<DocumentNode>;

Expand Down Expand Up @@ -118,6 +119,8 @@ interface ApolloServerOptionsBase<TContext extends BaseContext> {

// For testing only.
__testing_incrementalExecutionResults?: GraphQLExperimentalIncrementalExecutionResults;

customExecutor?: GraphQLExecutor;
}

export interface ApolloServerOptionsWithGateway<TContext extends BaseContext>
Expand Down
12 changes: 12 additions & 0 deletions packages/server/src/externalTypes/requestPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,15 @@ export type GraphQLRequestContextDidEncounterSubsequentErrors<
export type GraphQLRequestContextWillSendSubsequentPayload<
TContext extends BaseContext,
> = GraphQLRequestContextWillSendResponse<TContext>;

export type GraphQLExecutionResult = {
data?: Record<string, any> | null;
errors?: ReadonlyArray<GraphQLError>;
extensions?: Record<string, any>;
};

export type GraphQLExecutor<
TContext extends BaseContext = Record<string, any>,
> = (
requestContext: GraphQLRequestContextExecutionDidStart<TContext>,
) => Promise<GraphQLExecutionResult>;
13 changes: 13 additions & 0 deletions packages/server/src/plugin/cacheControl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ export interface ApolloServerPluginCacheControlOptions {
* delivery response, and the body contains no errors.
*/
calculateHttpHeaders?: boolean | 'if-cacheable';

/**
* Disables instrumenting all fields for dynamic cache control hints. This is
* a pretty big significant performant boost, especially the larger a response
* is.
*/
staticOnly?: boolean;

// For testing only.
__testing__cacheHints?: Map<string, CacheHint>;
}
Expand Down Expand Up @@ -125,10 +133,15 @@ export function ApolloServerPluginCacheControl(

const defaultMaxAge: number = options.defaultMaxAge ?? 0;
const calculateHttpHeaders = options.calculateHttpHeaders ?? true;
const staticOnly = options.staticOnly ?? false;
const { __testing__cacheHints } = options;

return {
async executionDidStart() {
if (staticOnly) {
return {};
}

// Did something set the overall cache policy before we've even
// started? If so, consider that as an override and don't touch it.
// Just put set up fake `info.cacheControl` objects and otherwise
Expand Down
4 changes: 3 additions & 1 deletion packages/server/src/plugin/subscriptionCallback/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ function isAsyncIterable<T>(value: any): value is AsyncIterable<T> {
return value && typeof value[Symbol.asyncIterator] === 'function';
}

type RetryResponse = Response | Error;

interface SubscriptionObject {
cancelled: boolean;
asyncIter: AsyncGenerator<ExecutionResult, void, void>;
Expand Down Expand Up @@ -265,7 +267,7 @@ class SubscriptionManager {
`Sending \`${action}\` request to router` + maybeWithErrors,
id,
);
return retry<Response, Error>(
return retry<RetryResponse>(
async (bail) => {
response = fetch(url, {
method: 'POST',
Expand Down
3 changes: 3 additions & 0 deletions packages/server/src/requestPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ export async function processGraphQLRequest<TContext extends BaseContext>(
makeGatewayGraphQLRequestContext(requestContext, server, internals),
);
return { singleResult: result };
} else if (internals.customExecutor) {
const result = await internals.customExecutor(requestContext);
return { singleResult: result };
} else {
const resultOrResults = await executeIncrementally({
schema: schemaDerivedData.schema,
Expand Down