You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express应用中GraphQL错误状态码的前后端传递处理方法

解决Express+GraphQL后端传递自定义HTTP状态码的问题

GraphQL 规范默认所有响应都返回 200 OK,错误信息会放在响应体的 errors 数组中,这就是你前端始终拿到 200 的核心原因。要传递 409、422 这类状态码,需要在 Express 层面拦截响应,根据错误信息动态修改 HTTP 状态码,具体步骤如下:

1. 定义自定义错误类

创建带状态码标识的错误类,方便在 resolver 中抛出时标记对应的 HTTP 状态:

class GraphQLHTTPError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.name = 'GraphQLHTTPError';
    this.statusCode = statusCode;
    Error.captureStackTrace(this, this.constructor);
  }
}

2. 在 Resolver 中抛出自定义错误

处理业务逻辑时,遇到冲突、验证失败等场景,抛出自定义错误:

// 示例:创建用户的 mutation resolver
createUser: async (parent, { email }, context) => {
  const existingUser = await User.findOne({ email });
  if (existingUser) {
    // 抛出409冲突错误
    throw new GraphQLHTTPError('该邮箱已被注册', 409);
  }
  // 验证参数格式
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    // 抛出422验证错误
    throw new GraphQLHTTPError('邮箱格式无效', 422);
  }
  // 正常创建用户逻辑
  const user = await User.create({ email });
  return user;
}

3. 配置 GraphQL 错误格式化

将自定义错误转换为 GraphQL 规范的错误格式,并在 extensions 中携带状态码:

如果你用 Apollo Server Express:

const { ApolloServer } = require('apollo-server-express');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  formatError: (err) => {
    // 识别自定义错误
    if (err.originalError instanceof GraphQLHTTPError) {
      return {
        message: err.message,
        extensions: {
          statusCode: err.originalError.statusCode,
          code: err.originalError.name
        }
      };
    }
    // 其他错误保持默认处理
    return err;
  },
  // 将 req/res 传入上下文(可选,若需在 resolver 中直接操作响应)
  context: ({ req, res }) => ({ req, res })
});

// 挂载到 Express
server.applyMiddleware({ app, path: '/graphql' });

如果你用 express-graphql:

const { graphqlHTTP } = require('express-graphql');

app.use('/graphql', graphqlHTTP({
  schema,
  rootValue: resolvers,
  formatError: (err) => {
    if (err.originalError instanceof GraphQLHTTPError) {
      return {
        message: err.message,
        extensions: {
          statusCode: err.originalError.statusCode
        }
      };
    }
    return err;
  }
}));

4. 添加 Express 响应拦截中间件

这是关键步骤:拦截 GraphQL 响应,根据 errors 中的状态码修改 HTTP 状态:

app.use((req, res, next) => {
  // 仅处理 GraphQL 路径的 POST 请求
  if (req.path === '/graphql' && req.method === 'POST') {
    const originalSend = res.send;
    res.send = function(data) {
      try {
        const responseData = JSON.parse(data);
        // 提取第一个错误的状态码(若存在)
        if (responseData.errors?.length) {
          const statusCode = responseData.errors[0].extensions?.statusCode;
          if (typeof statusCode === 'number') {
            res.status(statusCode);
          }
        }
      } catch (e) {
        // 解析失败时保持默认状态码
      }
      // 调用原 send 方法发送响应
      return originalSend.call(this, data);
    };
  }
  next();
});

前端处理示例

现在前端可以正常获取到对应的 HTTP 状态码,用 fetch 示例:

const createUserMutation = `
  mutation CreateUser($email: String!) {
    createUser(email: $email) {
      id
      email
    }
  }
`;

fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: createUserMutation,
    variables: { email: 'test@example.com' }
  })
})
.then(res => {
  // 根据状态码处理不同错误场景
  if (res.status === 409) {
    alert('邮箱已被注册');
  } else if (res.status === 422) {
    alert('邮箱格式错误');
  }
  return res.json();
})
.then(data => {
  if (!data.errors) {
    console.log('用户创建成功', data.data.createUser);
  }
})

为什么之前的方法没生效?

  • 仅在 extensions 中设置 statusCode 不会改变 HTTP 状态,因为 GraphQL 规范默认返回 200
  • res.ok 基于 HTTP 状态码判断,之前状态码一直是 200,所以 res.ok 始终为 true,无法区分错误场景

内容的提问来源于stack exchange,提问作者Mr.Unforgettable

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 00:39:53