I’m creating some custom controllers for my content type Order, and need to do some validation along with success and error responses.
One of my controllers looks as following:
async create(ctx) {
let { product: requested_product, amount: requested_amount, client, deliverytime, type } = ctx.request.body;
const product = await strapi.services.product.findOne({
id: requested_product
})
if (requested_amount !== product.price)
return ctx.badRequest(null, [{ messages: [{ id: 'The order request is invalid.' }] }]);
(...)
}
When the amount differs my GraphQL request results in an undesired error message: "Cannot return null for non-nullable field Order.id.",, when I’d want to return my error message above.
I get why this is happening, but I want to know what is the correct way to respond to the client. Obviously I cannot return an Order object when failing.
I’m not an expert in GraphQL, but I’m using a custom query & resolver for this approach.
First of all, I’ve create a file at ./api/*/config/schema.graphql.js or for plugins in ./extensions/*/config/schema.graphql.js.
And defined a new query and a resolver for it as shown bellow:
const { sanitizeEntity } = require('strapi-utils');
const _ = require('lodash');
module.exports = {
query: `
customOrders(where: JSON): JSON
`,
resolver: {
Query: {
customOrders: {
description: 'Find orders',
resolverOf: 'application::order.order.find', // Will apply the same policy on the custom resolver as the controller's action `find`.
resolver: async (obj, options, ctx) => {
//Get orders by using filters from 'where'
let entities = await strapi.query('order').find(options.where);
//Will return an error custom message if there are no orders
//Also here you can do a custom validation.
if (_.isEmpty(entities)) {
return {
status: '404',
message: 'No orders found'
}
}
//Will return sanitized orders
return {
status: '200',
orders: entities.map(entity =>
sanitizeEntity(entity, {
model: strapi.models.order,
})
)
};
},
},
},
},
};
Now in GraphQL, I use the newly created Query: customOrders, it returns orders if they are found and custom error message if there are no orders:
module.exports = {
query: `
createProduct(input: JSON): JSON
`,
resolver: {
Mutation: { // Depends on your needs, you should use Query(for fetching data) or Mutation (for data modifications)
createProduct: {
description: 'Custom create product with validation',
resolverOf: 'application::product.product.create', // Will apply the same policy on the custom resolver as the controller's action `find`.
resolver: async (obj, options, ctx) => {
let { product: requested_product, amount: requested_amount, client, deliverytime, type } = ctx.request.body;
const product = await strapi.services.product.findOne({id: requested_product});
//Will return an error custom message the amount differs
if (requested_amount !== product.price) {
return { status: '404', message: 'The order request is invalid.'};
}
//(other custom code)
},
},
},
},
};
What I’m doing to copy Strapi’s own error reporting approach, is:
// HTTP Error library used by Strapi, allows us to display custom error message in the CMS
const Boom = require('boom')
...
resolver: async (_ , { id }) => {
const book = await strapi.services['books'].findOne({ id })
if (!book) throw Boom.notFound('No book found by that id.')
return book
}
Would be interested to hear the Strapi team’s opinion on this approach.
Stumbled upon this entry and here’s my solution after some research. To return a custom message through graphql, you can throw as below as graphql displays that message instead
if (someLogicIsWrong) {
throw new Error('your message here')
}
The Boom approach works more or less.
I still get Status: 200 OK for all requests, but at least the errors messages will display correctly. Now I have to tweak my Frontend app into throwing those messages with the error object. What a mess.
Still having a bit of trouble with this. Got to the point where the data returned is in the right structure, using what @taka has put in. But what we really need is the request status to be 400, instead of 200. (same as @pedrosimao mentioned)