DELETE with Express.JS - javascript

I am trying to get my DELETE method to work. When the function is ran it is going straight into the throw Error line. In the console it is printing the following two errors. “404 Not Found” “Error Uncaught (in promise) Error"
Here is my client side code
async function deleteItem(item) {
let requestOptions = {
method: "DELETE",
headers: { "Content-Type": "application/json" },
}
const response = await fetch("/delete/:id", requestOptions);
if (response.status != 204) {
throw Error("Cannot delete your item from list");
}
return item;
}
And server side code
app.delete("/delete/:id"),
async (request, res) => {
try {
await Item.deleteOne({ _id: request.params.id });
res.sendStatus(204);
} catch {
res.sendStatus(404);
console.log('test');
}
};

You need to pass the ID of the "thing" that you want to delete in the client side code.
async function deleteItem(item) {
let requestOptions = {
method: "DELETE",
headers: { "Content-Type": "application/json" },
}
const response = await fetch("/delete/:id", requestOptions); // <----- HERE!
if (response.status != 204) {
throw Error("Cannot delete your item from list");
}
return item;
}
it should be something like (assuming that the item object has the id)
const response = await fetch(`/delete/${ item.id }`, requestOptions);
e.g.: /delete/12423

You aren't catching the promise returned from your fetch() function causing it to throw an error. Also, it looks like you aren't sending the request with a valid id.
You could fix that by doing
fetch(...).then((response) => {
if (response.status != 204) {
throw Error("Cannot delete your item from list");
}
}).catch((err) => {
console.error(err); // handle error
});
or
const response = await fetch(...).catch((err) => {
console.error(err); // handle error
});
// if the promise is rejected and the catch block is executed then 'response' will be undefined
if (!response) return;
if (response.status != 204) {
throw Error("Cannot delete your item from list");
}
edit: or of course you could not catch the promise rejection and just send the request with a valid id

Related

SWR not working properly with async fetch

Recently updated SWR - now for some reason my data is not fetching properly.
const { data: expressionsData, error: expressionsError } = useSWRImmutable(
[`dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
apiRequest
);
Using this fetching,
import firebase from "./firebase";
export async function apiRequest(path, method = "GET", data) {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
//this is a workaround due to the backend responses not being built for this util.
if (path == "dashboard/get-settings") {
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.error === "error") {
throw new CustomError(response.code, response.messages);
} else {
return response;
}
});
}
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
console.log("error", response);
if (response.status === "error") {
// Automatically signout user if accessToken is no longer valid
if (response.code === "auth/invalid-user-token") {
firebase.auth().signOut();
}
throw new CustomError(response.code, response.message);
} else {
return response.data;
}
});
}
// Create an Error with custom message and code
export function CustomError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
// Check if a indexDb database exists
export function indexedDbdatabaseExists(dbname, callback) {
const req = window.indexedDB.open(dbname);
let existed = true;
req.onsuccess = function () {
req.result.close();
if (!existed) window.indexedDB.deleteDatabase(dbname);
callback(existed);
};
req.onupgradeneeded = function () {
existed = false;
callback(existed);
};
}
Now I'm looking at this StackOverflow thread,
useSWR doesn't work with async fetcher function
And thinking I'll just remake the fetcher to be without Async. I'm just wondering why this has stopped working though in general, and if I can just keep my existing codebase.
The error is a 400 message, it only happens with this expressions API call which takes longer to load due to the amount of data I think,
xxxx/dashboard/expression/get-expression-analytics?startTime=1648183720488&endTime=1650865720488 400 (Bad Request)
with error log
These calls are working fine, they have substantly less data though.
const { data: overall, error: psychometricError } = useSWRImmutable(
`dashboard/psychometric/get-psychometric-home?starttime=infinite`,
apiRequest
);
const { data: sentimentData, error: sentimentError } = useSWRImmutable(
[`dashboard/sentiment/get-sentiment-timefilter?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
fetchSentiment
);
Made an update to the fetch call to be more readable and specifically about the URL pathway.
import firebase from './firebase';
// Create an Error with custom message and code
export function CustomError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
export async function expressionsRequest(path, method = 'GET') {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
return fetch(`/api/${path}`, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
if (!response.ok) {
throw `Server error: [${response.status}] [${response.statusText}] [${response.url}]`;
}
return response.json();
})
.then((receivedJson) => {
if (receivedJson.status === 'error') {
// Automatically signout user if accessToken is no longer valid
if (receivedJson.code === 'auth/invalid-user-token') {
firebase.auth().signOut();
}
throw new CustomError(receivedJson.code, receivedJson.message);
} else {
return receivedJson.data;
}
})
.catch((err) => {
console.debug('Error in fetch', err);
throw err;
});
}
Additionally, this is what the lambda function (using next API folder) looks like,
const requireAuth = require('../../_require-auth');
const { db } = require('../../_sql');
export default requireAuth(async (req, res) => {
const { uid: id } = req.user;
const startTime = Math.round(req.query.startTime * 0.001);
const endTime = Math.round(req.query.endTime * 0.001);
const parameters = [id, startTime, endTime];
//sql injection definitely possible here, need to work out better method of dealing with this.
const sqlText = `SELECT a,b,c,d,e,f,g,h,i FROM tablename WHERE a=$1 AND i BETWEEN $2 AND $3;`;
try {
const { rows } = await db.query(sqlText, parameters);
return res.status(200).json({
code: 0,
data: rows,
});
} catch (error) {
return res.status(200).json({
code: 0,
message: 'Error occurred in getting tablename',
error,
});
}
});
using postman with the same query, i.e.,
curl --location --request GET 'http://localhost:3000/api/dashboard/expression/get-expression-analytics?startTime=1648387240382&endTime=1651069240382' \
--header 'Authorization: Bearer xxxx' \
--data-raw ''
Successfully returns a response with data attached.
Based on your first code blocks, the startDate value is getting passed into the fetcher as method, and the endDate value is getting passed into the fetcher as data. This is based on the useSWR docs about passing in an array for the key argument: https://swr.vercel.app/docs/arguments#multiple-arguments
If the code you provided is correct, I'd assume the 400 is coming from trying to pass in a random value for the method option for fetch.
This should be fixed by only passing the API endpoint path into useSWR instead of an array:
const { data: expressionsData, error: expressionsError } = useSWRImmutable(
`dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`,
apiRequest
);

Why is it not waiting for an awaited Promise.all to resolve?

I previously had working code that inefficiently called awaits on every element of an iterable. I'm in the process of refactoring to use Promise.All. However, my code is not waiting for the Promise.All to resolve before executive further code.
Specficially, the purgeRequestPromises line executes before the initial Promise.All resolves. I'm not sure why that is? retrieveSurrogateKey is an async function, so its return line will be wrapped in a resolved promise.
try {
//retrieve surrogate key associated with each URL/file updated in push to S3
const surrogateKeyPromises = urlArray.map(url => this.retrieveSurrogateKey(url));
const surrogateKeyArray = await Promise.all(surrogateKeyPromises).catch(console.log);
//purge each surrogate key
const purgeRequestPromises = surrogateKeyArray.map(surrogateKey => this.requestPurgeOfSurrogateKey(surrogateKey));
await Promise.all(purgeRequestPromises);
// GET request the URLs to warm cache for our users
const warmCachePromises = urlArray.map(url => this.warmCache(url));
await Promise.all(warmCachePromises)
} catch (error) {
logger.save(`${'(prod)'.padEnd(15)}error in purge cache: ${error}`);
throw error
}
async retrieveSurrogateKey(url) {
try {
axios({
method: 'HEAD',
url: url,
headers: headers,
}).then(response => {
console.log("this is the response status: ", response.status)
if (response.status === 200) {
console.log("this is the surrogate key!! ", response.headers['surrogate-key'])
return response.headers['surrogate-key'];
}
});
} catch (error) {
logger.save(`${'(prod)'.padEnd(15)}error in retrieveSurrogateKey: ${error}`);
throw error
}
}
I know that the purgeRequestPromises executes early, because I get errors complaining that I've set my Surrogate-Key header as undefined in my HEAD request:
async requestPurgeOfSurrogateKey(surrogateKey) {
headers['Surrogate-Key'] = surrogateKey
try {
axios({
method: `POST`,
url: `https://api.fastly.com/service/${fastlyServiceId}/purge/${surrogateKey}`,
path: `/service/${fastlyServiceId}/purge${surrogateKey}`,
headers: headers,
})
.then(response => {
console.log("the status code for purging!! ", response.status)
if (response.status === 200) {
return true
}
});
} catch (error) {
logger.save(`${'(prod)'.padEnd(15)}error in requestPurgeOfSurrogateKey: ${error}`);
throw error;
}
}
retrieveSurrogateKey is synchronously returning undefined: the value in the try block is a promise and no errors are thrown synchronously, so the catch clause is never executed and execution falls out the bottom, returning undefined from the function body.
You could try something like:
function retrieveSurrogateKey(url) { // returns a promise
return axios({
// ^^^^^^
method: 'HEAD',
url: url,
headers: headers,
}).then(response => {
console.log("this is the response status: ", response.status)
if (response.status === 200) {
console.log("this is the surrogate key!! ", response.headers['surrogate-key'])
return response.headers['surrogate-key'];
}
}).catch(error => {
logger.save(`${'(prod)'.padEnd(15)}error in retrieveSurrogateKey: ${error}`);
throw error;
});
}
Note that it is superfluous to declare a function returning a promise as async if it doesn't use await. There is also a secondary problem in this line:
const surrogateKeyArray = await Promise.all(surrogateKeyPromises).catch(console.log);
The catch clause will will fulfill the promise chain unless the error is rethrown. You could (perhaps) leave off the .catch clause or recode it as
.catch( err=> { console.log(err); throw err} );
You don't have to remove async from retrieveSurrogateKey() in order for it to work. In fact it's more readable if you don't. As was already explained, the problem is that the promise returned by retrieveSurrogateKey() does not follow the completion of the promise returned by the call to axios(). You need to await it:
async retrieveSurrogateKey(url) {
try {
const response = await axios({
method: 'HEAD',
url,
headers,
});
console.log('this is the response status: ', response.status);
if (response.status === 200) {
const surrogateKey = response.headers['surrogate-key'];
console.log('this is the surrogate key!! ', surrogateKey);
return surrogateKey;
}
} catch (error) {
logger.save(`${'(prod)'.padEnd(15)}error in retrieveSurrogateKey: ${error}`);
throw error;
}
}
This preserves the same logic you currently have, but you'll notice that when response.status !== 200, you end up with a resolved promise of undefined, rather than a rejected promise. You might want to use validateStatus to assert the exact status of 200. By default axios resolves any response with a status >= 200 and < 300:
async retrieveSurrogateKey(url) {
try {
const response = await axios({
method: 'HEAD',
url,
headers,
validateStatus(status) {
return status === 200;
}
});
const surrogateKey = response.headers['surrogate-key'];
console.log('this is the surrogate key!! ', surrogateKey);
return surrogateKey;
} catch (error) {
logger.save(`${'(prod)'.padEnd(15)}error in retrieveSurrogateKey: ${error}`);
throw error;
}
}
This way, you're always guaranteed a surrogate key, or a rejected promise.

How to resolve the converting circular structure to JSON issue in fetch

I want to get the list of the repositories by providing a username.
Below is what I have done so far.
router.get('/github/:username', (req, res) => {
try {
const url = `https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc&client_id=${config.get('githubClientId')}&clientSecret=${config.get('githubSecret')}`;
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
console.log(url);
fetch(url, {
method: 'GET',
headers: headers,
}).then(data => {
if (data.status !== 200) {
return res.status(404).send({
msg: 'No GitHub profile found'
});
} else {
return data.json();
}
}).then(result => res.json(result));
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
})
When I use the dynamically created URL in the browser, I get the response
When I pass valid user-name, I get the repositories in Postman, where I am testing the API
When I pass invalid user-name, I get the following error
(node:18684) UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON
at JSON.stringify (<anonymous>)
at stringify (E:\Connector\node_modules\express\lib\response.js:1123:12)
at ServerResponse.json (E:\Connector\node_modules\express\lib\response.js:260:14)
at fetch.then.then.result (E:\Connector\routes\api\profile.js:396:31)
at process._tickCallback (internal/process/next_tick.js:68:7)
(node:18684) UnhandledPromiseRejectionWarning: Unhandled promise rejection. 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(). (rejection id: 2)
Can anybody tell me how to resolve this error? I have looked at many resources but could not find any concrete.
The problem is the return res.status(404).send(…) in the first then callback. The second then callback will then try to res.json(result) that return value.
You should instead write
router.get('/github/:username', (req, res) => {
const url = `https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc&client_id=${config.get('githubClientId')}&clientSecret=${config.get('githubSecret')}`;
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};
console.log(url);
fetch(url, {
method: 'GET',
headers: headers,
}).then(data => {
if (data.status !== 200) {
res.status(404).send({
msg: 'No GitHub profile found'
});
} else {
return data.json().then(result => {
res.json(result);
});
}
}).catch(err => {
console.error(err.message);
res.status(500).send('Server Error');
});
})

How to wrap JavaScript fetch in a function - unhandled promise rejection

I'm trying to write a wrapper function for the JavaScript fetch command.
I took the example code from this post:
function fetchAPI(url, data, method = 'POST') {
const headers = {
'Authorization': `Token ${getAuthToken()}`,
};
return fetch(url, { headers, 'method': method, 'body': data })
.then(response => {
if (response.ok) {
const contentType = response.headers.get('Content-Type') || '';
if (contentType.includes('application/json')) {
return response.json().catch(error => {
return Promise.reject(new Error('Invalid JSON: ' + error.message));
});
}
if (contentType.includes('text/html')) {
return response.text().then(html => {
return {
'page_type': 'generic',
'html': html
};
}).catch(error => {
return Promise.reject(new Error('HTML error: ' + error.message));
});
}
return Promise.reject(new Error('Invalid content type: ' + contentType));
}
if (response.status === 404) {
return Promise.reject(new Error('Page not found: ' + url));
}
return response.json().then(res => {
// if the response is ok but the server rejected the request, e.g. because of a wrong password, we want to display the reason
// the information is contained in the json()
// there may be more than one error
let errors = [];
Object.keys(res).forEach((key) => {
errors.push(`${key}: ${res[key]}`);
});
return Promise.reject(new Error(errors)
);
});
}).catch(error => {
return Promise.reject(new Error(error.message));
});
};
And I'm calling it like this:
fetchAPI('/api/v1/rest-auth/password/change/', formData).then(response => {
console.log('response ', response);
});
Edit: I have modified the code to display information returned by the server if the request is ok but refused, for example because of an invalid password. You have to interrogate the response json if ok == false.
A valid URL fetch is fine. But if there is an error, I see an Unhandled Rejection (Error): error message.
Why is it that the rejects are unhandled even though they are in catch blocks? What's the secret sauce here?
The way to avoid an unhandled promise rejection, is to handle it:
fetchAPI('/api/v1/rest-auth/password/change/', formData).then(response => {
console.log('response ', response);
}).catch(error => {
// do something meaningful here.
});;

Unexpected url in react-native

I have been using fetch method of react-native to call api but unfortunately its saying unexpected url below is my code
const myRequest = new Request("http://ipadress::portAddess/console/login/LoginForm.jsp", { method: 'GET' });
fetch(myRequest).then(response => {
if (response.status === 200) {
return response;
} else {
throw new Error('Something went wrong on api server!');
}
}).then(response => {
console.debug(response);
// ...
}).catch(error => {
console.error(error);
});
Why are you using new Request here?
You have to pass url and params to fetch function directly. Here's an example for your code:
fetch("http://ipadress::portAddess/console/login/LoginForm.jsp", {
method: 'GET',
})
.then(...)

Categories