I know NestJS handle all the status code who are different from 200-299 as an error, and I already created my own HttpExceptionFilter implements ExceptionFilter to handle errors.
Well, this is my current scenario. One of my providers is sending me the response with status code 500 in case a value is wrong (not a real server error), but in the response it comes the message and description.
Is there a way to catch inline the exception without transfer the response to my ExceptionFilter?
const options = {
requestBody,
headers: {
Authorization: `Bearer 12345`,
'Content-Type': 'text/plain',
},
data: requestBody,
};
const endpoint = http://myclientendpoint.com;
try {
const response = await this.httpService
.post(endpoint, requestBody, options)
.toPromise();
if (
response == null ||
response == undefined ||
response.data == null ||
response.data == undefined
) {
return null;
}
console.log(response.data);
return await response.data;
} catch (error) {
console.log('>>> THIS IS TRIGGERED WITHOUT THE RESPONSE BODY');
console.log(error);
return null;
}
It is possible adding a try catch and getting the body content with: error.response.data
This is the example:
try {
const response = await this.httpService
.post(endpoint, requestBody, options)
.toPromise();
if (
response == null ||
response == undefined ||
response.data == null ||
response.data == undefined
) {
return null;
}
console.log(response.data);
return await response.data;
} catch (error) {
if (error.response.status === 500) {
console.log('> TRYING TO HANDLED 500 status code');
console.log(error.response.data);
return error.response.data;
}
if (error.response.status === 403) {
console.log('> TRYING TO HANDLED 403 status code');
console.log(error.response.data);
return error.response.data;
}
if (error.response.status === 505) {
console.log('> TRYING TO HANDLED 505 status code');
console.log(error.response.data);
return error.response.data;
}
console.log(error);
return null;
}
Related
Why I am not getting the desired output as i given in res variable for returning from this route
#Get('/signout')
signOut(#Session() session: any) {
session.userId = null;
let res = JSON.parse(
JSON.stringify({
status: 'success',
}),
);
console.log( "--> ", res);
if (!session.userId) {
return res;
}
}
i am getting empty json object as response
{}
i tried printing out my res var it outputs fine.
To help debug your code have you tried just returning the response without any logic ?
#Get('/signout')
signOut(#Session() session: any) {
const res = {
status: 'success',
}
console.log( "--> ", JSON.stringify(res));
return res;
}
and then try
#Get('/signout')
signOut(#Session() session: any) {
session.userId = null;
const res = {
status: 'success',
};
console.log( "--> ", JSON.stringify(res));
if (!session.userId) {
return res;
} else {
throw new HttpException('Error', 500);
}
}
json response can be generated with two ways in NestJs, either use response object of express Response, or just add a header on the top of route handler method
Solution with applying header;
#Get('/')
#Header('Content-type', 'application/json')
handleGet(){
return JSON.stringify({ status : "success"})
}
Solution with using response imported from Express.
#Get('/')
#Header('Content-type', 'application/json')
handleGet(#Response() res : Response){
return res.json({ status : "success"});
}
These are the two possible ways that i found to return JSON response from a route handler method. further if all your responses need to be JSON, you may create an interceptor to alter your response before it is sent
I have a react native app and a nodejs backend. I'm using refresh and access tokens for authentication. My RN code looks like this (simplified):
const onRequest = (config) => {
console.log('data before request', config.data);
config.headers = {
'Authorization': `Bearer ${accessToken.current}`,
'phosphor-device-id': `${deviceId}`,
'Accept': 'application/json',
};
return config;
};
const onRequestError = (error) => {
Promise.reject(error);
};
const onResponse = (response) => {
return response;
};
const onResponseError = async (error) => {
if (error.response.status === 401 && !oConfig._retry) {
oConfig._retry = true;
return refreshAccessToken().then((token) => {
accessToken.current = token;
apiCall.defaults.headers['Authorization'] = `Bearer ${token}`;
oConfig.headers['Authorization'] = `Bearer ${token}`;
return apiCall.request(oConfig);
})
.catch((error) => {
accessToken.current = null;
setAuth(false);
Promise.reject(error);
});
} else {
return Promise.reject(error);
}
}
apiCall.interceptors.request.use(onRequest, onRequestError);
apiCall.interceptors.response.use(onResponse, onResponseError);
In my nodejs code, I have middleware to check for incoming requests. It looks like this:
app.use((req, res, next) => {
console.log(`${req.method}: ${req.url}`);
if (Object.keys(req.query).length > 0) {
console.log('query params', req.query);
}
if (Object.keys(req.body).length > 0) {
console.log('body params', req.body);
}
next();
});
When the user submits an item with an expired access token, the response is "catched" by the axios response interceptor, and a new access token is generated and send back to the user. This works. Also, with return apiCall.request(oConfig);, the original request is retried. This does not work.
The first time , I get some logs about the request in my server console about the received req.body (thanks to the middleware). In my react native console, I see this body-object also (thanks to console.log('date before request', config.data); So when the request is retried, the full original body/data-object is send again to the server. But the second time, the req.body-object on the server (or what the server receives) is empty. I don't get any output in my node.js-middleware about the req.body-object, and my controller fails because it needs this content.
This only happens with POST requests (and req.body). When a "refresh-access-token" happens with a GET-request, the req.query-object is still complete in the second try.
What can possibly be wrong with this?
Edit: ofc I'm using express.json()
Try using error.config instead of oConfig
const onResponseError = async (error) => {
if (error.response.status === 401 && ! error.config._retry) {
error.config._retry = true;
return refreshAccessToken().then((token) => {
accessToken.current = token;
apiCall.defaults.headers['Authorization'] = `Bearer ${token}`;
error.config.headers['Authorization'] = `Bearer ${token}`;
return apiCall.request(error.config);
})
.catch((error) => {
accessToken.current = null;
setAuth(false);
Promise.reject(error);
});
} else {
return Promise.reject(error);
}
}
I don't know why, but if I do a POST response using request(options,callback), no request.Response object is passed to the callback, I need the response object to check for the X-CSRF-TOKEN header if it exists or not, and if it does exist I need to set it to a dictionary http_header and if the statusCode is 403 then I would resend the request and resolve the Promise.
var http_header = {
"Content-Type": "application/json",
"Cookie": ".ROBLOSECURITY="+ROBLOSECURITY
}
function MakeRbxReq(http_method, url, payload) {
let jsonpayload
try {
if (payload == undefined) {return}
jsonpayload = JSON.stringify(payload)
} finally {}
var options = {
uri: "http://" + url,
body: jsonpayload || "",
methpd: http_method,
headers: http_header
}
return new Promise(resolve => {
request(options, (_,response) => {
if (http_method.toUpperCase() == "POST" || http_method.toUpperCase() == "DELETE" || http_method.toUpperCase() == "PUT" || http_method.toUpperCase() == "PATCH") {
console.log("RES POST: "+response) // This would be undefined somehow
// The rest of the code below will error
if (response.headers["X-CSRF-TOKEN"] != undefined) {
http_header["X-CSRF-TOKEN"] = response.headers["X-CSRF-TOKEN"]
options.headers = http_header
if (response.statusCode == 403) {
request(options, (_,res) => {
resolve({statusCode: res.statusCode, body: res.body})
})
return
}
}
}
resolve({statusCode: response.statusCode, body: response.body})
return
})
})
}
async function t() {
await MakeRbxReq("POST","https://economy.roblox.com/v1/purchases/products/123",{})
}
t()
I am creating a loop to create/update users using functions with built-in promises:
for (const user of usersjson.users) {
let getuser = getUser(url, okapikey, user[fieldMap.externalSystemId],
'externalSystemId'); //Check if user exists on the server
await getuser
.then(async (data) => {
if (data.users.length != 0) { //If user exists in the array
update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
promises.push(update); //Store function in array
i++;
} else {
create = createNewUser(url, okapikey, createduser);//Create create function
promises.push(create); //Store function in array
i++;
}
}).catch((err) => {
console.error(err);
});
if (promises.length == 50 || i == usersjson.users.length) {//Run functions in batches of 50
await Promise.allSettled(promises)
.then((responses)=> {
for (const response of responses) { //For each promise response
if (response.status == 'fulfilled') { //If fulfilled
if (response.value.status == 204) {
console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
} else {
if (response.value.status == 201 && response.value.headers.location) {
console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
} else {
console.log(response.value.headers.location);
}
}
} else { //Handle rejections
console.log(`There was an error with the user:${response.value}`);
}
}
}).catch((err)=> {
console.log(err);
});
promises=[]; //Empty Promise array
}
}
async function updateUser(url, token, user, userid)
{
return new Promise((resolve, reject) => {
//Create headers for put request
const options = {
method: "put",
headers: {
'x-okapi-token': token,
'x-okapi-tenant':'tenant',
'Content-type':"application/json"
}
};
//Make API get call
user.id=userid; //Adding the required field ID to the JSON
axios.put(`${url}/users/${userid}`, JSON.stringify(user), options)
.then(response => {
if (response.status == 204) {
resolve(response);
} else {
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
}
}).catch((err) => {
console.error(`Error Code: ${err.response.status}`);
if (typeof err.response.data == 'string') {
console.error(err.response.data);
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
} else if (err.response.data.errors[0].message) {
console.error(`Error Text: ${err.response.data.errors[0].message}`);
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
} else {
reject(`Error Code: ${err.response.status}\nError Text: ${err.response.data.errors[0].message}\nError Status: ${err}`);
}
console.log(err.response);
});
});
};
async function createNewUser (url, token, user) {
return new Promise((resolve, reject) => {
//Create headers for put request
const options = {
headers: {
'X-Okapi-token': token,
'Content-type':"application/json"
}
};
//Make API get call
axios.post(`${url}/users`, JSON.stringify(user), options)
.then(response => {
if (response.status == 201) {
resolve(response);
} else {
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
}
}).catch((err) => {
console.error(`Error on ${user.externalSystemId}: ${err}`);
if (err.response.data && typeof err.response.data == 'string') {
console.error(err.response.data);
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
} else if (err.response.data.errors[0].message) {
console.error(`Error Text: ${err.response.data.errors[0].message}`);
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
} else {
reject(`Error Code: ${err.response.status}: ${user.externalSystemId},\nError Text: ${err.response.data.errors[0].message},\nError Status: ${err}`)
}
});
});
};
const getUsers = (url,user,password) =>
{
return new Promise((resolve, reject) => {
//Create headers for POST request
const options = {
method: 'post',
headers: {
'Authorization': 'Basic '+Buffer.from(`${user}:${password}`).toString('base64')
}
}
//Make API get call
axios.get(url, options)
.then(response => {
resolve(response.data);
}).catch((err) => {
console.error(err);
reject(err);
});
});
};
The code and loop works fine when every promise is fulfilled, but once a promise is rejected, the loop breaks. I get the error message, for example:
Error on XXX: Error: Request failed with status code 422 Error Text:
User with this username already exists
node:internal/process/promises:246
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing
inside of an async function without a catch block, or by rejecting a
promise which was not handled with .catch(). The promise rejected with
the reason "Error Code: 422: XXX, Error Text: User with this username
already exists, Error Status: Error: Request failed with status code
422".] { }
Looking at the code and error, I believe this comes from the "createNewUser" function.
I'm not sure why the code breaks - I added catches to all the functions, handled rejections, and added catch statements in the main body of the code, but the loop still breaks.
What I need is for the loop to continue as usual even if one function fails (I will later change the log from console.log to an actual log file).
update = updateUser(url, okapikey, createduser, data.users[0].id);//Create update function
promises.push(update); //Store function in array
create = createNewUser(url, okapikey, createduser);//Create create function
promises.push(create); //Store function in array
This is inaccurate. You are not storing functions in that array, you actually call the updateUser/createNewUser functions here and store the resulting promises in the array. Then, your loop goes on to sequentially (because of the await) do more getUser operations before actually calling Promise.allSettled on the promises array. In the meantime, some of the promises might already have been rejected without having any handlers attached to them.
This is basically the same problem as discussed in Waiting for more than one concurrent await operation and Any difference between await Promise.all() and multiple await?.
To fix it, collect actual functions that you can execute later in your array:
let functions = [];
for (const user of usersjson.users) {
i++;
try {
const data = await getUser(url, okapikey, user[fieldMap.externalSystemId], 'externalSystemId');
if (data.users.length != 0) {
functions.push(() =>
// ^^^^^
updateUser(url, okapikey, createduser, data.users[0].id)
); // Create update function and store it in array
} else {
functions.push(() =>
// ^^^^^
createNewUser(url, okapikey, createduser)
); // Create create function and store it in array
}
} catch(err) {
console.error(err);
}
if (functions.length == 50 || i == usersjson.users.length) { // in batches of 50
const promises = functions.map(fn => fn()); // Run functions
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
const responses = await Promise.allSettled(promises);
for (const response of responses) {
if (response.status == 'fulfilled') {
if (response.value.status == 204) {
console.log(`${response.value.status}: User ${response.value.request.path.substring(7)} was updated.`);
} else {
if (response.value.status == 201 && response.value.headers.location) {
console.log(`${response.value.status}: User ${response.value.headers['location']} was created.`);
} else {
console.log(response.value.headers.location);
}
}
} else {
console.log(`There was an error with the user:${response.value}`);
}
}
functions = []; // empty functions array
}
}
(I've tried to avoid awaiting any .then(…) chains)
I have the following code:
User.getConfByID(userID)
.then((item)=>{
if(item.length == 0){
res.status(400).json({error:"NO_USER_FOUND"})
}else{
if(item[0].token == token){
if((Math.abs(Date.now() - item[0].conf_iat)) > tokenValid){
res.status(401).json({error: "TOKEN_INVALID"})
}else{
return mariaDBTemplates.updateOneRowTemplate("User_confirmation", {confirmed:1}, "user_id", userID)
}
}else{
res.status(401).json({error: "TOKEN_NOT_SAME"})
}
}
})
.then(()=>{
res.status(200).json({success: "CONFIRMED"})
})
.catch((err)=>{
res.status(500).json({error: err.message})
})
You see I have different kinds of error messages with different kinds of status codes. When I run this code, it always gives me this warning:
Error: Can't set headers after they are sent
I think this is because i don't "break" the Promise after sending a response right?. But how can I solve this? Any suggestions?
Cheerio
your problem is with your promise chain. in your first .then, you always set the response with res, but the next .then in the chain tries to set the response again. Note that not returning anything from a promise is the same as return Promise.resolve(undefined);.
here's how I would do it:
User.getConfByID(userID)
.then((item) => {
if(item.length == 0)
return { statusCode: 400, body: { error: "NO_USER_FOUND" } };
else {
if(item[0].token == token) {
if((Math.abs(Date.now() - item[0].conf_iat)) > tokenValid)
return { statusCode: 401, body: { error: "TOKEN_INVALID" } };
else {
//not sure what this returns, but it looks like this is
//what you're trying to return the 200 for
mariaDBTemplates.updateOneRowTemplate("User_confirmation", { confirmed: 1 }, "user_id", userID);
return { statusCode: 200, body: { success: "CONFIRMED" } };
}
} else
return { statusCode: 401, body: { error: "TOKEN_NOT_SAME" } };
}
})
.then((result) => {
res.status(result.statusCode).json(result.body);
})
.catch((err) => {
res.status(500).json({ error: err.message });
});
Also note that returning a value from a promise is the same as returning Promise.resolve(value);, and will continue the promise chain.