I have the following code:
router.post('/:email/addWorkflow', async function (req, res, next) {
const params = req.params;
const workflow = req.body;
const email = params.email;
User.findOne({ email: email }, function (err, user) {
if (err) {
res.status(500).send({
error: 'Error while querying database'
});
} else if (user) {
const workflows = user.workflows;
workflows.forEach(wf => {
if (wf) {
if (wf.workflowId === workflow.workflowId) {
res.status(409).send({
error: 'Workflow with that id already exists'
});
}
}
});
workflows.push(workflow);
User.updateOne({ email: email }, { $set: { workflows: workflows } }, { upsert: false }, function (err) {
if (err) {
res.status(500).send({
message: 'Error while updating database'
});
} else {
res.status(200).send({
message: 'Wf added successfully'
});
}
});
} else {
res.status(404).send({
message: 'No such user'
});
}
});
});
After I make a post with an already existing workflowId, I get the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:485:11)
..........
at /home/petar/Documents/jsProjects/p/backend/routes/users.js:50:29
at CoreDocumentArray.forEach (<anonymous>)
at /home/petar/Documents/jsProjects/p/backend/routes/users.js:47:17
at /home/petar/Documents/jsProjects/p/backend/node_modules/mongoose/lib/model.js:4915:16
at /home/petar/Documents/jsProjects/p/backend/node_modules/mongoose/lib/model.js:4915:16
at /home/petar/Documents/jsProjects/linear-mixed-models/backend/node_modules/mongoose/lib/query.js:4380:11
[... lines matching original stack trace ...]
at processTicksAndRejections (internal/process/task_queues.js:76:11) {
code: 'ERR_HTTP_HEADERS_SENT'
Any ideas? I looked at other posts for the same error. I understand that it happens if I try to send response 2 time: res.send({...}) and res.send({...}). However, this does not happen in my case. Thanks in advance
I am not completely sure what line the error message is indicating, but the following loop is the only place I can think of a multiple response on your code
workflows.forEach(wf => {
//foreach is looping
if (wf) {
if (wf.workflowId === workflow.workflowId) {
res.status(409).send({
error: 'Workflow with that id already exists'
});
//but I don't think this guy will stop looping after the first "send()"
}
}
});
Currently, my Apollo Server(running on HapiJS) returns HTTP 200 for every request, including failed ones.
I would like the GraphQL server to return HTTP 4xx for unsuccessful requests. The primary reason for it is that I want to set up monitoring for my ELB.
I know that Apollo Server has an engine platform, but I want to implement it using my current infrastructure.
Any ideas of how I could accomplish that? I tried to capture 'onPreResponse' event for my HapiJS server but I couldn't modify status code there.
After reading this answer. Here is a solution by modifying the hapijs plugin graphqlHapi of hapiApollo.ts file.
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 };
Now, when the GraphQL resolver throws an error, the client-side will receive our custom response with Http status code 400 instead of 200 status code with GraphQL errors response.
General from the browser:
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
The response body is: {"message":"some error"}
I am trying to call an api and I get the error "Unhandled promise rejection: Error: Request failed with status code 500". I dunno how did I get the error.I put the api call in componentDidMount. I am not sure whether the error comes from the redux implementation or from how I called the api.
This is how I called the api. After a successful login, I put the username as a token and use it to call another api.
state={
username: '',
semcode: [
{}
]
}
componentWillMount() {
AsyncStorage.getItem('Login_token').then((token) => {
console.log('this is coursescreen',token);
let Login_token = token;
this.setState({ username: Login_token });
});
}
componentDidMount(){
this.props.getSemcode(this.state.username);
}
componentWillReceiveProps(nextProps) {
console.log('xx',nextProps);
if (nextProps.semCode != undefined) {
this.setState({ semcode: nextProps.semCode });
}
}
This is how I wrote my action file:
export const getSemcode = (username) => async dispatch => {
let param = {
nomatrik: username,
}
console.log(`${helper.ROOT_URL}/result/GetListOfKodSesiSem`)
let code_res = await
axios.post(`${helper.ROOT_URL}/result/GetListOfKodSesiSem`, param)
console.log(code_res.data);
if (code_res.data.length > 0) {
const { code } = code_res.data;
dispatch({ type: SEMCODE_FETCH_SUCCESS, payload: { semCode: code }});
}
}
This is how I wrote my reducer:
import { SEMCODE_FETCH_SUCCESS} from '../actions/types';
const INITIAL_STATE={
semCode:[],
}
export default function (state=INITIAL_STATE, action){
switch(action.type){
case SEMCODE_FETCH_SUCCESS:
return action.payload
default:
return state;
}
}
Can anyone help me pleaseeeeee
Error Message
Error received from axios.post: {"config":{"transformRequest":
{},"transformResponse":{},"timeout":0,"xsrfCookieName":"XSRF-
TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","maxContentLength":-1,"headers":
{"Accept":"application/json, text/plain, /","Content-
Type":"application/json;charset=utf-8"},
"method":"post","nomatrik":"BB16160907",
"url":"https://smp.ums.edu.my/api/result/GetListOfKodSesiSem","data":"
{\"Accept\":\"application/json\",\"Content-
Type\":\"application/json\"}"},"request":
{"UNSENT":0,"OPENED":1,"HEADERS_RECEIVED":2,"LOADING":3,"DONE":4,
"readyState":4,"status":500,"timeout":0,"withCredentials":true,"upload":
{},"_aborted":false,"_hasError":false,"_method":"POST","_response":"
{\"Message\":\"An error has occurred.\"}",
"_url":"https://smp.ums.edu.my/api/result/GetListOfKodSesiSem",
"_timedOut":false,"_trackingName":"unknown",
"_incrementalEvents":false,"responseHeaders":{"Date":"Sat, 30 Dec 2017
03:58:25
GMT","Content-Length":"36","X-Powered-By":"ARR/3.0","X-AspNet-
Version":"4.0.30319","Expires":"-1","Content-Type":"application/json;
charset=utf-8","Server":"Microsoft-IIS/10.0","Pragma":"no-cache","Cache-
Control":"no-cache"},"_requestId":null,"_headers":
{"accept":"application/json, text/plain, /","content-
type":"application/json;charset=utf-
8"},"_responseType":"","_sent":true,"_lowerCaseResponseHeaders":{"date":"Sat,
30 Dec 2017 03:58:25
GMT","content-length":"36","x-powered-by":"ARR/3.0","x-
aspnet-version":"4.0.30319","expires":"-1","content-type":"application/json; charset=utf-8","server":"Microsoft-IIS/10.0","pragma":"no-cache","cache-
control":"no-cache"},"_subscriptions":[],"responseURL":
"https://smp.ums.edu.my/api/result/GetListOfKodSesiSem"},"response":{"data":
{"Message":"An error has
occurred."},"status":500,"headers":{"date":"Sat, 30 Dec 2017 03:58:25
GMT","content-length":"36","x-powered-by":"ARR/3.0","x-
aspnet-version":"4.0.30319","expires":"-1","content-type":"application/json; charset=utf-8","server":"Microsoft-IIS/10.0","pragma":"no-cache","cache-
control":"no-cache"},"config":{"transformRequest":{},"transformResponse":
{},"timeout":0,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-
TOKEN","maxContentLength":-1,"headers":{"Accept":"application/json,
text/plain,
/","Content-Type":"application/json;charset=utf-8"},"method": "post","nomatrik":"BB16160907",
"url":"https://smp.ums.edu.my/api/result/GetListOfKodSesiSem","data":"
{\"Accept\":\"application/json\",\"Content-
Type\":\"application/json\"}"},"request":
{"UNSENT":0,"OPENED":1,"HEADERS_RECEIVED":2,"LOADING":3,"DONE":4,
"readyState":4,"status":500,"timeout":0,"withCredentials":true,"upload":
{},"_aborted":false,"_hasError":false,"_method":"POST","_response":"
{\"Message\":\"An error has occurred.\"}",
"_url":"https://smp.ums.edu.my/api/result/GetListOfKodSesiSem",
"_timedOut":false,"_trackingName":"unknown","_incrementalEvents":false, "responseHeaders":{"Date":"Sat, 30 Dec 2017 03:58:25 GMT","Content-
Length":"36","X-Powered-By":"ARR/3.0","X-AspNet-
Version":"4.0.30319","Expires":"-1","Content-Type":"application/json;
charset=utf-8","Server":"Microsoft-IIS/10.0","Pragma":"no-cache","Cache-
Control":"no-cache"},"_requestId":null,"_headers":
{"accept":"application/json, text/plain, /","content-
type":"application/json;charset=utf-
8"},"_responseType":"","_sent":true,"_lowerCaseResponseHeaders":{"date":"Sat,
30 Dec 2017 03:58:25
GMT","content-length":"36","x-powered-by":"ARR/3.0","x-
aspnet-version":"4.0.30319","expires":"-1","content-type":"application/json; charset=utf-8","server":"Microsoft-IIS/10.0","pragma":"no-cache","cache-
control":"no-cache"},"_subscriptions":
[],"responseURL":"https://smp.ums.edu.my/api/result/GetListOfKodSesiSem"}}}
Login action:
export const attemptLogin = (username, password) => async dispatch => {
let param = {
txtNomatrik: username,
txtPwd: password,
public_key: helper.PUBLIC_KEY,
secret_key: helper.SECRET_KEY
}
console.log(`${helper.ROOT_API_URL}/v1/basic/ad/std/login`)
let login_res = await
axios.post(`${helper.ROOT_API_URL}/v1/basic/ad/std/login`, param)
console.log(login_res.data);
await AsyncStorage.setItem('jwtToken',login_res.data.token);
if (login_res.data.status == 'Successful login') {
const { login } = login_res.data;
dispatch({ type: LOGIN_SUCCESS});
}
}
Problem
Your request is failing because you are not adding the JWT token to the headers.
Solution
Using Axios and with your code this should work. Evidently our big problem here was that you have to pass data even though it is empty. If we do not pass data it fails with error 500.
export const getSemcode = (username) => async dispatch => {
let jwtToken = await AsyncStorage.getItem('jwtToken').then((data) => {
console.log('this is semcode',data);
});
let config = {
method: 'POST',
url: 'url/to/sem',
headers: {
'content-type': 'application/x-www-form-urlencoded',
AntiTemperSignature: jwtToken,
UserID: '123456',
},
data: '',
json: true
};
try {
return axios(config)
.then((response) => {
console.log(response);
if (response.data.length > 0) {
const { code } = response.data;
dispatch({ type: SEMCODE_FETCH_SUCCESS, payload: { semCode: code } });
}
})
.catch((error) => {
console.log(error);
});
}
}
You are looking in the wrong place.
An error code 500 is returned by the remote server when it can't handle the request. In this case, I suspect that the POST to ${helper.ROOT_URL}/result/GetListOfKodSesiSem is failing. The axios library is a promise based library. Wrap the call in a try-catch block like this:
try {
console.log(`${helper.ROOT_URL}/result/GetListOfKodSesiSem`)
let code_res = await
axios.post(`${helper.ROOT_URL}/result/GetListOfKodSesiSem`, param)
console.log(code_res.data);
if (code_res.data.length > 0) {
const { code } = code_res.data;
dispatch({ type: SEMCODE_FETCH_SUCCESS, payload: { semCode: code }});
}
} catch (err) {
console.error(`Error received from axios.post: ${JSON.stringify(err)}`);
}
This will at least give you a view in your debug console on what is happening. You can then coordinate that call with any debug logs from the backend to figure out what the error really is.
Your root cause, however, is that the remote server is returning a Server Error (HTTP code 500) to your client.
I am using expressjs to retrieve data from elasticsearch and send back to my angular app at the front end. Currently I am facing a problem since expressjs doesn't wait until the query execution is finished. I searched for a solution for that and the community says use "Promise or Sync". But I cant figure out where should I use it. I tried to use it but I am getting errors.
This is where I am receiving the request from the frontend and calling the elasticsearch query for send the response.
api.post('/clsDependencies', (req, res) => {
classDependencies(req.body.className);
res.json(messages);
});
This the function for querying the elasticsearch.
function classDependencies(csName) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: csName
}
}
}
};
search('testclass', body)
.then(results => {
results.hits.hits.forEach((hit, index) => hit._source.dependencies.forEach(
function(myClass){
messages.push({text: myClass.methodSignature , owner: `\t${++nmb} -
${myClass.dependedntClass}`});
}))})
.catch(console.error);
};
Expected data gets initialized to the variable(messages) which I am trying to send back to the front end. But the variable doesn't get initialized at the time when response is send back. What Should I do to wait till the query execution finish before send back the data to frontend.
EDIT
messages is defined outside of both functions.
function classDirectory(className) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: className
}
}
}
};
return search('testclass', body).then(results => {
results.hits.hits.forEach((hit, index) =>
getDirectories(hit._source.JarFileName));
return messages;
})
.catch(function(err) {
// log the error, but keep the promise rejected
console.error(err);
throw err;
});
};
function getDirectories(jarName) {
let body = {
size: 20,
from: 0,
query: {
match: {
jarFileName: {
query: jarName
}
}
}
};
return search('testjar', body).then(results => {
results.hits.hits.forEach((hit, index) =>
messages.push({text: hit._source.jarFileName , owner: `\t${++nmb} -
${hit._source.directory}`})
);
return messages;
})
.catch(function(err) {
// log the error, but keep the promise rejected
console.error(err);
throw err;
});
};
The Javascript interpreter does not "block" when you make asynchronous calls. This has absolutely nothing to do with Express.
Your call to search() is non-blocking so while it's in process, classDependencies() returns and the rest of your code continues to run. This is the way asynchronous calls in Javascript work.
If you want to call res.json() when classDependencies() is done, then return a promise from it and call res.json() when that promise resolves.
You could do something like this:
api.post('/clsDependencies', (req, res) => {
classDependencies(req.body.className).then(messages => {
res.json(messages);
}).catch(err => {
res.status(500).send(something here);
});
});
function classDependencies(csName) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: csName
}
}
}
};
return search('testclass', body).then(results => {
let messages = [];
results.hits.hits.forEach((hit, index) => hit._source.dependencies.forEach(function(myClass) {
messages.push({
text: myClass.methodSignature,
owner: `\t${++nmb} - ${myClass.dependedntClass}`
});
}));
// make messages be the resolved value of the returns promise
return messages;
}).catch(function(err) {
// log the error, but keep the promise rejected
console.error(err);
throw err;
});
};
api.post('/clsDirectory', (req, res) => {
classDependency(req.body.className, res);
});
function classDependency(csName, cb) {
let body = {
size: 20,
from: 0,
query: {
match: {
ClassName: {
query: csName
}
}
}
};
search('testclass', body)
.then(results => {
results.hits.hits.forEach((hit, index) =>
hit._source.dependencies.forEach(
function(myClass){
messages.push({text: myClass.methodSignature , owner: `\t${++nmb} -
${myClass.dependedntClass}`});
}));
cb.json(messages);
})
.catch(console.error);
};
For some reason no error shows up in the server console when I start my hapi server with nodemon and navigate to http://localhost:3000/hapi-ext-fetch and this makes debugging very difficult. Here is my code:
var Hapi = require('hapi');
var Joi = require('joi');
var fetch = require('isomorphic-fetch');
var debugMode = { debug: { request: [ 'error', 'request-internal' ] }};
var server = new Hapi.Server(debugMode);
server.connection({ port: 3000 });
var myPlugin = {
register: function (server, options, next) {
server.route([
{
method: 'GET',
path: '/{name}',
handler: function ( request, reply ) {
throw new Error('this error isnt shown!');
},
config: {
validate: {
params: {
name: Joi.string().min(3).max(10)
}
}
}
}
]);
next();
}
};
myPlugin.register.attributes = {
name: 'myPlugin',
version: '1.0.0'
};
server.register([
{
register: myPlugin,
routes: {
prefix: '/test'
}
}
], function() {
server.ext( 'onPreResponse', ( request, reply ) => {
if ( typeof request.response.statusCode !== 'undefined' ) {
return reply.continue();
}
fetch('http://localhost:3000/test/whatever')
.then(function(result) {
reply(result);
})
.catch(function(err) {
reply('error on server side: ' + err.stack);
});
});
server.start((err) => {
if (err) {
throw err;
}
console.log('Server running at:', server.info.uri);
});
});
I'm using hapi 13.0.0
Can't say I totally understand your use case here and if this question will be helpful to other people. But what you're trying to do it seems is:
Send a request to /hapi-fetch-ext
Have that request 404
And then in an onPreResponse go fetch another route /test/whatever
Hope to see the "this error isn't shown error"
Not sure if you're aware but this is going to cause an infinite cycle of requests (your fetch will cause another onPreResponse and so on and so on). So you should probably only go fetch on a 404:
server.ext( 'onPreResponse', ( request, reply ) => {
if (request.response.isBoom && request.response.output.statusCode === 404) {
return fetch('http://localhost:3000/test/whatever')
.then(function(result) {
reply(result);
})
.catch(function(err) {
reply('error on server side: ' + err.stack);
});
}
return reply.continue();
});