import type { ApolloServer, BaseContext, ContextFunction, } from '@apollo/server'; import type { WithRequired } from '@apollo/utils.withrequired'; import type { Context, Handler } from 'aws-lambda'; import type { LambdaResponse, MiddlewareFn } from './middleware'; import type { RequestHandler, RequestHandlerEvent, RequestHandlerResult, } from './request-handlers/_create'; export interface LambdaContextFunctionArgument< RH extends RequestHandler, > { event: RH extends RequestHandler ? EventType : never; context: Context; } export interface LambdaHandlerOptions< RH extends RequestHandler, TContext extends BaseContext, > { middleware?: Array>; context?: ContextFunction<[LambdaContextFunctionArgument], TContext>; } export type LambdaHandler> = Handler< RequestHandlerEvent, RequestHandlerResult >; export function startServerAndCreateLambdaHandler< RH extends RequestHandler, >( server: ApolloServer, handler: RH, options?: LambdaHandlerOptions, ): LambdaHandler; export function startServerAndCreateLambdaHandler< RH extends RequestHandler, TContext extends BaseContext, >( server: ApolloServer, handler: RH, options: WithRequired, 'context'>, ): LambdaHandler; export function startServerAndCreateLambdaHandler< RH extends RequestHandler, TContext extends BaseContext, >( server: ApolloServer, handler: RH, options?: LambdaHandlerOptions, ): LambdaHandler { server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests(); // This `any` is safe because the overload above shows that context can // only be left out if you're using BaseContext as your context, and {} is a // valid BaseContext. const defaultContext: ContextFunction< [LambdaContextFunctionArgument], any > = async () => ({}); const contextFunction: ContextFunction< [LambdaContextFunctionArgument], TContext > = options?.context ?? defaultContext; return async function (event, context) { const resultMiddlewareFns: Array>> = []; try { for (const middlewareFn of options?.middleware ?? []) { const middlewareReturnValue = await middlewareFn(event); // If the middleware returns an object, we assume it's a LambdaResponse if ( typeof middlewareReturnValue === 'object' && middlewareReturnValue !== null ) { return middlewareReturnValue; } // If the middleware returns a function, we assume it's a result callback if (middlewareReturnValue) { resultMiddlewareFns.push(middlewareReturnValue); } } const httpGraphQLRequest = handler.fromEvent(event); const response = await server.executeHTTPGraphQLRequest({ httpGraphQLRequest, context: () => { return contextFunction({ event, context, }); }, }); const result = handler.toSuccessResult(response); for (const resultMiddlewareFn of resultMiddlewareFns) { await resultMiddlewareFn(result); } return result; } catch (e) { const result = handler.toErrorResult(e); for (const resultMiddlewareFn of resultMiddlewareFns) { await resultMiddlewareFn(result); } return result; } }; }