简体   繁体   English

如何在 GraphQL 中处理错误并发送响应

[英]How to handle error and send response in GraphQL

I was starting with GraphQL and I was unable to comprehend how we can throw errors in GraphQL我是从 GraphQL 开始的,但我无法理解如何在 GraphQL 中抛出错误

I went through a couple of articles on the web but almost all of them use Apollo and the code-structure looks very different than how I work.我在网上浏览了几篇文章,但几乎所有文章都使用 Apollo,代码结构看起来与我的工作方式大不相同。

Consider this piece of code, here where I am making a mutation, now how can send a response message with error and change headers status message in case of error?考虑这段代码,在这里我进行了更改,现在如何发送带有错误的响应消息并在出现错误时更改标头状态消息?

  AddNewPersonalInfo: {
  type: userDashboardType,
  args: { 
    parameter: {
      type: userCreationlInputType
    }
  }, 
  resolve: async (parent, args, context) => {
    args.parameter.userId = context.req.headers.userId
    //Check if user info already exsist
    const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
    if (checkIfUserInformationExsist[0]) {
      const error = {
        code: 403, 
        message: 'User info Already exsist'
      }
      throw new Error(error)
    } else {
      try {
      const addLinkedinUser = await insertIntheTable('personal', payload)
      return true
      } catch (err) {
        console.error(err)
        throw new Error(err)
      }
    }
  }
}

What I have faced in one of my projects, it is hard to set the status code of the response.我在我的一个项目中遇到过,很难设置响应的状态代码。 So, I made some custom error response to identify correct statusCode using express-graphql所以,我做了一些自定义的错误响应来使用express-graphql识别正确的 statusCode

Below is the example ( What I have used in one of my projects ):下面是示例(我在我的一个项目中使用的):

-------- app.js file -------- -------- app.js file --------

const graphqlHTTP = require('express-graphql')

app.use('/graphql', (req, res) => {
  graphqlHTTP({
    schema: GraphQLSchema, //A GraphQLSchema instance from GraphQL.js. A schema must be provided.
    graphiql: true,
    context: { req },
    formatError: (err) => {
      const error = getErrorCode(err.message)
      return ({ message: error.message, statusCode: error.statusCode })
    }
  })(req, res)
})

-------- getErrorCode function implementation-------- -------- getErrorCode函数实现--------

const { errorType } = require('../constants')

const getErrorCode = errorName => {
  return errorType[errorName]
}

module.exports = getErrorCode

-------- Constant.js file-------- -------- Constant.js文件--------

exports.errorName = {
  USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',
  SERVER_ERROR: 'SERVER_ERROR'
}

exports.errorType = {
  USER_ALREADY_EXISTS: {
    message: 'User is already exists.',
    statusCode: 403
  },
  SERVER_ERROR: {
    message: 'Server error.',
    statusCode: 500
  }
}

Now, we are ready to use our setup.现在,我们已准备好使用我们的设置。

From your query or mutation, you need to require constant file and return custom error:从您的查询或突变中,您需要需要常量文件并返回自定义错误:

const { errorName } = require('../constant')

AddNewPersonalInfo: {
  type: userDashboardType,
  args: { 
    parameter: {
      type: userCreationlInputType
    }
  }, 
  resolve: async (parent, args, context) => {
    args.parameter.userId = context.req.headers.userId
    //Check if user info already exsist
    const checkIfUserInformationExsist = await getSelectedThingFromTable('CatsWork_personal', 'userId', `${userId}`)
    if (checkIfUserInformationExsist[0]) {
      const error = {
        code: 403, 
        message: 'User info Already exsist'
      }
      throw new Error(errorName.USER_ALREADY_EXISTS) // Here you can use error from constatnt file
    } else {
      try {
      const addLinkedinUser = await insertIntheTable('personal', payload)
      return true
      } catch (err) {
        console.error(err)
        throw new Error(errorName.SERVER_ERROR) // Here you can use error from constatnt file
      }
    }
  }
}

--------Error response-------- --------错误响应--------

{
  error: [{
    "statusCode": 403,
    "message": "User is already exists."
  }],
  data: null
}

We just need to write custom error handling from FS side too.我们也只需要从 FS 端编写自定义错误处理。

Note:- formatError: is deprecated and replaced by customFormatErrorFn .注意:- formatError:已弃用并替换为customFormatErrorFn It will be removed in version 1.0.0.它将在 1.0.0 版中删除。 You can refer customFormatErrorFn .您可以参考customFormatErrorFn

graphql should be an application level layer that shouldn't (see last paragraph why shouldn't and not doesn't ) require http to work. graphql应该是应用级层不应该(见最后一段为什么不应该和不)需要HTTP工作。 Although in 99% of cases it runs on top of http, because of how convenient it is to do so, graphql is itself a layer 7 protocol.尽管在 99% 的情况下它运行在 http 之上,但由于这样做非常方便,graphql 本身就是一个第 7 层协议。

What does that mean in your case?这对你来说意味着什么? Well, it means you should not mix concepts from HTTP/REST with concepts from graphql and focus on the latter.嗯,这意味着您不应该将 HTTP/REST 的概念与来自 graphql 的概念混合在一起,而应该专注于后者。 The headers error code is a HTTP/REST concept, graphql sends errors in the errors field of the response and the nodejs implementation already catches all your errors and adds them to the list.标头错误代码是一个 HTTP/REST 概念,graphql 在响应的errors字段中发送错误,并且 nodejs 实现已经捕获了所有错误并将它们添加到列表中。 The HTTP status will be always 200, and your clients shouldn't care and consume your graphql api and not a mix of REST with graphql. HTTP 状态将始终为 200,您的客户不应该关心和使用您的 graphql api,而不是 REST 与 graphql 的混合。

Now, that being said, there are couple of things that REST over HTTP does better.现在,话虽如此,REST over HTTP 有几件事做得更好。 So people, including the developers of Apollo, kinda mixed concepts too, mainly because the graphql standard is not complete (aka, it doesn't have a standard/rule for solving all the problems you might encounter while building an API), so people improvised.所以人们,包括 Apollo 的开发人员,也有点混合概念,主要是因为 graphql 标准不完整(也就是说,它没有一个标准/规则来解决您在构建 API 时可能遇到的所有问题),所以人们即兴的。 Honestly, I wouldn't recommend graphql yet for any serious project.老实说,我不会为任何严肃的项目推荐 graphql。 Been there, done that, not worth it.去过那里,做到了,不值得。 Just stick with REST over HTTP.坚持使用 REST over HTTP。

Reference参考

You can specify an error function inside graphqlHTTP like this:您可以在 graphqlHTTP 中指定一个错误函数,如下所示:

app.use("/graphql", graphqlHTTP({
        schema,
        graphiql: true,
        customFormatErrorFn: err => { 
            try { 
                err.details = JSON.parse(err.message);
                err.message = Array.isArray(err.details.error) ? err.details.error.join(",") : err.details.error;
                return err;
            } catch {
                return err;
            }
        }
    }));

where err.message might contain a JSON object or a string.其中err.message可能包含 JSON 对象或字符串。

you can use those function to generate specific client and server error functions:您可以使用这些函数来生成特定的客户端和服务器错误函数:

const clientError = error => new Error(JSON.stringify({
    success: false,
    code: 400,
    error
}));

const serverError = ({ name, message, stack }) => new Error(JSON.stringify({
    success: false,
    error: "Server Error",
    code: 500,
    name,
    message,
    stack
}));

const userValidationError = err => { 
    if (err.name === "ValidationError") return clientError(Object.values(err.errors).map(({ message }) => message));
    return serverError(err);
}

module.exports = {
    clientError,
    serverError,
    userValidationError
};

userValidationError function is useful if you have a mongodb validation error.如果您有 mongodb 验证错误,则userValidationError函数很有用。

so that you would use it inside resolve function like this:这样你就可以在解析函数中使用它,如下所示:

try { 
    const createdDocument = await MongooseDoc.create(data);
    return createdDocument;
} catch (err) { 
    throw userValidationError(err);
}

the response would be回应将是

{
  "errors": [
    {
      "message": "error details 1,error details 2",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "document"
      ],
      "details": {
        "success": false,
        "code": 400,
        "error": [
          "error details 1",
          "error details 2"
        ]
      }
    }
  ],
  "data": {
    "document": null
  }
}

if you want to throw a clientError you throw it outside try catch.如果你想抛出一个clientError你可以把它扔到try catch之外。

Hopefully this code helps someone send dynamic error messages in graphql.希望这段代码可以帮助某人在 graphql 中发送动态错误消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM