I'm getting "Bad request" response with message "roleId must be a string" when running a mutation. I don't know why since the "roleId" field is optional.
Schema
input CustomerUpdateInput {
name: String
roleId: String
}
updateCustomer(customerId: String!, customer: CustomerUpdateInput!): Customer!
Mutation (ERROR)
mutation updateCustomer{
updateCustomer(customerId:"62c6d6ba303c734ef44ea4ed",
customer: {name:"Pablo"}
),
{id, name }
}
Mutation (WITHOUT ERROR)
mutation updateCustomer{
updateCustomer(customerId:"62c6d6ba303c734ef44ea4ed",
customer: {
name:"Pablo",
roleId:"62c6d64f303c734ef44ea4d8"
}
),
{id, name }
}
Error
{
"errors": [
{
"message": "Bad Request Exception",
"extensions": {
"code": "BAD_USER_INPUT",
"response": {
"statusCode": 400,
"message": [
"roleId must be a string"
],
"error": "Bad Request"
}
}
}
],
"data": null
}
Related
I'm running the below GraphQL query to create data on dynamodb table, but gets the response with "Not Authorized to access createClient on type Client"
Why this is happening at.
GraphQL query:
`mutation addClient{
createClient(input:{
fullName: "Jhon Smith"
title: "Software Engineer"
organization: "AWS"
contactNo: "+341289655524"
email: "smithj#amazon.com"
country: "Canada"
address: "AN/458, Norton place, Down Town"
}){
fullName
}
}`
Response :
{
"data": {
"createClient": null
},
"errors": [
{
"path": [
"createClient"
],
"data": null,
"errorType": "Unauthorized",
"errorInfo": null,
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "Not Authorized to access createClient on type Client"
}
]
}
It solved with authMode
const newTodo = await API.graphql({ query: mutations.createClient, variables: {input: inputData}, authMode: "AMAZON_COGNITO_USER_POOLS" });
module.exports.allchatrooms = async(req, res) => {
try {
const details = await Chatroom.find({});
return res.status(200).send({
status: 200,
message: "all the chatroom",
data:details,
});
}
catch (error) {
return res.status(400).send({
status: 00,
err: error,
});
}
};
error in postman
{
"status": 400,
"success": false,
"err": {
"stringValue": "\"{ _id: 'allchatroom' }\"",
"valueType": "Object",
"kind": "ObjectId",
"value": {
"_id": "allchatroom"
},
"path": "_id",
"reason": {},
"name": "CastError",
"message": "Cast to ObjectId failed for value \"{ _id: 'allchatroom' }\" (type Object) at path \"_id\" for model \"user\""
}
}
model file
const mongoose = require('mongoose');
const Chatroom = new mongoose.Schema({
chatroom_name:{
type:String,
require:true
}
})
module.exports = mongoose.model('chat',Chatroom);
i am try to make the chatroom application for the practice because i am new in this field so try to make this project how can handle the real time data but when i hit the api to get all the chatrooms list then i got this error
is there a way how to run an exception through the apollo exception handler manually?
I have 90% of the application in GraphQL but still have two modules as REST and I'd like to unify the way the exceptions are handled.
So the GQL queries throw the standard 200 with errors array containing message, extensions etc.
{
"errors": [
{
"message": { "statusCode": 401, "error": "Unauthorized" },
"locations": [{ "line": 2, "column": 3 }],
"path": [ "users" ],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"response": { "statusCode": 401, "error": "Unauthorized" },
"status": 401,
"message": { "statusCode": 401, "error": "Unauthorized" }
}
}
}
],
"data": null
}
where the REST throws the real 401 with JSON:
{
"statusCode": 401,
"error": "Unauthorized"
}
So can I simply catch and wrap the exception in the Apollo Server format or do I have to format my REST errors manually? Thanks
I am using NestJS and the GraphQL module.
You can set up a custom exception filter which catches the REST-Api errors and wraps them in the Apollo Server format. Something like:
#Catch(RestApiError)
export class RestApiErrorFilter implements ExceptionFilter {
catch(exception: RestApiError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = 200;
response
.status(status)
.json(RestApiErrorFilter.getApolloServerFormatError(exception);
}
private static getApolloServerFormatError(exception: RestApiErrorFilter) {
return {}; // do your conversion here
}
I'm seeing some class-validator errors in an unexpected location. I would expect a more convenient format for dealing with errors, but perhaps I'm unaware of some graphQL tricks for dealing with objects in the extensions field...
While running NestJS sample/23-graphql-code-first, I see the following in the GraphQL playground:
With input:
addRecipe(newRecipeData: {
description: "too short"
title: "this field should fail for being too long"
ingredients: ["normal"]
}) {
title
}
}
I am returned:
"errors": [
{
"message": "Bad Request Exception",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"addRecipe"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"response": {
"statusCode": 400,
"message": [
"title must be shorter than or equal to 30 characters",
"description must be longer than or equal to 30 characters"
],
"error": "Bad Request"
},
"status": 400,
"message": "Bad Request Exception",
"stacktrace": [
"Error: Bad Request Exception",
" at ValidationPipe.exceptionFactory nest/sample/23-graphql-code-first/node_modules/#nestjs/common/pipes/validation.pipe.js:78:20)",
...
]
}
}
}
],
"data": null
}
These errors are deeply nested, and "Bad Request Exception" is not so useful. Is this working as intended?
The best solution is based on this official comment: creates your own formatError or formatResponse function.
I just used in this project on GitHub and it worked fine!
The change needs to be added to src/app.module.ts, by default.
My Sample:
import { GraphQLError, GraphQLFormattedError } from 'graphql';
GraphQLModule.forRoot({
autoSchemaFile: true,
debug: false,
formatError: (error: GraphQLError) => {
const graphQLFormattedError: GraphQLFormattedError = {
message: error?.extensions?.exception?.response?.message || error?.message,
};
return graphQLFormattedError;
},
}),
And now, I'm getting a formatted error response from GraphQL:
{
"errors": [
{
"message": [
"firstName must be longer than or equal to 1 characters",
"lastName must be longer than or equal to 1 characters"
]
}
],
"data": null
}
I ended up doing this in main.ts (although i'm sure there is a better solution)
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (errors: ValidationError[]) => {
const error_messages = errors.map(error =>
Object.values(error.constraints),
);
return new BadRequestException(error_messages.toString());
},
forbidUnknownValues: false,
}),
);
When you use Validation pipe you can change that behaviour:
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: errors => new BadRequestException(errors), // TODO: Use graphql errors instead
forbidUnknownValues: true,
}),
);
If you're using mercurius, you can use this:
// In main.ts file, just register ValidationPipe like normal
app.useGlobalPipes(new ValidationPipe({ forbidUnknownValues: true }))
// In app.module.ts file, add this errorFormatter:
GraphQLModule.forRoot<MercuriusDriverConfig>({
...
errorFormatter: execution => {
const [error] = execution.errors // take first error
const originalError = error?.originalError
if (originalError instanceof HttpException)
return {
statusCode: originalError.getStatus(),
response: { data: originalError.getResponse() as any }
}
return { statusCode: 500, response: execution }
}
})
Here is my solution you could add error and ok objects to your #ObjectType.
#ObjectType()
export class UserCreateOutput {
//can be null if error
#Field(() => User, { nullable: true })
user?: User;
#Field(() => Boolean)
ok?: boolean;
#Field(() => String, { nullable: true })
error?: string;
}
On .service.ts you could catch and show error as a return.
catch (error) {
return {
ok: false,
error: 'Could not create account', //Your custom error message
user: null, //Make sure that this object can be null :)
};
If you are looking for how to strip the stack trace off of the GraphQL Error you just have to make debug field as false
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
debug: false, // set as ENV param
playground: true, // set as ENV param
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
})
If you set both debug and playground as ENV params to true for dev environment, you will not see the stack trace in prod.
Here is my code to validate a model.
const loopback = require('loopback');
const {connectors, getDsnFormRules} = require('../../common/dsn');
const validator = require('indicative');
function validateConnectionPar(err, done) {
Promise.resolve().then(() => validator.validateAll(this, rules)
.then(result => done())
.catch(error => {
err(new Error(JSON.stringify(error)));
done();
});
}
module.exports = (Dsn) => {
Dsn.validateAsync('dsn', validateConnectionPar, {message: 'connection par are invalid'});
};
I am using indicative library to do validations. Object to be validated has structure like :
rules: {
'connector': 'required|string',
'dsnParams.host': 'required|string',
'dsnParams.port': 'required|integer|range:0,65536',
'dsnParams.username': 'required|string',
'dsnParams.password': 'required|string',
}
If there is some error, I get a response like :
{
"error": {
"statusCode": 422,
"name": "ValidationError",
"message": "The `dsn` instance is not valid. Details: `dsn` is invalid (value: undefined).",
"details": {
"context": "dsn",
"codes": {
"dsn": [
"custom.Error: [{\"field\":\"dsnParams.host\",\"validation\":\"required\",\"message\":\"required validation failed on dsnParams.host\"},{\"field\":\"dsnParams.port\",\"validation\":\"range\",\"message\":\"dsnParams.port must be in the range 0 to 65536 exclusive\"}]"
]
},
"messages": {
"dsn": [
"is invalid"
]
}
},
"stack": "ValidationError: The `dsn` instance is not valid. Details: `dsn` is invalid (value: undefined).\n at /home/bala/IRIS/Beginning/iris/node_modules/loopback-datasource-juggler/lib/dao.js:355:12\n at ModelConstructor.<anonymous> (/home/bala/IRIS/Beginning/iris/node_modules/loopback-datasource-juggler/lib/validations.js:577:13)\n at ModelConstructor.next (/home/bala/IRIS/Beginning/iris/node_modules/loopback-datasource-juggler/lib/hooks.js:93:12)\n at done (/home/bala/IRIS/Beginning/iris/node_modules/loopback-datasource-juggler/lib/validations.js:574:25)\n at /home/bala/IRIS/Beginning/iris/node_modules/loopback-datasource-juggler/lib/validations.js:652:7\n at Promise.resolve.then.then.catch.error (/home/bala/IRIS/Beginning/iris/server/models/dsn.js:17:7)\n at runMicrotasksCallback (internal/process/next_tick.js:58:5)\n at _combinedTickCallback (internal/process/next_tick.js:67:7)\n at process._tickCallback (internal/process/next_tick.js:98:9)"
}
}
When i return a new Error object to err() function, those errors are displayed as custom.Error in code.
Is there a way to obtain them as list of messages in
"messages": {
"dsn": [
"is invalid"
]
}
something like :
"messages": {
"dsn": [
"port": "is invalid",
"host": "required validation failed on dsnParams"
]
}