Fetch request not returning any data in console, or on front end - javascript

When I submit my form, I am not returning any data, not even in my console. I am trying to return details from WHOIS regarding the URL that is searched, and am getting nothing back.
Can anyone provide any advice as to why this might be the case?
Here is my front end script tag, after my form:
document.getElementById('search').addEventListener('submit', function(e) { e.preventDefault(); getDetails(); })
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
return new Promise(function (resolve, reject) {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
}).then(async response => {
if (response.ok) {
response.json().then(json => resolve(json))
console.log(data);
} else {
response.json().then(json => reject(json))
}
}).catch(async error => {
reject(error)
})
})
}
On my express backend I am using req.params.url if that helps provide any context at all...
My Status Code is 200, and all appears to be normal in the Headers tab...

You have a mix of promise and async syntax, which is confusing, let's translate it first by unpicking the promise and then into await (if you can use async then do, it's easer than Promise/then):
document.getElementById('search').addEventListener(
'submit',
function(e) {
e.preventDefault();
getDetails();
});
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {})
{
// Fetch will throw an exception if it can't connect to the service
const response = await fetch(url, {
method: 'GET',
headers: { 'Content-Type': 'text/html' },
body: JSON.stringify(data)
});
if (response.ok)
return await response.json();
// We could connect but got an error back from the service
// There may not be a response body, so response.json() or .body() might crash
throw new Error(`Error from server ${response.status}: ${response.statusText}`;
// We don't need catch, as any exception in an await will cascade up anyway
}
This makes it much more readable, and it's apparent that getDetails doesn't make any changes itself, it just returns the JSON from the service. The fix needs to be in the event listener - it needs to do something with that result:
document.getElementById('search').addEventListener(
'submit',
async e => {
e.preventDefault();
const searchResult = await getDetails();
// Do something to show the results, populate #results
const resultsElement = document.getElementById('results');
resultsElement.innerText = JSON.stringify(searchResult);
});

You are mis-using async/await. Try this:
async function getDetails(url = `http://localhost:3000/lookup/${url}`, data = {}) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'text/html'
},
body: JSON.stringify(data)
});
const json = await response.json();
if (response.ok) {
console.log(json);
return json;
} else {
throw new Error(json);
}
}
In essence await is a replacement for then (but you can only use it in functions marked with async).

Related

NodeJS Fetch Not Waiting

I'm trying to force Node to wait for either a success or a failure. I understood fetch to return a promise and I thought I told it how to handle both.
The following code does not honor the await I asked it to do:
async function getAccessToken() {
...
let fetchResult = await fetch(argumentParserResult.authorizationUrl, {
method: 'POST',
body: formData,
headers: headers
}).then(success => {
console.log("Success reached. " + JSON.stringify(success));
process.exit(2);
}, other => {
console.log("Other reached. " + JSON.stringify(other));
process.exit(3);
});
console.log('## after fetch fetchResult=' + fetchResult);
...
}
You might think that the await would cause it to, wait for the Promise to complete, but instead it leaves the whole function, and goes back to the caller. It does not print the '## after fetch fetchResult=' line. Neither the failure, nor success handler is executed.
I should point out that it also does not appear to make the requested POST call either. Instead, it sees that request and does something completely different without raising any exception.
Why is it not honoring the 'await' keyword whatsoever?
--- If I try the try/catch approach as follows:
async function getAccessToken() {
console.log('##getAccessToken BP1');
if (argumentParserResult.authenticationScheme == 'OAUTH2') {
console.log('##getAccessToken BP2');
const fetch = require('node-fetch');
const url = argumentParserResult.resourceUrl;
console.log('##getAccessToken BP3');
let formData = new URLSearchParams({
'grant_type': 'client_credentials',
'client_id': argumentParserResult.clientId,
'scope': argumentParserResult.clientScope,
'client_secret': argumentParserResult.clientSecret
})
console.log('##getAccessToken BP4');
let headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
console.log('##getAccessToken BP5');
console.log('POST ' + argumentParserResult.authorizationUrl);
console.log(JSON.stringify(formData));
console.log('##getAccessToken BP6');
try {
console.log('##getAccessToken BP7');
const response = await fetch(argumentParserResult.authorizationUrl, {
method: 'POST',
body: formData,
headers,
});
console.log('##getAccessToken BP8');
console.log(`Success reached.`, JSON.stringify(response));
const json = await response.json();
console.log('##getAccessToken BP9');
console.log(`Other reached.`, json);
return json;
} catch (error) {
console.log('##getAccessToken BP10');
console.log(`!! something went wrong`, error.message);
console.error(error);
return error;
} finally {
console.log('##getAccessToken BP11');
console.log(`fetch finished`);
}
console.log('##getAccessToken BP12');
}
console.log('##getAccessToken BP13');
return "Should not have reached this point";
}
I get
##getAccessToken BP1
##getAccessToken BP2
##getAccessToken BP3
##getAccessToken BP4
##getAccessToken BP5
POST https://some-url
{}
##getAccessToken BP6
##getAccessToken BP7
As you can see, it goes just inside of the try block, then goes back to the caller without triggering the finally, error handlers or the logging after the fetch.
Using the .then approach as follows:
async function getAccessToken() {
console.log('##getAccessToken BP1');
if (argumentParserResult.authenticationScheme == 'OAUTH2') {
console.log('##getAccessToken BP2');
const fetch = require('node-fetch');
const url = argumentParserResult.resourceUrl;
console.log('##BP1.9');
let formData = new URLSearchParams({
'grant_type': 'client_credentials',
'client_id': argumentParserResult.clientId,
'scope': argumentParserResult.clientScope,
'client_secret': argumentParserResult.clientSecret
})
console.log('##getAccessToken BP3');
let headers = {
'Content-Type': 'application/x-www-form-urlencoded'
};
console.log('##getAccessToken BP4');
console.log('POST ' + argumentParserResult.authorizationUrl);
console.log(JSON.stringify(formData));
let response = await fetch(argumentParserResult.authorizationUrl, {
method: 'POST',
body: formData,
headers: headers
}).then(success => {
console.log('##getAccessToken BP5');
console.log("Success reached. " + JSON.stringify(success));
return success // !--> LOOK HERE, you should return the success variable
}).catch(e => {
console.log('##getAccessToken BP6');
console.log(e) // !--> LOOK HERE, if you catch the error, no error will be thrown to the caller
return e
});
console.log('##getAccessToken BP7');
console.log('## after fetch fetchResult=', fetchResult); // !--> LOOK HERE, this log will always log something now, it could be the responso or the error
}
console.log('##getAccessToken BP8');
}
I get these logs:
##getAccessToken BP1
##getAccessToken BP2
##BP1.9
##getAccessToken BP3
##getAccessToken BP4
POST https://login.microsoftonline.com/5a9bb941-ba53-48d3-b086-2927fea7bf01/oauth2/v2.0/token
{}
As you can see above, it goes just to the point of the fetch, then returns to the calling function.
In neither case, can I see any evidence that the fetch was ever called.
Try this:
async function getAccessToken() {
try {
const response = await fetch(argumentParserResult.authorizationUrl, {
method: 'POST',
body: formData,
headers,
});
console.log(`Success reached.`, JSON.stringify(response));
const json = await response.json();
console.log(`Other reached.`, json);
} catch (error) {
console.log(`!! something went wrong`, error.message);
console.error(error);
} finally {
console.log(`fetch finished`);
}
}
You don't need to use thenable object when writing with async/await, instead, catch the error with a try catch bloc, and just get the async value using return of awaited function.
You are mixing await and then. It is not forbidden, but in most simple case you don't need it.
Solution without then:
async function getAccessToken() {
try {
console.log('fetching data') // this log will always appear as first log, before fetching data
let fetchResult = await fetch(argumentParserResult.authorizationUrl,
{
method: 'POST',
body: formData,
headers: headers
})
let jsonR = await fetchResult.json()
console.log('fetch done') // this log will appear only if fetch is done with no errors
} catch (e) {
console.error('something went wrong', e) // this log will appear only if there was an error
}
console.log('after all') // this log will appear always, after fetch (even if fetch fails or not)
}
Solution with then:
async function getAccessToken() {
let fetchResult = await fetch(argumentParserResult.authorizationUrl, {
method: 'POST',
body: formData,
headers: headers
}).then(success => {
console.log("Success reached. " + JSON.stringify(success));
return success // !--> LOOK HERE, you should return the success variable
}).catch(e => {
console.log(e) // !--> LOOK HERE, if you catch the error, no error will be thrown to the caller
return e
});
console.log('## after fetch fetchResult=', fetchResult); // !--> LOOK HERE, this log will always log something now, it could be the responso or the error
}
As you can see, error handling is not quite convenient in the second solution. That's why you should not mix await with then, unless you know what you are doing
The point of async/await is to get rid of the callbacks and make the code more procedural. Your code:
async function getAccessToken() {
...
let fetchResult = await fetch(argumentParserResult.authorizationUrl, {
method: 'POST',
body: formData,
headers: headers
})
.then( success => {
console.log("Success reached. " + JSON.stringify(success));
process.exit(2);
}, other => {
console.log("Other reached. " + JSON.stringify(other));
process.exit(3);
});
console.log('## after fetch fetchResult=' + fetchResult);
...
}
fails, because you are
Waiting for fetch() to resolve and return a result, and
In your then() chain, you are
Invoking process.exit() in the case of either success or failure.
Than means you kill the entire process as soon as the call to fetch() resolves with either a success or a failure.
If you do something like this:
async function getAccessToken() {
...
const opts = {
method: 'POST',
body: formData,
headers: headers
};
const {json, err} = await execFetch( argumentParserResult.authorizationUrl, opts );
if ( err ) {
console.log("that didn't work!", err);
process.exit(1);
}
...
}
async function execFetch( url, opts ) {
const response = { json: undefined, err: undefined };
const { res, err } = await fetch( argumentParserResult.authorizationUrl, opts )
.then( res => ({ res , err: undefined }) )
.catch( err => ({ res: undefined , err }) );
if ( err ) {
response.err = err;
}
else if ( !res.ok ) {
// non-2xx HTTP status
response.err = new Error(`${res.status}: ${res.statusText}`);
}
else {
// the 2xx happy path: deserialize the JSON response body into a JS object
response.json = res.json();
}
return response;
}
Your call to fetch() will always succeed and hand you back a tuple with a json and an err property.
A successful call will return something like this:
{
json: { a: 1, b: 2, c: 3, },
err: undefined,
}
Whilst a call to fetch() that fails will return something like this:
{
json: undefined ,
err: /* some error object with details about what went south */,
}

Rejected promise with SyntaxError: Unexpected token i in JSON at position 0 when fetching raw file from Gitlab

I'm trying to fetch a raw file from Gitlab repository, following official documentation.
The functions are the following:
methods: {
async getProjects(url, method, payload) {
const token = this.$store.getters.token
const headers = {
'Content-Type': 'application/json',
'Private-Token': token
}
const response = await fetch(url, {
method: method,
headers: headers,
body: JSON.stringify(payload)
})
return response.json()
},
[...]
async addNewProject() {
const payload = {
"name": "newProjectName",
"namespace_id": 12,
"description": "description"
}
this.getProjects("https://gitlab.example.com/api/v4/projects/", "POST", payload)
.then(data => {
console.log(data.id)
})
.catch((e) => {
console.error(e)
})
let rawFile = null
try {
rawFile = await JSON.parse(this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET"))
} catch (e) {
rawFile = this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET")
}
console.log(rawFile)
}
}
Logging the rawFile shows a pending Promise object with rejected state and the SyntaxError as in the title.
Is it possible that the raw format of the file is causing this error? If so, how to prevent it?
There are a few things going on.
gitlab.example.com doesn't exist, so it's difficult to know what results you actually get
You are passing the promise to JSON.parse. You need to use await or then to use the response:
The end of your getProjects calls response.json() which will parse the json. You don't need to parse it again.
How about using:
let rawFile = await this.getProjects("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master", "GET")
If the response is json, that would give you an object, but if the response is not json, you want to use response.text() instead. You'd need a different function:
async getTextFile(url) {
const token = this.$store.getters.token
const headers = {
'Private-Token': token
}
const response = await fetch(url, {
method: "GET",
headers: headers
})
return response.text()
}
then call it like this:
let rawFile = await this.getTextFile("https://gitlab.example.com/api/v4/projects/1/repository/files/readme%2Emd/raw?ref=master")

Vue - returning the result of a synchronous method

I'm struggling to return the synchronous results of the method below.
I call the method from a different method:
var result = this.getVendor(id)
console.log(result)
Here is the fetch method:
methods: {
async getData(id) {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
.then(res => res.json())
.then(data => {
return data;
});
await response;
}
}
How do I return the results response of the getData() function to show in the console?
Async functions Always return a promise.
You can use the await syntax to return it properly.
async getData(id) {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
const data = await response.json()
return data
}
You can access the data out of that function anywhere you call it.
let data = null
object.getData(2)
.then(x => {
data = x
})
Also if you are going to use async await make sure to use try and catch to handle any errors that come up.
async getData(id) {
try {
const response = await fetch(`${API_URL}api/${id}`, {
method: "GET",
headers: {
authorization: `Bearer ${localStorage.token}`
}
})
const data = await response.json()
return data
}
} catch(err) {
console.error(err)
}

Subscribe http.post that is placed inside a promise Angular 6

It gets complicated to me when I mix the promise with subscribe and another async task together.
This is my auth service:
getCurrentUserToken(){
return new Promise((resolve,reject)=>{
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
resolve(idToken)
}).catch(function(error) {
reject(error)
});
})
}
This is my HTTP service:
sendEmail(email) {
return this.authService.getCurrentUserToken().then(token => {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Basic server-Password',
})
};
let data = email
data['idToken'] = token
return this.http.post(this.apiServer + 'sendEmail', data, httpOptions)
})
}
This is how I call the sendEmail(email) function at the component:
Observable.fromPromise(this.httpService.sendEmail(element)).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
))
I have to pass currentUserToken to the API to let the API authenticate the user session. Still, both of the the getCurrentUserToken() sendEmail() are running in async, so I have to use Promise to pass the Token to sendEmail() function, and let the sendEmail function to call the API to send the email.
Without the promise, I am able to subscribe to the http.post like this:
this.httpService.sendEmail(element).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
))
Unfortunately, I screwed it up when I added the promise into it, and the console.log is returning this:
Observable {_isScalar: false, source: Observable, operator: MapOperator}
Please advise on how to subscribe to the http.post that is placed inside the Promise.
There's seriously no need of Complicating things here.
I'll use async/await syntax here and for that, we'll have to work with Promises instead of Observables. Good thing is, we can leverage the toPromise() method on an Observable value to change it to a Promise
Focus on my comments in the code as well
Here's the implementation
For getCurrentUserToken
getCurrentUserToken() {
return firebase.auth().currentUser.getIdToken(true);
// This will already return a Promise<string>
// So no need to do a .then and then return from there.
}
For sendEmail
async sendEmail(email) {
// Since getCurrentUserToken returns a Promise<string> we can await it
const token = await this.authService.getCurrentUserToken();
// token will now have the Current User Token
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Basic server-Password',
})
};
let data = email
data['idToken'] = token
return this.http.post(this.apiServer + 'sendEmail', data, httpOptions).toPromise();
// Notice how we're calling the .toPromise() method here
// to change Observable into a Promise
}
How to use it?
This code will go in your Component Method where you were previously calling this.httpService.sendEmail. DO MAKE SURE TO MARK THAT FUNCTION AS async THOUGH.
// We can only await something in a function which is declared of type async
async sendEmail() {
try {
const data = await this.httpService.sendEmail(element);
// Since sendEmail again returns a Promise, I can await it.
console.log(data);
} catch (error) {
console.log(error);
}
}
Why don't we use Observable instead of Promises here.
getCurrentUserToken() {
return new Observable(obs => {
firebase
.auth()
.currentUser.getIdToken(/* forceRefresh */ true)
.then(function(idToken) {
obs.next(idToken);
obs.complete();
})
.catch(function(error) {
obs.error(error);
});
});
}
sendEmail(email): Observable {
return new Observable(obs => {
this.authService.getCurrentUserToken().subscribe(token => {
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
Authorization: 'Basic server-Password'
})
};
let data = email;
data['idToken'] = token;
this.http
.post(this.apiServer + 'sendEmail', data, httpOptions)
.subscribe(
result => {
obs.next(result);
obs.complete();
},
error => {
obs.error();
}
);
});
});
}
// now call the service from Component like this.
this.httpService.sendEmail(element).subscribe(
data3 => {
console.log(data3)
}, error => {
console.log(error)
}
));

How to handle null response in fetch api

I building a react-native app and using fetch api for handling server request, it is working fine if json returned from the server is not null, but if the response from the server is null it will give me an error-"Json Parse error:Unexpected EOF", below is the code used by me for fetch, I tried to set break-point while debugging to see what is coming in response when null is returned from the server, I am unable to find something on which I can put some check and see if response is null before parsing it, so need help
return fetch(url, //service url{
method: type, // get or post
headers: {
'Accept': 'application/json',
'Content-Type': contentType,
},
body: data //some input parameters
}).then((response) => {
return response.json();
})
.then((responseJson) => {
request.onSuccess(responseJson); // success callback
})
.catch((error) => {
request.onError(error); // error callback
console.error(error);
});
There is a good answer here, but in my case I needed access to the response object after response.text() returns:
function buildResult(response) {
// response.json() crashes on null response bodies
// return {
// data: response.json(),
// identityToken: response.identityToken // sliding expiration...
// };
return new Promise((resolve, reject) => {
response.text().then(body => {
resolve({
data: body.length ? JSON.parse(body) : null,
identityToken: response.identityToken // sliding expiration...
});
}).catch(err => {
reject(err);
});
});
}
//
// the api fetch function
//
function apiFetch(url) {
return fetch(url)
.then(checkStatus)
.then(parseIdentityToken)
.then(buildResult);
}
If the json response null Instead of using response.json() use response.text()
fetch(path)
.then(function (response) {
return response.text()
}).then(function (data) {
resolve(data.length == 0 ? null : JSON.parse(data))
}).catch(err => {
reject(err);
})
If you want to сheck the response request for emptiness:
const response = await fetch(url, options); // your url and options
if (response.ok) {
const contentType = response.headers.get('content-type');
if (contentType && contentType.indexOf('application/json') !== -1) {
const json = await response.json();
successCb(json); // Write your script.
} else {
successCb(); // if the request is successful but the response is empty. Write your script.
}
}

Categories

Resources