feat: upgraded apollo-server-express, logging extension (#2058)

This commit is contained in:
Wyatt Johnson
2018-11-01 22:05:04 +00:00
committed by GitHub
parent b3cfe32631
commit 7aa9a6b4f7
7 changed files with 465 additions and 82 deletions
+11 -3
View File
@@ -1,4 +1,7 @@
import uuid from "uuid";
import { Config } from "talk-common/config";
import logger from "talk-server/logger";
import { User } from "talk-server/models/user";
import { Request } from "talk-server/types/express";
@@ -9,9 +12,14 @@ export interface CommonContextOptions {
}
export default class CommonContext {
public user?: User;
public req?: Request;
public config: Config;
public readonly user?: User;
public readonly req?: Request;
public readonly config: Config;
public readonly logger = logger.child({
context: "graph",
contextID: uuid.v4(),
});
constructor({ user, req, config }: CommonContextOptions) {
this.user = user;
@@ -0,0 +1,42 @@
import { formatApolloErrors } from "apollo-server-errors";
import { GraphQLError } from "graphql";
import { GraphQLExtension, GraphQLResponse } from "graphql-extensions";
import CommonContext from "talk-server/graph/common/context";
export class LoggerExtension implements GraphQLExtension<CommonContext> {
private logError = (ctx: CommonContext) => (err: Error) => {
if (err instanceof GraphQLError) {
ctx.logger.error({ err: err.originalError }, "graphql error");
} else {
ctx.logger.error({ err }, "graphql query error");
}
return err;
};
public requestDidStart(o: {
operationName?: string;
context: CommonContext;
}) {
o.context.logger.debug({ operationName: o.operationName }, "graphql query");
}
public willSendResponse(o: {
graphqlResponse: GraphQLResponse;
context: CommonContext;
}): void | { graphqlResponse: GraphQLResponse; context: CommonContext } {
if (o.graphqlResponse.errors) {
return {
...o,
graphqlResponse: {
...o.graphqlResponse,
errors: formatApolloErrors(o.graphqlResponse.errors, {
formatter: this.logError(o.context),
debug: false,
}),
},
};
}
}
}
@@ -1,11 +1,16 @@
import { resolveGraphqlOptions } from "apollo-server-core";
import { GraphQLOptions } from "apollo-server-express";
import { Handler } from "express";
import { FieldDefinitionNode, GraphQLError, ValidationContext } from "graphql";
// TODO: when https://github.com/apollographql/apollo-server/pull/1907 is merged, update this import path
import {
ExpressGraphQLOptionsFunction,
graphqlExpress,
GraphQLOptions,
} from "apollo-server-express";
import { FieldDefinitionNode, GraphQLError, ValidationContext } from "graphql";
} from "apollo-server-express/dist/expressApollo";
import { Config } from "talk-common/config";
import { Omit } from "talk-common/types";
import { LoggerExtension } from "talk-server/graph/common/middleware/extensions/logger";
// Sourced from: https://github.com/apollographql/apollo-server/blob/958846887598491fadea57b3f9373d129300f250/packages/apollo-server-core/src/ApolloServer.ts#L46-L57
const NoIntrospection = (context: ValidationContext) => ({
@@ -23,26 +28,33 @@ const NoIntrospection = (context: ValidationContext) => ({
export const graphqlMiddleware = (
config: Config,
baseOptions: GraphQLOptions | ExpressGraphQLOptionsFunction
) => {
// Generate the validation rules.
const validationRules: Array<(context: ValidationContext) => any> = [];
requestOptions: ExpressGraphQLOptionsFunction
): Handler => {
// Create a new baseOptions that will be merged into the new options.
const baseOptions: Omit<GraphQLOptions, "schema"> = {
// Disable the debug mode, as we already add in our logging function.
debug: false,
// Include extensions.
extensions: [
// Log queries and errors.
() => new LoggerExtension(),
],
};
if (config.get("env") === "production" && !config.get("enable_graphiql")) {
// Disable introspection in production.
validationRules.push(NoIntrospection);
baseOptions.validationRules = [NoIntrospection];
}
// Generate the actual middleware.
return graphqlExpress(async (req, res) => {
// Resolve the base options.
const requestOptions = await resolveGraphqlOptions(baseOptions, req, res);
// Resolve the options for the GraphQL middleware.
const options = await requestOptions(req, res);
// Apply the validators, sourced from: https://github.com/apollographql/apollo-server/blob/958846887598491fadea57b3f9373d129300f250/packages/apollo-server-core/src/ApolloServer.ts#L104-L107
requestOptions.validationRules = requestOptions.validationRules
? requestOptions.validationRules.concat(validationRules)
: validationRules;
return requestOptions;
// Provide the options.
return {
...options,
...baseOptions,
};
});
};
+1 -1
View File
@@ -11,7 +11,7 @@ export interface ManagementContextOptions {
}
export default class ManagementContext extends CommonContext {
public mongo: Db;
public readonly mongo: Db;
constructor({ req, mongo, config }: ManagementContextOptions) {
super({ req, config });
+8 -8
View File
@@ -24,14 +24,14 @@ export interface TenantContextOptions {
}
export default class TenantContext extends CommonContext {
public tenant: Tenant;
public tenantCache: TenantCache;
public user?: User;
public mongo: Db;
public redis: Redis;
public queue: TaskQueue;
public loaders: ReturnType<typeof loaders>;
public mutators: ReturnType<typeof mutators>;
public readonly tenant: Tenant;
public readonly tenantCache: TenantCache;
public readonly user?: User;
public readonly mongo: Db;
public readonly redis: Redis;
public readonly queue: TaskQueue;
public readonly loaders: ReturnType<typeof loaders>;
public readonly mutators: ReturnType<typeof mutators>;
constructor({
req,