React button connection with database through axios.post() - javascript

I have 4 inputs and button which takes all data from them and sends to my PostreSQL database through axios.post() request. Not clearly understand how .then() is working. So, here is my button code which just calls this.addNewPainting function:
<button onClick={ this.addNewPainting }>Submit</button>
Here is my addNewPainting function:
addNewPainting() {
axios.post(`http://localhost:333/api/add`, {
title: this.state.titleInput,
year: this.state.yearInput,
size: this.state.yearInput,
location: this.state.locationInput
})
.then(function(response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
Before this project, I used to put response.data to the array with this.setState, but now I have the database and I'm just stuck.
Here is my controller function:
add_painting: (req, res, next) => {
const db = req.app.get('db');
const { title, year, size, location } = req.body;
console.log(title, year, size, location);
db.add_painting([ title, year, size, location ])
.then( () => res.status(200).send() )
.then( () => res.status(500).send() );
}
And the endpoint:
app.post('/api/add', paintings_controller.add_painting);

For future reading (becase you requested it): I'm not an expert using promises, but it works similarly like the AJAX requests.
When you make a request to the server (GET, POST, PUT, etcetera), you're waiting for a response from this (a collection of data, a message, a succesful/unsuccesful POST/PUT/DELETE, etcetera). Depending of the response, you'll code the expected events (error, success, complete, etcetera).
In this case you're using axios, a new way to do AJAX requests. The equivalent way of the error/success/complete/... events is the then() function. Using this approach you can perform operations that makes new tasks or simply print a response message (in your case) of the server.
From MDN:
The then() method returns a Promise. It takes up to two arguments:
callback functions for the success and failure cases of the Promise.
Let's suppose that we have this snippet of code in AJAX:
$.ajax(
{
url : yourURL,
type : 'POST',
data : yourData,
datatype : 'json',
success : function(data) {
yourSuccessFunction(data);
},
error : function(jqXHR, textStatus, errorThrown) {
yourErrorFunction();
}
});
Using axios, you'll code something like this:
axios.post('/user', {
YourData: yourData
}).then(() => { this.yourSuccessFunction() })
}).catch(() => { this.yourErrorFunction() });

I just found the error. I was making a request to PORT 333 in my axios.post(), but the server was working on port 3333.

Related

Unable to retrieve all the data in chunks using Observable

I am attempting to use a streaming strategy to send data in chunks to the browser. However, when the data is read it does not send them in chunks from the code written to stream the results. It reads and sends the first batch and then gives a message that there are some more items left. Why isn't the rest of the data streamed? I thought was how Observables work, to read the data in chunks in the next callback. Here are how the results are displayed, but with the ... more items, shown below
[
...,
{
productCode: 1829222,
productName: 'Twizzlers'
} ,
... 141 more items
]
Here is the code that tries to stream the data:
const fetch = (url, payload) =>{
try{
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
};
const request = new Request(url, requestOptions);
const data$ = fromFetch(request).pipe(
switchMap(response => {
if (response.ok) {
return response.json();
} else {
return of({ error: true, message: `Error ${ response.status }` });
}
}),
catchError(err => {
console.error(err);
return of({ error: true, message: err.message })
})
);
data$.subscribe({
next: result => console.log(result),
complete: () => console.log('done')
});
}catch(e){
console.error(e)
}
}
I am attempting to use a streaming strategy to send data in chunks to the browser.
Data is sent to the browser in chunks. When the server sends data to the browser, the server is sending it in "chunks" and the browser is storing it in a buffer. You are decoding that buffer to something useable when you run response.json().
For further reading on TCP connections (How GET, POST, etc) work under the hood, I suggest reading the "Bulding blocks of TCP" chapter in the book "High Performance Browser Networking" https://hpbn.co/building-blocks-of-tcp/
Your code works well with standard GET request for some dummy json. I can see the response well and I'm not seeing the error you are seeing
https://stackblitz.com/edit/rxjs-xwyctu?file=index.ts
If you are actually trying to "stream" data to the browser, you can look into Server Sent Events (SSE) https://hpbn.co/server-sent-events-sse/. This is how you can establish a long running GET request to stream data from the server to the client.
You can see an example here: https://github.com/Fallenstedt/server-sent-events-example

Uncaught (in promise); Thrown error in fetch() not being caught

I've already read tons of resources to try to help me on this. This gist did not solve it for me (https://github.com/github/fetch/issues/203#issuecomment-266034180). It also seemed like this (JavaScript Promises - reject vs. throw) would be my answer but it is not. Also this (Error thrown in awaited Promise not caught in catch block) and this (errors not being thrown after promise).
I'm developing a project using a Yii2 PHP server-side solution, and Vue frontend solution. The project has several resources (lessons, media, etc) and REST API endpoints on the server-side that all are used the same. My dev work would benefit from me creating a re-usable API client class (in native JS - not anyting Vue related). I created an 'abstract' class that I 'extend' for each resource and use its functions for the CRUD operations.
I'd like to set up some middleware functions that are going to process the response from the API so that will be handled in the same fashion after every request I make so that I don't have to reproduce that processing code in the Vue apps and components that are using those API client classes.
The code is using the native JS fetch() function. I'm using .then() and .catch() in the functions as needed to process responses and control the flow.
My problem is that I have a function to process the API response, and in it I throw an error if I receive a non-200 response. I've implemented .catch() blocks in several places but I always get an error "Uncaught (in promise)" regardless of putting catch() calls everywhere.
When a user starts watching a video, I make an API call to my server to update a status on a user_media record. So, in the Vue component, I use my UserMedia helper class to create() a resource on the server and implement a then() and catch() on that. When there is an error server-side, I expect the catch() to catch that error and handle it. But, I just get the error "Uncaught (in promise)" as if I'm not trying to catch the error at all.
In the code, I am using updateWatchedStatus() in the vimeo video component, that calls the UserMediaApi.create() which calls YiiApiHelper.request() which calls YiiApiHelper.processRestResponse() where the error is thrown. I've tried implementing catch() blocks all over the place but it's never caught.
CLEARLY, I don't understand something about either fetch(), promises, or catching errors. But I can't figure it out. It seems like the only way around this is to have to write a bunch more code to try to compensate. Any help is appreciated. Even if I'm going about this all wrong and should be doing it someway else entirely.
The full code for that can be seen here:
YiiApiHelper.js https://pastebin.com/HJNWYQXg
UserMediaApi.js https://pastebin.com/9u8jkcSP
Vimeo Video Vue Component https://pastebin.com/4dJ1TtdM
For brevity, here's what's important:
Generic API Helper:
const request = function(resource, options){
return fetch(resource, options)
.then(response => Promise.all([response, response.json()]));
}
const resourceUrl = function(){
return this.autoPluralizeResource ?
this.resourceName+'s' :
this.resourceName;
}
const create = function(postData, options){
const url = new URL(this.baseUrl+'/'+this.resourceUrl());
if(!options){
options = {};
}
options = {
method: 'POST',
body: JSON.stringify(postData),
...options,
}
if(!options.headers){
options.headers = {};
}
options.headers = {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
"Content-Type": "application/json",
...options.headers
}
return this.request(url, options)
.then(this.processRestResponse);
}
const processRestResponse = function([response, body]){
if(!response.ok){
if(response.status == 422){
if(Array.isArray(body)){
let messages = [];
body.forEach(validationError => {
messages.push(validationError.message);
})
throw {
name: response.status,
message: messages.join("\n")
}
}
}
throw {
name: response.status,
message: (body.message) ?
body.message :
response.statusText
}
}
return Promise.all([response, body]);
}
export default {
baseUrl: '',
resourceName: '',
autoPluralizeResource: true,
resourceUrl: resourceUrl,
request: request,
create: create,
processRestResponse: processRestResponse,
handleErrorResponse: handleErrorResponse
};
UserMedia helper:
import YiiApiHelper from './../../yiivue/YiiApiHelper.js';
export default {
...YiiApiHelper,
baseUrl: window.location.origin+'/media/api/v1',
resourceName: 'user-media',
autoPluralizeResource: false
}
VimeoVideo.js:
let updateWatchedStatus = function(watchedStatusId) {
if(!props.userMedia){
// --- User has no record for this media, create one
return UserMediaApi.create({
media_id: props.media.id,
user_id: props.userId,
data: {
[Helper.WATCHED_STATUS_KEY]: watchedStatusId
}
}).then(([response, body]) => {
context.emit('userMediaUpdated', {userMedia: body});
return body;
}).catch(YiiApiHelper.handleErrorResponse);;
}
// --- User has a record, update the watched status in the data
let data = {
...userMedia.value.data,
[Helper.WATCHED_STATUS_KEY]: watchedStatusId
}
return UserMediaApi.update(props.media.id+','+props.userId, {
data: data
}).then(([response, body]) => {
context.emit('userMediaUpdated', {userMedia: body});
return body;
}).catch(YiiApiHelper.handleErrorResponse);;
}
Figured out and fixed this a while ago and figured I should come back in case it helps anyone.
Wrapping the request in a promise, and passing its resolve/reject into promises returned was the solution.
The code below isn't complete but it's enough to illustrate what had to be done to get this working as intended:
const request = function(resource, options){
return new Promise((resolve, reject) => {
return fetch(resource, options)
.then(response => {
if(
options &&
options.method == "DELETE" &&
response.status == 204
){
// --- Yii2 will return a 204 response on successful deletes and
// --- running response.json() on that will result in an error
// --- "SyntaxError: Unexpected end of JSON input" so we will just
// --- avoid that by returning an empty object
return Promise.all([response, JSON.stringify("{}"), resolve, reject])
}
// --- Include resolve/reject for proper error handling by response processing
return Promise.all([response, response.json(), resolve, reject])
}).then(this.processRestResponse)
});
}
const create = function(postData, options){
const url = new URL(this.baseUrl+'/'+this.resourceUrl());
if(!options){
options = {};
}
options = {
method: 'POST',
body: JSON.stringify(postData),
...options,
}
if(!options.headers){
options.headers = {};
}
options.headers = {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
"Content-Type": "application/json",
...options.headers
}
return this.request(url, options);
}
const processRestResponse = function([response, body, resolve, reject]){
// --- If the response is okay pass it all through to the function
// --- that will be handling a response
if(response.ok){
return resolve([response, body]);
}
// --- If there are validation errors prepare them in a string
// --- to throw a user friendly validation error message
if(
response.status == 422 &&
Array.isArray(body)
){
let messages = [];
body.forEach(validationError => {
messages.push(validationError.message);
})
return reject({
name: response.status,
message: messages.join("\n")
})
}
// --- If there is another error just provide the status text
// --- as a message (Yii provides this)
return reject({
name: response.status,
message: (body.message) ?
body.message :
response.statusText
})
}
export default {
baseUrl: '',
resourceUrl: resourceUrl,
request: request,
create: create,
processRestResponse: processRestResponse,
handleErrorResponse: handleErrorResponse
};

Error: Can't set headers after they are sent node.js

hi guys im fairly new to node.js and I was wonder if I am making a call twice that I am unaware of. I am getting a Error: Can't set headers after they are sent.
export const hearingReminder = functions.https.onRequest((request, response) => {
console.log(request.body)
const payload = {
notification: {
title: 'Upcoming Hearing',
body: 'You have a hearing in one hour.',
}
};
const fcm = request.body.fcm
console.log(request.body.fcm)
try {
response.status(200).send('Task Completed');
return admin.messaging().sendToDevice(fcm, payload);
} catch (error) {
return response.status(error.code).send(error.message);
}
Your code is attempting to send a response twice, under the condition that admin.messaging().sendToDevice generates an error. Instead of sending the 200 response before the call, only send it after the call. Sending the response should always be the very last thing performed in your function.
Your code should be more like this:
admin.messaging().sendToDevice(fcm, payload)
.then(() => {
response.status(200).send('Task Completed');
})
.catch(error => {
response.status(error.code).send(error.message);
})
Note that you don't need to return anything for HTTP type functions. You just need to make sure to handle all the promises, and only send the response after all the promises are resolved.

Pending promise state using javascript fetch api with java spark (POST request)

I am trying to use ES6's fetch api to post some login data to a java spark server. The GET requests work perfectly, but when I try to POST something, the Promise on the client side stays 'pending'. I checked, and the server receives the request body, and parses it to an object. Using postman, it also returns true or false, so I think something is wrong with the CORS setup. I'm new to that, so i jut let * through, thinking it should work. I am using VueJS, but i don't think that really matters here, thought I'd add this info, maybe it helps. I will post the code below.
JS:
methods: {
login: function () {
data = '"perfectly valid json string"'
this.postData('http://te.st/login', data)
},
postData: function(url, data){
return fetch(url, {
body: data,
method: 'POST',
})
.then(response => response.json())
.then(function (result){
app.response = result
})
}
}
Java:
private static void enableCORS(final String origin, final String methods, final String headers) {
options("/*", (request, response) -> {
String accessControlRequestHeaders = request.headers("Access-Control-Request-Headers");
if (accessControlRequestHeaders != null) {
response.header("Access-Control-Allow-Headers", accessControlRequestHeaders);
}
String accessControlRequestMethod = request.headers("Access-Control-Request-Method");
if (accessControlRequestMethod != null) {
response.header("Access-Control-Allow-Methods", accessControlRequestMethod);
}
return "OK";
});
before((request, response) -> {
response.header("Access-Control-Allow-Origin", origin);
response.header("Access-Control-Request-Method", methods);
response.header("Access-Control-Allow-Headers", headers);
response.type("application/json");
});
}
(called in the code as enableCORS("*","GET,POST","Origin, Content-Type, Access-Control-Allow-Origin"); )
And the endpoint:
post("/login", (req, res) -> {
boolean ret = dao.checkLogin(gson.fromJson(req.body(), User.class));
return gson.toJson(ret);
});

Done function never called after $.ajax

I'm a bit new to all this (including Javascript callbacks and ES6). I'm using NodeJS + Express + MongoDB.
I'm calling an Ajax function to update an item and the success Ajax call is never done.
Here is my Ajax call (called from React)
editBug : function(bug){
console.log('about to edit bug with these values',bug);
$.ajax({
url:'/api/bugs',
method: 'PUT',
data:bug
})
.done((jqxhr) => {
console.log('succcess while editing the bug');
this.setState({successVisible : true});
})
.fail((jqxhr) => {
console.log('error : ' + jqxhr);
})
},
Here is my API function:
app.put('/api/bugs',function(req,res){
//console.log('req',req);
console.log('query string : ',req.query);
console.log('query params : ',req.params);
console.log('query body: ',req.body);
let id = new ObjectID(req.body._id);
req.body._id = new ObjectID(req.body._id);
db.collection('bugs').replaceOne(
{_id:id},
req.body,
function(err,result){
assert.equal(err,null);
console.log('Successfull replace!');
res.status(200);
}
);
});
The Successfull replace! log is correctly shown on the server side.
The about to edit bug with these values is correctly shown on the front side. But the succcess while editing the bug log is not shown on front end and it seems .done call is never executed.
The problem is that you are not sending any response back to the browser on node side. Try the following snippet and you should be good to go
Also, I'd like to point out that you should handle the errors. While updating the bugs if something goes wrong, the best practice would be to inform the browser with the 500 status code indicating that the intended action failed. I've added this aspect in the snipped below
app.put('/api/bugs', function(req, res) {
//console.log('req',req);
console.log('query string : ', req.query);
console.log('query params : ', req.params);
console.log('query body: ', req.body);
let id = new ObjectID(req.body._id);
req.body._id = new ObjectID(req.body._id);
db.collection('bugs').replaceOne({
_id: id
},
req.body,
function(err, result) {
if (err) {
console.log('Failed replace');
res.status(500).end(); // <- We set the response status code and end the request
} else {
assert.equal(err, null);
console.log('Successfull replace!');
res.status(200).end(); // <- We set the response status code and end the request
}
}
);
});
Don't you need to end your response object on the Node.js side?
Try adding res.end(); or any kind of response to your response object.
Also, you can use chrome's (or any other browser's) network tab to actually see how your AJAX requests end up, to see if they hang or finish.

Categories

Resources