How to fetch data from MySQL after POST completes - mysql

When the user clicks a button, I want the app to
read text from an input field
post it to MySQL to add as a new row
pull all rows from MySQL (including the new row)
update the react component state with the new data it just pulled
and rerender a list of all that data for the user
The problem I'm seeing is that pulling down all rows doesn't include the one that was just posted unless I include a manual delay (via setTimeout).
This is what my 2 functions look like
// gets all data, updates state, thus triggering react to rerender
listWords() {
setTimeout(() => fetch("http://localhost:9000/listWords")
.then(res => res.text())
.then(res => this.setState({ wordList: JSON.parse(res)})), 2000)
}
// sends new word to the MySQL database, then kicks off the listWords process to refetch all data
addWord() {
fetch("http://localhost:9000/addWord", {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
word: this.state.newWord
})
})
.then(res => res.json())
.then(this.listWords())
}
I shouldn't have to have that setTimeout in there, but when I remove it, the listWords update doesn't have the newly posted row.
My best guess is that either
I'm messing up the promise .then() paradigm, or
MySQL responds to my POST request but doesn't actually add the row until the GET has already completed, thereby missing out on the new row.
How can I ensure the POST has successfully completed before I pull the rows again?
I am using react, express, sequelize, and for the DB it's MySQL in a docker container.

You indeed made a mistake, a common trap which I myself fell into as well a few times :)
When calling addWord() it will evaluate then(this.listWords()). This will immediately call listWords().
Instead you pass a function to then(...) (instead of calling a function) for example like this:
.then(() => this.listWords())
or even like this:
.then(this.listWords)
Now, instead of passing the result of listWords() to then() it will pass a function that will call listWords() to then() thus only executing listWords() when the Promise reaches this then()
Example to make this behavior even clearer:
When calling a function function foo(arg1) {}, JS needs to know the value of arg1 before calling foo. Let's take the following piece of code:
foo(bar())
In this case, bar() must be called before foo(...) because the arg1 will be the returned value of bar(). In constrast to the following case:
foo(() => bar())
Now, arg1 will be a function instead of the returned value of bar(). This is equivalent to:
var arg = () => bar();
foo(arg);
In contrast to:
var arg = bar();
foo(arg);
Where it is obvious what will happen.
In chained functions like foo(arg1).bar(arg2).baz(arg3), all args will be evaluated before calling foo(...).
Some help to debug such problems: Use the Network Inspector in your browser. It will show the order of requests performed and in this example you would have seen that the GET request was actually performed before the POST request. This might not explain why it happens but you can understand the problem faster.

Related

Composable functions in Puppeteers page.Evaluate

I'm relatively new to puppeteer and I'm trying to understand the patterns that can be used to build more complex apis with it. I am building a cli where I am running a WebGL app in puppeteer which i call various functions in, and with my current implementation i have to copy and paste a lot of setup code.
Usually, in every cli command i have to setup pupeteer, setup the app and get access to its api object, and then run an arbitrary command on that api, and get the data back in node.
It looks something like this.
const {page, browser} = await createBrowser() // Here i setup the browser and add some script tags.
let data;
page.exposeFunction('extractData', (data) => {
data = data;
})
await page.evaluate(async (input) => {
// Setup work
const requestEvent = new CustomEvent('requestAppApi', {
api: undefined;
})
window.dispatchEvent(requestEvent);
const api = requestEvent.detail.api;
// Then i call some arbitrary function, that will
always return some data that gets extracted by the exposed function.
const data = api.arbitraryFunction(input);
window.extractData(data)
}, input)
What i would like is to wrap all of the setup code in a function, so that i could call it and just specify what to do with the api object once i have it.
My initial idea was to have a function that will take a callback that has this api object as a parameter.
const { page, browser } = wait createBrowser();
page.exposeFunction(async (input) =>
setupApiObject(((api) =>
api.callSomeFunction(input)
), input)
However, this does not work. I understand that puppeteer requires any communication between the node context and the browser to be serialised as json, and obviously a function cant be. Whats tripping me up is that I'm not actually wanting to call these methods in the node context, just have a way to reuse them. The actual data transfer is already handled by page.exposeFunction.
How would a more experienced puppeteer dev accomplish this?
I'll answer my own question here, since i managed to figure out a way to do it. Basically, you can use page.evaluate to create a function on the window object that can later be reused.
So i did something like
await page.evaluate(() => {
window.useApiObject = function(callback: (api) => void){
// Perform setup code
callback()
}
})
Meaning that later on i could use that method in the browser context and avoid redoing the setup code.
page.evaluate(() => {
window.useApiObject((api) => {
api.someMethod()
})
})

MySQL XDevApi Nodejs: How do I turn the result rows into JSON or something the client can check properties and rowset?

Data model
sess.getSchema("myschema").getTable("mymix").select(['recId', 'JSONdoc']).execute()
.then
(
goodResult=>
{
cbFunc(null, goodResult)
},
badResult =>
{
console.log("err");
cbFunc(badResult)
}
);
then Controller:
obj.findAll((er, data) => // (er, data) is cbFunc
{
if (er)
res.status(500).send(er);
else
res.status(200).send(data);
});
I can see there are 11 records returned, but res.status(200).send(data); becomes {} in Postman
Question 1, how do I turn the 11 records into navigatable resultset or JSON back to caller?
Question 2, according to https://dev.mysql.com/doc/dev/connector-nodejs/8.0/module-Result.html, goodResult.getAffectedItemsCount() supposed return 11, but is 0.
Question 1
Assuming you are using some kind of HTTP framework (seems to be the case according to the available details) you can't simply return back to the client the value that gets resolved by the Promise returned by execute().
In this case, what you get is an instance of a RowResult, which isn't directly serializable to JSON because access to the data itself happens with functions like fetchOne() and fetchAll(). These methods need to be called to build a serializable data structure (a plain JavaScript object or array for instance) in the server side before sending it back to the client.
In this case, the select() will yield only one result set, which means you can simply call fetchAll() and be done with it, or loop through that result set using fetchOne() (check the examples in the documentation). Which means you can do the following:
sess.getSchema("myschema").getTable("mymix").select(['recId', 'JSONdoc'])
.execute()
.then(goodResult => {
cbFunc(null, goodResult.fetchAll())
})
Question 2
Affected items are only items that were somehow changed by an operation. This is only the case for DML operations such as insert(), delete() and update(). In this case, since you are simply retrieving data from the table with select(), there will be no affected items.
Disclaimer: I'm the lead developer of the MySQL X DevAPI Connector for Node.js

How to put the results of MySQL database in a variable in Node.js?

I have a problem in my node.js application. I'm connecting to the database and getting the data
let users = {};
let currentUser = "example"; // this variable changes every time a user connects
connection.query('SELECT * FROM users WHERE name="'+currentUser+'"', function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
});
console.log(users[currentUser]);
When I try to console.log the result[0] from inside the function, it returns this:
RowDataPacket {
n: 11,
id: 'VKhDKmXF1s',
name: 'user3',
status: 'online',
socketID: 'JbZLNjKQK15ZkzTXAAAB',
level: 0,
xp: 0,
reg_date: 2018-07-16T20:37:45.000Z }
I want to put that result from MySQL into users.example or something like that, but when I try the code it returns undefined. So I tried console.log(users[currentUser].id) as an example and it shows an error
TypeError: Cannot read property 'id' of undefined
So how do I put the data from the result inside my variable users[currentUser]?
So how do I put the data from the result inside my variable users[currentUser]?
That's happening. The problem is how it is being tested.
The correct test is with the console.log inside the callback function(err, result)
function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
console.log(users[currentUser]);
});
Issue: users[currentUser] is still undefined outside that function
Well, yes and no. It is undefined in code that executes before the callback is fired.
And how do I fix that?
Anything that needs the result of the query must be executed from within the callback function, because that's the only code location where you know for certain that the data exists.
Well, I need that data outside in a global variable.
You can stick the query data in a global, but that doesn't solve the timing issue
of only accessing that global when it is defined and contains current data. That will cause lots of frustration.
If you don't want to call one or more specific functions to process the query data
within the callback, an alternative is to use a nodejs EventEmitter to coordinate the data production and data consumption.
Yet another alternative is to not use a callback function, and use Promise and/or async/await, both of which are supported by modern nodejs. This alternative doesn't involve global variables, but provides different ways to code the fact that some operations need to wait for the results of others.
connection.query is an async call. The console.log is called before the query fetches the data from db.
Because you're using a callback, your console.log is happening before the result comes back from the database. Additionally, you have a type in your code user -> users.
let users = {};
let currentUser = "example"; // this variable changes every time a user connects
connection.query('SELECT * FROM users WHERE name="'+currentUser+'"', function (err, result) {
if (err) console.log(err);
users[currentUser] = result[0];
console.log(users[currentUser]);
});
Side note: research "SQL Injection" before you use this code. The way you're building your query opens you up for anyone to access your database. Using a library like squel.js or knex.js will help you avoid this.
For an explanation of why things happen in the order they do, take a look at the JavaScript event loop.

How do I use the data returned by an ajax call?

I am trying to return an array of data inside a JSON object that is return from a URL, I can see the data that is being returned using console.log.
However when trying to catch the return array in a variable for example:
var arr = list();
console.log(arr.length);
The length being output by this code is "0" despite the fact that the data returned has content (so the length is greater than zero). How can I use the data?
list: function() {
var grades = [];
$.getJSON(
"https://api.mongolab.com/api/1/databases", function(data) {
console.log(data);
grades [0] = data[0].name;
console.log(grades.length);
});
return grades;
},
The issue you are facing is easy to get snagged on if you aren't used to the concept of asynchronous calls! Never fear, you'll get there.
What's happening is that when you call the AJAX, your code continues to process even though the request has not completed. This is because AJAX requests could take a long time (usually a few seconds) and if the browser had to sit and wait, the user would be staring in angsuish at a frozen screen.
So how do you use the result of your AJAX call?
Take a closer look at the getJSON documentation and you will see a few hints. Asynchronous functions like getJSON can be handled in two ways: Promises or Callbacks. They serve a very similar purpose because they are just two different ways to let you specify what to do once your AJAX is finished.
Callbacks let you pass in a function to getJSON. Your function will get called once the AJAX is finished. You're actually already using a callback in the example code you wrote, it's just that your callback is being defined inside of your list() method so it isn't very useful.
Promises let you pass in a function to the Promise returned by getJSON, which will get called once the AJAX is finished.
Since you are doing all this inside of a method, you have to decide which one you're going to support. You can either have your method take in callbacks (and pass them along) or you can have your method return the promise returned by getJSON. I suggest you do both!
Check it out:
var list = function(success) {
// Pass in the callback that was passed into this function. getJSON will call it when the data arrives.
var promise = $.getJSON("https://api.mongolab.com/api/1/databases", success)
// by returning the promise that getJSON provides, we allow the caller to specify the promise handlers
return promise;
}
// Using callbacks
list(function(grades) {
console.log(grades);
});
// Using promises
list()
.success(function(grades) {
console.log(grades);
});

Ext.Data.Connection request result not rendering on grid?

Ive been stuck with this issue for some time
My JSon store fields need to retrieve some more info:
{ name: "ExpirationDate", convert: convertDate },
{ name: "AffectedObject", convert: GetValue },
The date method is working fine but the result from GetValue is not being rendered on the grid even though the code is working and returning the correct value (either with or without JSON):
function GetValue(v) {
var conn = new Ext.data.Connection();
conn.request({
url: 'test/GetObjectByID',
method: 'POST',
params: { id: v },
scriptTag: true,
success: function (response) {
console.log(response.responseText);
ReturnResult(response.responseText);
},
failure: function () {
Ext.Msg.alert('Status', 'Something went wrong');
}
});
function ReturnResult(str) {
return Ext.util.JSON.decode(str.toString());
}
Any idea why the result is not not showing?
The 'convert' property is expecting an immediate return value. Your GetValue function is issuing an asynchronous request and then immediately returning nothing. At some arbitrary point in the future after the request completes the 'success' function is called, but it is no longer connected to the original call so any value it may return is meaningless.
Though you could make it work by replacing the use of Ext.data.Connection with manually constructed synchronous requests, I recommend reconsidering the mechanism by which you are getting this data. Issuing a separate request for every record in your data store is less than optimal.
The best solution is to bring that additional data in on the server side and include it in the response to the store proxy's initial request. If that cannot be done then you can try listening to the store's 'load' event and performing conversion for all loaded records with a single request. Any grids or other views you have reading from the store may have to be configured to display dummy text in place of the missing data until the conversion request completes.