Yes, I expected something like this . I think the documentation should more clearly reflect this though and it leaves the matter of where to put GraphQL business-logic unresolved. The problem with handling this inside a custom resolver is that you’d have to replicate the behavior of the default resolve method, since the pattern of extending the factory behavior is not implemented for GraphQL resolvers. I extracted the default behavior into utility functions myself, so I can handle business-logic inside the resolver and trigger the default behavior with a single function call. This is suboptimal though, since changes to the ‘core’ resolve methods need to be mirrored when updating Strapi.
Perhaps it can help you or someone else. Please note that I’ve only implemented the resolve methods I personally needed.
// src/utilities/core-resolvers.js
/**
* This exports the ‘core’ resolve methods extracted/duplicated from
* @strapi/plugin-graphql. When overwriting the resolve function to add
* custom business-logic, these methods can be used to trigger the
* default resolve behavior from within this overwrite.
*
* Please note that not all core resolve methods are extracted yet.
* You can find and extract them from here:
* - node_modules/@strapi/plugin-graphql/server/services/builders/mutations
* - node_modules/@strapi/plugin-graphql/server/services/builders/queries
*
* I added the `contentTypeName` argument which should contain the
* contentType identifier ("api::restaurant.restaurant").
*/
const { sanitize } = require("@strapi/utils");
const coreUpdateMutationResolve = async (
contentTypeName,
parent,
args,
context
) => {
const { service: getService } = strapi.plugin("graphql");
const { transformArgs } = getService("builders").utils;
const { toEntityResponse } = getService("format").returnTypes;
const contentType = strapi.contentTypes[contentTypeName];
const { uid } = contentType;
// Direct copy from here on:
const { auth } = context.state;
const transformedArgs = transformArgs(args, { contentType });
// Sanitize input data
const sanitizedInputData = await sanitize.contentAPI.input(
transformedArgs.data,
contentType,
{ auth }
);
Object.assign(transformedArgs, { data: sanitizedInputData });
const { update } = getService("builders")
.get("content-api")
.buildMutationsResolvers({ contentType });
const value = await update(parent, transformedArgs);
return toEntityResponse(value, {
args: transformedArgs,
resourceUID: uid,
});
};
const coreCreateMutationResolve = async (
contentTypeName,
parent,
args,
context
) => {
const { service: getService } = strapi.plugin("graphql");
const { transformArgs } = getService("builders").utils;
const { toEntityResponse } = getService("format").returnTypes;
const contentType = strapi.contentTypes[contentTypeName];
const { uid } = contentType;
// Direct copy from here on:
const { auth } = context.state;
const transformedArgs = transformArgs(args, { contentType });
// Sanitize input data
const sanitizedInputData = await sanitize.contentAPI.input(
transformedArgs.data,
contentType,
{ auth }
);
Object.assign(transformedArgs, { data: sanitizedInputData });
const { create } = getService("builders")
.get("content-api")
.buildMutationsResolvers({ contentType });
const value = await create(parent, transformedArgs);
return toEntityResponse(value, { args: transformedArgs, resourceUID: uid });
};
module.exports = {
coreUpdateMutationResolve,
coreCreateMutationResolve,
};
You can use it like this:
// src/index.js
const { coreUpdateMutationResolve } = require("./utilities/core-resolvers.js");
module.exports = {
register({ strapi }) {
const extensionService = strapi.plugin("graphql").service("extension");
extensionService.use({
resolvers: {
Mutation: {
updateRestaurant: {
resolve: async (parent, args, context) => {
// Handle business logic here ...
return await coreUpdateMutationResolve(
"api::restaurant.restaurant",
parent,
args,
context
);
}
}
}
}
})
}
}