Javascript - Return json from fetch in an Object - json

I'm trying to make an application to get the recipes from https://edamam.com and I'm using fetch and Request object.
I need to make 3 request, and i thought that most beautiful way for do it is make an Object and a method that return the data in JSON.
I declarated into constructor a variable called this.dataJson, and i want to save there the data in JSON from the response. For that purpose i use this.
The problem is that i have a undefined variable.
.then( data => {this.dataJson=data;
console.log(data)} )
This is all my code.
class Recipe{
constructor(url){
this.url=url;
this.dataJson;
this.response;
}
getJson(){
var obj;
fetch(new Request(this.url,{method: 'GET'}))
.then( response => response.json())
.then( data => {this.dataJson=data;
console.log(data)} )
.catch( e => console.error( 'Something went wrong' ) );
}
getData(){
console.log("NO UNDFEIND"+this.dataJson);
}
}
const pa= new Recipe('https://api.edamam.com/search?...');
pa.getJson();
pa.getData();
I'm new studying OOP in JS and more new in Fetch requests...
If you guys can help me... Thanks very much!

Here's a solution using async-await (and a placeholder API):
class Recipe {
constructor(url) {
this.url = url;
this.dataJson;
this.response;
}
// the async keyword ensures that this function returns
// a Promise object -> we can use .then() later (1)
async getJson() {
try {
const response = await fetch(new Request(this.url, {
method: 'GET'
}))
const json = await response.json()
this.dataJson = json
} catch (e) {
console.error('Something went wrong', e)
}
}
getData() {
console.log("NO UNDFEIND:", this.dataJson);
}
}
const pa = new Recipe('https://jsonplaceholder.typicode.com/todos/1');
// 1 - here we can use the "then", as pa.getJson() returns
// a Promise object
pa.getJson()
.then(() => {
pa.getData()
});
If we want to stay closer to your code, then:
class Recipe {
constructor(url) {
this.url = url;
this.dataJson;
this.response;
}
getJson() {
// var obj; // not needed
// the "fetch" always returns a Promise object
return fetch(new Request(this.url, { // return the fetch!
method: 'GET'
}))
.then(response => response.json())
.then(data => {
this.dataJson = data;
// console.log(data) // not needed
})
.catch(e => console.error('Something went wrong'));
}
getData() {
console.log("NO UNDFEIND:", this.dataJson); // different syntax here
}
}
const pa = new Recipe('https://jsonplaceholder.typicode.com/todos/1');
// using "then", because the "fetch" returned a Promise object
pa.getJson()
.then(() => {
pa.getData();
});
The problem with your original code is that you initiate the request (pa.getJson()) and then immediately (on the next line) you want to read the data (pa.getData()). pa.getData() is called synchronously (so it happens in milliseconds), but the request is asynchronous - the data needs time to arrive (probably hundreds of milliseconds) - so, it's not there when you try to read it (it simply hasn't arrived yet).
To avoid this you have to use a technique to handle this asynchronous nature of the request:
use a callback function (blee - so last decade)
use a Promise object with then() (much better) or async-await (yeee!)
and call the pa.getData() when the response has arrived (inside the callback function, in the then() or after awaiting the result).

Related

Promise or Callback, which one is better to use with NodeJS?

I found that there are 2 different ways to write node functions using promise or callback, the first way is like following defining the findByEmail function:
class Users{
static async findByEmail(email: any ) : Promise<Users | undefined>{
const user: any = await Pools.execute(
"SELECT * FROM users WHERE email = ?",
[email])
.then(rows => {
return rows[0];
})
.catch(err => console.log(err) );
return user;
};
}
router.post(
"/api/users/signin",
async (req: Request, res: Response , next: NextFunction) => {
const { email, password } = req.body;
const existingUser = await Users.findByEmail(email);
});
And the second way would be like:
declare global {
namespace Express {
interface Response {
user?: Users;
}
}
}
static async findByEmail(req: Request, res: Response) {
const user = await Pools.execute(
"SELECT * FROM users WHERE email = ?",
[req.body.email])
.then(rows => {
res.user = rows[0];
})
.catch(err => console.log(err) );
};
router.post(
"/api/users/signin",
async (req: Request, res: Response , next: NextFunction) => {
await Users.findByEmail(req, res);
const existingUser = res.user;
});
I am not sure if this is a "opinion based" question or not? However my purpose of asking this is to know which way is a better practice and why? According to performance and other possible issues?
In particular I like to know either it is better to write functions with the return value or using response object to add the returning value to that inside the then() function, like .then(res.user = user) instead of const user = await pool.execute(SELECT ...) ?
Here's a way to impalement that makes the following improvements:
Makes findByEmail() into a utility function that is independent of the req and res objects and thus can be used generally.
Properly propagates all errors from findByEmail() back to the caller.
Implements some validation checks on incoming email field and makes separate error path for that.
Log all errors on the server
Check for all error conditions from the database request
Not mixing .then() and await.
Here's the code:
// resolves to null if email not found
// rejects if there's a database error
static async findByEmail(email) {
const rows = await Pools.execute("SELECT * FROM users WHERE email = ?", [email]);
if (!rows || !rows.length || !rows[0]) {
return null;
}
return rows[0];
};
router.post("/api/users/signin", async (req: Request, res: Response, next: NextFunction) => {
try {
// validate incoming parameters
if (!req.body.email) {
let errMsg = "No email value present in incoming signin request";
console.log(errMsg);
res.status(400).send(errMsg);
return;
}
let user = await Users.findByEmail(req.body.email);
if (!user) {
// do whatever you would do if user tries to signin with non-existent email
// presumably return something like a 404 status
} else {
// do whatever you wanted to do here with the user object after login
}
} catch(e) {
// some sort of server error here, probably a database error, not the client's fault
console.log(e);
res.sendStatus(500);
}
});

TextDecoder failing in ES6 Promise recursion

I'm attempting to query an API which responds with a ReadableStream of XML.
The code below uses a recursive Promise. Recursive because it sometimes doesn't decode the stream in a singular iteration and this is whats causing my headache.
While I'm successfully fetching the data, for some reason the decoding stage doesn't complete sometimes, which leads me to believe it's when the stream is too large for a single iteration.
componentDidMount() {
fetch("http://thecatapi.com/api/images/get?format=xml&size=med&results_per_page=9")
.then((response) => {
console.log('fetch complete');
this.untangleCats(response);
})
.catch(error => {
this.state.somethingWrong = true;
console.error(error);
});
}
untangleCats({body}) {
let reader = body.getReader(),
string = "",
read;
reader.read().then(read = (result) => {
if(result.done) {
console.log('untangling complete'); // Sometimes not reaching here
this.herdingCats(string);
return;
}
string += new TextDecoder("utf-8").decode(result.value);
}).then(reader.read().then(read));
}
I think that the next iteration was sometimes being called before the current iteration had completed, leading to incorrectly concatenation of the decoded XML.
I converted the function from sync to async and as a regular recursive method of the component rather than a recursive promise with a method.
constructor({mode}) {
super();
this.state = {
mode,
string: "",
cats: [],
somethingWrong: false
};
}
componentDidMount() {
fetch("http://thecatapi.com/api/images/get?format=xml&size=med&results_per_page=9")
.then( response => this.untangleCats( response.body.getReader() ) )
.catch(error => {
this.setState({somethingWrong: true});
console.error(error);
});
}
async untangleCats(reader) {
const {value, done} = await reader.read();
if (done) {
this.herdingCats();
return;
}
this.setState({
string: this.state.string += new TextDecoder("utf-8").decode(value)
});
return this.untangleCats(reader);
}

How to handle Promise in nodejs

I'm trying to execute a callback function in nodejs, using expressjs and angular 2 (i don't know if the angular2 part it's relevant).
What I do is:
I have a formular in angular2, with that I send a get request to my API route, then I send the text field in the formular to the URL via get, then I do an MYSQL query to look into a phonebook database, and I'm expecting to get a complete user with his details, from the phonebook.
Formular:
<div class="container">
<div class="col-md-4">
<h1>Addressbook</h1>
<form [formGroup]="searchForm" (ngSubmit)="doSearch($event)">
<input formControlName="searchString" type="text" placeholder="Name">
<button type="submit">Search</button>
</form>
</div>
</div>
First function, doSearch:
doSearch(event) {
let formData = this.searchForm.value;
var searchString = this.searchForm.value.searchString;
this.http.get('/phonebook/search/'+searchString, function(req, res){}).subscribe(
function(response) {
console.log("Success Response");
},
function(error) { console.log("Error happened" + error)},
function() { console.log("the subscription is completed")}
);
}
This calls to the route sending a parameter, so not so hard.
Now the create router gets into the game:
public static create(router: Router, basePath: string) {
console.log("[SearchRoute::create] Creating routes for /search.");
// call the function for retrieving the address book results
router.get(basePath + "/search/:searchString", (req: Request, res: Response, next: NextFunction) => {
console.log("## [SearchRoute] Called GET /search.");
var object = searchUser(req);
console.log(object);
});
}
And finally, the function searchUser gets called:
function searchUser(req: Request) {
console.log("searchUser Function executed.");
var searchString = req.params.searchString;
var query = p_query('SELECT XXXX')
.then(function (results) {
console.log("query executed and all okay");
return (results);
})
.catch(function (error) {
console.error("Wooopsi", error);
});
console.log("query result: "+query);
}
Additionally, I post here the new query function that I build to be able to handle promises (which I don't know if it was the best choice):
function p_query(q) {
return new Promise(function (resolve, reject) {
// The Promise constructor should catch any errors thrown on
// this tick. Alternately, try/catch and reject(err) on catch.
myMYSQL.db.query(
q,
function (error, results) {
if (error)
reject(error);
resolve(results);
});
})
};
So, what I actually want to do, what's my issue?
I want to send the result of the query back to the client (the angular2 formular), and I was not being able to do it...
So after this really long post, I really appreciate if you read til here, and sorry for the complicated question!
PS: I know i explain myself really bad :(
Regards,
Daniel
In this official angular 2 documentation on the http client they propose to put the http logic into a separate service. I've setup it similar to the example just for a search.service.ts:
import { Injectable } from '#angular/core';
import { Http, Response,Headers, RequestOptions,URLSearchParams }
from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
#Injectable()
export class SearchService {
constructor(private http: Http) {
}
getSearchResult(searchString) : Observable<any> {
return this.http.get('/phonebook/search/'+searchString)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body;
}
private handleError (error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
return Observable.throw(errMsg);
}
}
In your component import the service and do the code snippets:
// don't forget to put the service in the app.modul or the component providers!
constructur(public mySearchService : SearchService) {}
// in your doSearch of your component:
doSearch(event) {
let formData = this.searchForm.value;
var searchString = this.searchForm.value.searchString;
mySearchService.getSearchResult(searchString).subscribe(
data => mylist.data, // or which datastructure I want to write to.
error => console.error(error) // or how I log the errors..
);
}
EDIT: The search_user in your database model:
function searchUser(searchString) {
console.log("searchUser Function executed.");
return myMYSQL.db.select('phonebookentry', {
pbe_lastname: searchString, pbe_syncstate: 'new'
}) // returning the promise/observable to the main function...
} // Currently I don't know, how the returned data looks like.
On the node/express side in the router send it with res.json EDIT: use asynchronous call to searchUser:
router.get(basePath + "/search/:searchString",
(req: Request, res: Response, next: NextFunction) => {
console.log("## [SearchRoute] Called GET /search.");
searchUser(req)
.then( data => res.json(data);console.log(data) )
.catch (error => console.log(error));
});
You should go with recursive callback with each query results try to enjoy the beauty of async platform.
Send data to client via
res.send(data);
Your answer it's completely perfect, i understand everything! The only problem that i'm facing now, its this one:
I'm calling the function searchUser, and it doesn't return anything, just an undefined object, so i quess i'm not doing the return correctly.
That's my searchUser function:
function searchUser(searchString) {
console.log("searchUser Function executed.");
myMYSQL.db.select('phonebookentry', {
pbe_lastname: searchString,
pbe_syncstate: 'new'
}).then(function (user) {
console.log("user before: "+user);
return (user);
}).catch(function (err) {
console.log(err);
})}
Thank you so much for your useful answer! I'm almost finished here

Aurelia typescript load json service

I am trying to create a class that will have two functions:
1) Load items from a json stored in my local server and return that variable with all the items.
2) Return a single item by id.
The problem is I want to use these two methods from different modules, and I do not know how to go about implementing the module and using it. So far, I have been able to implement the http part with aurelia's fetch client, but I don't know how to make the function:
function getItems() {
// some http request code
return fetchedItems;
}
Because the code in aurelia.io does something like this (which I have tried and actually works if I print the data):
import 'fetch';
import {HttpClient} from "aurelia-fetch-client";
export function getItems(url) {
let client = new HttpClient();
client.configure(config => {
config
.withBaseUrl('api/')
.withDefaults({
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'X-Requested-With': 'Fetch'
}
})
.withInterceptor({
request(request) {
console.log(`Requesting ${request.method} ${request.url}`);
return request;
},
response(response) {
console.log(`Received ${response.status} ${response.url}`);
return response;
}
});
});
client.fetch(url)
.then(response => response.json())
.then(data => {
console.log(data);
});
}
All this works ok. The point is that instead of doing 'console.log(data);' I want to return it, but so far the only thing that seems to work is assigning the returned items to a local class variable with 'this.items = data'. I would be ok with this so long as I get a function that allows to do this:
let items = getItems();
And
let item = getItemById(id);
EDIT: SOLVED
Users should note that, in order for this to work, they should have this in their tsconfig.js:
"target": "es6"
Because async/await requires at least ES2015.
Use async / await
If you're using TypeScript and targeting ES6, you can use the await/async keywords.
export async function getItems(url) {
let client = new HttpClient();
client.configure(config => {
config
.withBaseUrl('api/')
.withDefaults({
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'X-Requested-With': 'Fetch'
}
})
.withInterceptor({
request(request) {
console.log(`Requesting ${request.method} ${request.url}`);
return request;
},
response(response) {
console.log(`Received ${response.status} ${response.url}`);
return response;
}
});
});
return await client.fetch(url)
.then(response => response.json());
}
client.fetch returns a promise, so you just have to return it:
return client.fetch(url)
.then(response => response.json());
To use the function:
getItems(url)
.then(data => this.someProperty = data);

(Serverless Framework Module) wait for promise to resolve before return statement

Is it possible for a Serverless Framework module to wait for the "resolve" of a promise before returning?
I'm aware that promises themselves can't do that, but different frameworks/libraries (express, Jasmine, hapijs, etc.) solve this by having a method that defines when to return. I need something like this:
let http = require('http'),
Promise = require('bluebird');
let action = (done) => {
return new Promise((resolve, reject) => {
http
.get('http://domain.com', resolve.bind({}, 'all good!'))
.on('error', reject.bind({}, 'all wrong!'));
})
.then((response) => {
console.log('Result', response);
return done(response); // <----------- I wan't to see this as the response
// of the lambda function
});
};
module.exports.run = (event, context, cb) => cb(null, action(done));
No, promises don't do that. It's impossible to read from the future, and don't want to (cannot) block. Your action is still asynchronous.
But given that your export takes a callback anyway, you can simply invoke that asynchronously:
module.exports.run = (event, context, cb) => {
action().then(res => cb(null, res), err=>cb(err));
};
It would be better though of course if you just returned the promise.