-
We'd like to log Mutation operations that go through graphql-ws (we use sockets because it's more performant on the client). There's a few extra items within our GraphQL Context like ipAddress, userId, countryCode etc. I'm unsure how to get ahold of that within the hooks though. We have a custom/dynamic context (simplified) like so: useServer(
{
schema,
context({ extra }) {
return new Promise(resolve => {
const ipAddress = getIpAddress(extra.request);
const countryCode = getDetectedCountry(extra.request);
resolve({ ipAddress, countryCode });
});
} Idea: useServer({
context({ extra }) { ... },
onSubscribe({ extra }, msg) {
const args = {
schema,
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
};
const operationDef = getOperationAST(args.document, args.operationName);
if (operationDef.operation !== 'mutation') {
return;
}
const { ipAddress, countryCode } = graphqlContext; // Unsure how to retrieve these
getMutationVariableParameters(operationDef).forEach(mutation => {
logInfo(mutation.operationName, {
operation: operationDef.operation,
arguments: mutation.arguments,
ipAddress,
countryCode,
});
});
}
});
export function getMutationVariableParameters(operationDef: OperationDefinitionNode) {
const selections = operationDef.selectionSet.selections as FieldNode[];
return selections.map(selection => {
return {
operationName: selection.name.value,
arguments: selection.arguments.reduce((acc, argument) => {
acc[argument.name.value] = getArgumentValue(argument.value);
return acc;
}, {} as Record<string, any>),
};
});
} Originally this happened within an Apollo Server Middleware, but realised that those middlewares don't happen through Subscription operations so need to duplicate that logging logic. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
It looks like I can do something like this inside my custom context function, but I'm not sure if it's a good idea: context({ extra }) {
return new Promise(resolve => {
const ipAddress = getIpAddress(extra.request);
const countryCode = getDetectedCountry(extra.request);
const context = { ipAddress, countryCode };
extra.context = context; // store it on extra
resolve(context);
});
} |
Beta Was this translation helpful? Give feedback.
-
The As you suggested with #281 (comment), the first argument of all hooks, the connection context, is what you should use. Since the type MyExtras = {
ipAddress: string;
countryCode: string;
};
useServer<MyExtras>(
{
onConnect(ctx) {
ctx.extra.ipAddress = getIpAddress(ctx.extra.request);
ctx.extra.countryCode = getDetectedCountry(ctx.extra.request);
},
// your other options...
},
myWebSocketServer,
); |
Beta Was this translation helpful? Give feedback.
The
onSubscribe
hook gets called beforecontext
. Reason in simple, you might want to have theonSubscribe
supply the complete GraphQL execution args (including context, validation, parsing, etc.). GraphQL execution arguments are available only in theonOperation
hook that happens after the operation has been executed.As you suggested with #281 (comment), the first argument of all hooks, the connection context, is what you should use. Since the
context
hook is only for the GraphQL context, it is not a fitting place for setting up the connection context; however,onConnect
, on the other hand, is. Additionally, exactly as you showcased, I'd too recommend putting your extras in theContext.e…