hapijs - Apollo Server & 4xx 状态码
问题描述
目前,我的 Apollo 服务器(在 HapiJS 上运行)为每个请求返回 HTTP 200,包括失败的请求。
我希望 GraphQL 服务器为不成功的请求返回 HTTP 4xx。主要原因是我想为我的 ELB 设置监控。
我知道 Apollo Server 有一个引擎平台,但我想使用我当前的基础架构来实现它。
关于我如何做到这一点的任何想法?我试图为我的 HapiJS 服务器捕获“onPreResponse”事件,但我无法在那里修改状态代码。
解决方案
看完这个答案。这是通过修改hapiApollo.ts文件的hapijs
插件graphqlHapi
的解决方案。
server.ts
:
import { makeExecutableSchema } from 'apollo-server';
import { ApolloServer, gql } from 'apollo-server-hapi';
import Hapi from 'hapi';
import { graphqlHapi } from './hapiApollo';
const typeDefs = gql`
type Query {
_: String
}
`;
const resolvers = {
Query: {
_: () => {
throw new Error('some error');
},
},
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const port = 3000;
async function StartServer() {
const app = new Hapi.Server({ port });
graphqlHapi.register(app, { path: '/graphql', graphqlOptions: { schema } });
app.ext('onPreResponse', (request: any, h: any) => {
const response = request.response;
if (!response.isBoom) {
return h.continue;
}
return h.response({ message: response.message }).code(400);
});
await app.start();
}
StartServer()
.then(() => {
console.log(`apollo server is listening on http://localhost:${port}/graphql`);
})
.catch((error) => console.log(error));
hapiApollo.ts
:
import Boom from 'boom';
import { Server, Request, RouteOptions } from 'hapi';
import { GraphQLOptions, runHttpQuery, convertNodeHttpToRequest } from 'apollo-server-core';
import { ValueOrPromise } from 'apollo-server-types';
export interface IRegister {
(server: Server, options: any, next?: Function): void;
}
export interface IPlugin {
name: string;
version?: string;
register: IRegister;
}
export interface HapiOptionsFunction {
(request?: Request): ValueOrPromise<GraphQLOptions>;
}
export interface HapiPluginOptions {
path: string;
vhost?: string;
route?: RouteOptions;
graphqlOptions: GraphQLOptions | HapiOptionsFunction;
}
const graphqlHapi: IPlugin = {
name: 'graphql',
register: (server: Server, options: HapiPluginOptions, next?: Function) => {
if (!options || !options.graphqlOptions) {
throw new Error('Apollo Server requires options.');
}
server.route({
method: ['GET', 'POST'],
path: options.path || '/graphql',
vhost: options.vhost || undefined,
options: options.route || {},
handler: async (request, h) => {
try {
const { graphqlResponse, responseInit } = await runHttpQuery([request, h], {
method: request.method.toUpperCase(),
options: options.graphqlOptions,
query:
request.method === 'post'
? // TODO type payload as string or Record
(request.payload as any)
: request.query,
request: convertNodeHttpToRequest(request.raw.req),
});
// add our custom error handle logic
const graphqlResponseObj = JSON.parse(graphqlResponse);
if (graphqlResponseObj.errors && graphqlResponseObj.errors.length) {
throw new Error(graphqlResponseObj.errors[0].message);
}
const response = h.response(graphqlResponse);
Object.keys(responseInit.headers as any).forEach((key) =>
response.header(key, (responseInit.headers as any)[key]),
);
return response;
} catch (error) {
// handle our custom error
if (!error.name) {
throw Boom.badRequest(error.message);
}
if ('HttpQueryError' !== error.name) {
throw Boom.boomify(error);
}
if (true === error.isGraphQLError) {
const response = h.response(error.message);
response.code(error.statusCode);
response.type('application/json');
return response;
}
const err = new Boom(error.message, { statusCode: error.statusCode });
if (error.headers) {
Object.keys(error.headers).forEach((header) => {
err.output.headers[header] = error.headers[header];
});
}
// Boom hides the error when status code is 500
err.output.payload.message = error.message;
throw err;
}
},
});
if (next) {
next();
}
},
};
export { graphqlHapi };
现在,当 GraphQL 解析器抛出错误时,客户端将收到带有 Http 状态码 400 的自定义响应,而不是带有 GraphQL 错误响应的 200 状态码。
来自浏览器的一般:
Request URL: http://localhost:3000/graphql
Request Method: POST
Status Code: 400 Bad Request
Remote Address: 127.0.0.1:3000
Referrer Policy: no-referrer-when-downgrade
响应正文是:{"message":"some error"}
推荐阅读
- laravel - 如何更改 Laravel Sanctum 网址前缀?
- c# - 从 .Net 4 迁移到 5 会更改构建任务行为,并且不会将“中间输出”生成的资源包含到 dll 中
- typescript - 无法使用 PassportJS 读取未定义、NestJS 的属性“validateUser”
- javascript - 无法读取未定义的属性“ajaxSetup”
- python - FileNotFoundError:[Errno 2] 没有这样的文件或目录:'/mytrigger\xa0_init__.py'
- node.js - 是否可以使用节点 SDK 打包 Hyperledger Fabric 链码?
- rust - 为什么 read_line 不改变它的论点?
- ffmpeg - 使用ffmpeg为视频加水印时如何将特殊字符作为文本包含在内?
- python-3.x - 在第一个空白行将 CSV 上传到谷歌表格
- python - 如何在不添加子列表的情况下将键值对添加到列表中?