求助:Apollo 4 GraphQL对接Elasticsearch返回NULL问题
Apollo 4 GraphQL服务创建任务返回NULL问题排查
我正在搭建基于Apollo 4的GraphQL服务,实现与Elasticsearch的数据交互,但发送创建任务的POST请求时始终返回NULL。Elasticsearch与Apollo的连接看似正常,以下是核心文件代码、请求及返回内容,恳请帮忙排查问题:
server.js(服务启动文件)
const express = require('express') const app = express() const tasks = require('./routes/tasks') const { ApolloServer } = require('@apollo/server'); const { startStandaloneServer } = require('@apollo/server/standalone') const { ApolloServerPluginDrainHttpServer } = require('@apollo/server/plugin/drainHttpServer'); const { schema } = require('./schema.js'); const { resolvers } = require('./resolvers.js'); const http = require('http'); const cors = require('cors'); const { json } = require('body-parser'); const { expressMiddleware } = require('@apollo/server/express4'); app.use(express.json()) app.use(express.urlencoded({ extended: false })) const httpServer = http.createServer(app); const server = new ApolloServer({ schema, resolvers, plugins: [ApolloServerPluginDrainHttpServer({ httpServer })], }); const startServer = async () => { await startStandaloneServer(server, { listen: { port: 5555 }, }); console.log(`Server ready at: localhost:5555`); console.log('Query at: https://studio.apollographql.com/dev'); app.use( '/graphql', cors(), json(), expressMiddleware(server, { context: async ({ req }) => ({ token: req.headers.token }), }) ); app.listen(5551, () => { console.log('port: 5551'); }); }; startServer();
tasks.js(路由文件)
const express = require('express') const router = express.Router() const {getTasks,addTasks,changeTasks,deleteTasks,updateTaskDone} = require('../controllers/tasks') router.use(express.json()) router.use(express.urlencoded({extended:false})) router.get('/', getTasks); router.post('/', addTasks); router.put('/done/:id', updateTaskDone); router.delete('/delete/:id', deleteTasks); router.put('/rename/:id',changeTasks) module.exports = router;
tasks.js(控制器文件)
const {Client} = require('@elastic/elasticsearch') const client = new Client({ node: 'https://localhost:9200', auth: { username:"elastic", password:"rphcDrBrEm_i1D7_A+hS", }, tls: { rejectUnauthorized: false } }) async function run(){ console.log("connected to elastic!"); } run().catch(errore =>{ console.log("errore"); }) let tasks = [] const getTasks = async (req, res) => { const result = await client.search({ index: 'tasks', body:{ query:{ match_all: {} } } }) res.status(200).json({success:true, tasks:tasks}) } const addTasks = async (req, res) => { const { name } = req.body; const newTask = { name: name, done: false }; const result = await client.index({ index: 'tasks', body: newTask }); const { _id } = result; newTask._id = _id; console.log("Task creata con successo, ID:",_id) tasks.push(newTask); res.status(201).json(newTask); } const updateTaskDone = async (req, res) => { const { id } = req.params; const taskIndex = tasks.findIndex(task => task._id === id); const tasksToUpdate = [...tasks]; if (tasksToUpdate[taskIndex].done) { await client.update({ index: 'tasks', id: id, body: { doc: { done: false } } }); tasksToUpdate[taskIndex].done = false; }else { await client.update({ index: 'tasks', id: id, body: { doc: { done: true } } }); tasksToUpdate[taskIndex].done = true; } console.log("Task aggiornata con successo, ID:",id) tasks = tasksToUpdate; res.status(200).json({ success: true, task: tasks }); }; const changeTasks = async (req, res) => { const { id } = req.params; const tasksToUpdate = [...tasks]; const { newName } = req.body; const taskIndex = tasks.findIndex(task => task._id === id); await client.update({ index: 'tasks', id: id, body: { doc: { name: newName } } }); tasksToUpdate[taskIndex].name = newName; tasks = tasksToUpdate; res.status(200).json({ success: true, task: tasks }); }; const deleteTasks = async (req, res) => { const { id } = req.params; const taskIndex = tasks.findIndex(task => task._id === id); const tasksToUpdate = [...tasks]; const taskToDelete = tasks.splice(tasksToUpdate[taskIndex], 1); await client.delete({ index: 'tasks', id: id, }); const updatedTasks = [...tasks]; tasks = updatedTasks; res.status(200).json({ success: true, tasks: updatedTasks }); console.log("Task eliminata con successo, ID:",id) }; module.exports = {getTasks,addTasks,changeTasks,deleteTasks,updateTaskDone,client}
schema.js
const { makeExecutableSchema } = require('@graphql-tools/schema'); const typeDefs = `#graphql type Query { tasks: [Task!]! } type Mutation{ newTask( input: taskInput): Task } input taskInput{ name: String!, done: Boolean!, } type Task { name: String! done: Boolean! _id: ID! } ` const schema = makeExecutableSchema({ typeDefs }); module.exports = {schema}
resolvers.js
const { client } = require('./controllers/tasks'); const resolvers = { Query: { tasks: async (parent, args, context, info) => { const { body } = await client.search({ index: 'tasks', body: { query: { match_all: {} } } }); const tasks = body.hits.hits.map(hit => hit._source); return tasks; } }, Mutation: { newTask: async (parent, args, context, info) => { const { input } = args; const newTask = { name: input.name, done: false }; const result = await client.index({ index: 'tasks', body: newTask }); const { _id } = result; newTask._id = _id; return newTask; } } }; module.exports = { resolvers };
请求内容(Postman发送)
{ "query": "mutation newTask($input: taskInput) { newTask(input: $input) { _id name done } }", "variables": { "input": { "done": false, "name": "lol123123123" } } }
返回结果
{ "data": { "newTask": null } }
问题排查与修复方案
1. 修复Apollo Server启动冲突问题
你的server.js同时启动了两个独立服务:startStandaloneServer占用5555端口,又通过app.listen启动了5551端口的Express服务,导致GraphQL请求可能被路由到错误的服务实例。统一启动方式:
const express = require('express') const app = express() const tasks = require('./routes/tasks') const { ApolloServer } = require('@apollo/server'); const { expressMiddleware } = require('@apollo/server/express4'); const { ApolloServerPluginDrainHttpServer } = require('@apollo/server/plugin/drainHttpServer'); const { schema } = require('./schema.js'); const { resolvers } = require('./resolvers.js'); const http = require('http'); const cors = require('cors'); const { json } = require('body-parser'); app.use(express.json()) app.use(express.urlencoded({ extended: false })) app.use('/tasks', tasks); // 挂载REST路由 const httpServer = http.createServer(app); const server = new ApolloServer({ schema, resolvers, plugins: [ApolloServerPluginDrainHttpServer({ httpServer })], }); const startServer = async () => { await server.start(); // 先启动Apollo核心服务 // 挂载GraphQL中间件到Express app.use( '/graphql', cors(), json(), expressMiddleware(server, { context: async ({ req }) => ({ token: req.headers.token }), }) ); // 启动统一HTTP服务 await new Promise(resolve => httpServer.listen({ port: 5555 }, resolve)); console.log(`Server ready at http://localhost:5555/graphql`); }; startServer();
2. 修复Resolver返回值不匹配Schema问题
你的Query和Mutation resolver返回的任务对象缺少_id字段(仅返回Elasticsearch的_source),但Schema要求Task类型必须包含_id,Apollo会因字段缺失将结果置为null。修改Resolver:
const { client } = require('./controllers/tasks'); const resolvers = { Query: { tasks: async (parent, args, context, info) => { try { const { body } = await client.search({ index: 'tasks', body: { query: { match_all: {} } } }); // 把Elasticsearch的文档ID注入返回对象 return body.hits.hits.map(hit => ({ ...hit._source, _id: hit._id })); } catch (error) { console.error('获取任务失败:', error); throw new Error('无法获取任务'); } } }, Mutation: { newTask: async (parent, args, context, info) => { try { const { input } = args; const newTask = { name: input.name, done: input.done || false // 尊重传入的done值,或默认false }; const result = await client.index({ index: 'tasks', body: newTask }); // 返回符合Schema的完整对象 return { ...newTask, _id: result._id }; } catch (error) { console.error('创建任务失败:', error); throw new Error('无法创建任务'); } } } }; module.exports = { resolvers };
3. 确认Elasticsearch索引存在
执行以下命令检查tasks索引是否存在:
curl -u elastic:rphcDrBrEm_i1D7_A+hS -k https://localhost:9200/tasks
如果返回404,创建索引:
curl -u elastic:rphcDrBrEm_i1D7_A+hS -k -XPUT https://localhost:9200/tasks
4. 测试请求
确保Postman请求地址为http://localhost:5555/graphql,重新发送创建任务请求。
内容的提问来源于stack exchange,提问作者anfield
相关产品推荐
相关产品推荐

