axios query string thunk + graphql - redux

I am getting the following error from an axios thunk:
ExceptionsManager.js:82 messed up in createMeeting: Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:59)
at XMLHttpRequest.dispatchEvent (event-target.js:172)
at XMLHttpRequest.setReadyState (XMLHttpRequest.js:572)
at XMLHttpRequest.__didCompleteResponse (XMLHttpRequest.js:394)
at XMLHttpRequest.js:507
at RCTDeviceEventEmitter.emit (EventEmitter.js:189)
at MessageQueue.__callFunction (MessageQueue.js:366)
at MessageQueue.js:106
The problem is with my axios request and not with my graphql schema, because I am able to complete this mutation with (my ngrok url)/graphiql and also localhost/graphql. Is my query too long for axios or something? I've consoled logged all the information that I am inputting and it is all in the correct format, but again, the information is not even making it to my server. Also, the data.data.newMeeting looks weird, but that part is actually correct. Here is my thunk:
export const createMeeting = (chatId, userId, invitation) => {
return async dispatch => {
try {
const dateString = invitation.date.toDateString()
let {data} = await axios({
url: `${url}/graphql`,
method: 'POST',
data: {
query: `mutation{
newMeeting(chatId:${chatId},
userId:${userId},
latitude:${invitation.coords[0]},
longitude:${invitation.coords[1]},
name:"${invitation.name}",
rating:${invitation.rating},
address:"${invitation.address}",
date:"${dateString}"){
location
name
rating
date
senderId
chatId
}
}`
}
})
dispatch(setCurrentMeeting(data.data.newMeeting))
clearInvitation()
} catch (error) {
console.error('messed up in createMeeting: ', error)
}
}
}

Related

Axios post request to Firebase Auth REST API produces 400 error

I have an instance of Axios:
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://identitytoolkit.googleapis.com/v1'
});
export default instance;
Then I import it in my signup.vue file:
<script>
import axios from '../../axios-auth';
...
</script>
In that Vue file I have a signup form, which runs the following method once I hit the Submit button:
onSubmit() {
const formData = {
email: this.email,
age: this.age,
password: this.password,
confirmPassword: this.confirmPassword,
country: this.country,
hobbies: this.hobbyInputs.map(hobby => hobby.value),
terms: this.terms
};
console.log(formData);
axios.post('/accounts:signUp?key=my_key_goes_here', {
email: formData.email,
password: formData.password,
returnSecureToken: true
})
.then(res => {
console.info(res);
})
.catch(error => {
console.error(error);
});
}
I'm getting a 403 error - forbidden 400 error - bad request.
I tried to change headers:
instance.defaults.headers.post["Access-Control-Allow-Origin"] = "localhost";
instance.defaults.headers.common["Content-Type"] = "application/json";
But that didn't help.
I'm working from localhost and I saw that localhost is allowed by default. I tried also to add 127.0.0.1 to the list, but that also didn't help.
What am I missing? How can I make this request work?
If you get a 400 error it is maybe because you get an error from the API itself:
Common error codes
EMAIL_EXISTS: The email address is already in use by another account.
OPERATION_NOT_ALLOWED: Password sign-in is disabled for this project.
TOO_MANY_ATTEMPTS_TRY_LATER: We have blocked all requests from this device due to unusual activity. Try again later.
As a matter of fact, those errors return an HTTP Status Code of 400.
You can see the exact response message (e.g. EMAIL_EXISTS) by doing the following with axios:
axios.post('/accounts:signUp?key=my_key_goes_here', {
email: formData.email,
password: formData.password,
returnSecureToken: true
})
.then(res => {
console.info(res);
})
.catch(error => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
} else if (error.request) {
console.log(error.request);
} else {
console.log("Error", error.message);
}
});
See https://github.com/axios/axios#handling-errors
I agree with you as i have tried many approaches but was not getting the result. Hence i have tried to change the code.
You need to make two changes in your code.
1] You need to comment the instance.defaults.headers.post["Access-Control-Allow-Origin"] = "localhost"; because you are providing the authentication globally. As, firebase provides the feature of authentication and you are connecting the web app with REST API.
2] You need to add { headers: {'Content-Type': 'application/json' } in the axios.post() method to prevent it from CORS Error.
Following this approach i hope you can get the respective output.
Happy Coding!
Directly call
https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[yourkey]
No need to keep it in a separate file
Anyone who comes to the thread in future. I faced this issue and lost in debugging and worked with fetch. It was tiresome and took me a day but i made axios work. Here is the code.
const data = JSON.stringify({
idToken: authContext.token,
password: enteredNewPassword,
returnSecureToken: false,
});
// Send the valid password to the endpoint to change password
axios
.post(
"https://identitytoolkit.googleapis.com/v1/accounts:update?key=[Your Key]",
data,
{
headers: {
"Content-Type": "application/json",
},
}
)
.then((response) => {
console.log(response.data);
})
.catch((err) => {
console.log(err.message);
});
Remember to Stringify the data you want to send. Stringify it outside of the http request and then pass that variable. Don't know why but this helps!
Lastly remember to add the header when sending the request to firebase. Make sure axios.post is on the same line. My formatter gave a line break which was also cause of error.
Hope it helps :)

Sequelize delete request with payload works with Postman but not with Vue Frontend implementation

I am trying to send PUT and DELETE requests with my express backend to a sqlite database. Put request works fine but DELETE request always fails.
I already checked the headers within the network tab, it seems to be the right one for both (application/json)
With postman, I can easily delete entries, but with my frontend the body does not seem to be set correctly.
const countryToPush = {title: countryName}
try{
await CountryService.post(countryToPush)
console.log(countryName + ' added!')
} catch(err)
{
console.log(err)
}
},
removeFromDb: async function(countryName){
const countryToDelete = {title: countryName}
try{
await CountryService.delete(countryToDelete)
console.log(countryName + ' deleted!')
} catch(err)
{
console.log(err)
}
}
This is within my vue file where I get the 'countryName' from an on click function.
try {
const country = await Country.create(req.body)
res.send(country)
} catch (err) {
res.status(500).send({
error: 'an error has occurred trying to create the country'
})
}
},
async delete (req, res) {
try {
const toDelete = await Country.findOne({
where: {
title: req.body.title
}
})
await toDelete.destroy()
res.send(req.body.title + ' was deleted')
} catch (err) {
res.status(500).send({
error: 'an error has occurred trying to delete the country'
})
}
}
Whereas this is the example from my sqlite calls
Unfortunately, the DELETE request from my vue frontend always fails and gives me the defined error 500 an error has occurred trying to delete the country.
Any ideas what else I could try to get it working?
Model.delete() is not a Sequelize function, you want to use Model.destroy().
await CountryService.destroy(countryToDelete)
Right now you are swallowing the actual error - add console.log(err) to your catch to see that it is probably saying CountryService.destroy is undefined.

Where is the best place to modify backend response with redux-saga?

I have a function that prepares the errors from backend to be easy for displaying in the components - it's named prepareErrorMessages. It accepts the response from the backend and some default error message.
So - in the saga I have this:
function* updateSomethingFlow(action) {
try {
const response = yield call(updateSomething, action.payload);
if (response) {
yield put({
type: UPDATE_SUCCESS
});
}
} catch (err) {
yield put({
type: UPDATE_FAILURE,
payload: prepareErrorMessages(err, 'Failed to update. Please, try again.')
});
}
}
So - am I wrong to modify the errors from the backend here?
Or is it better to do this in the reducer?
case UPDATE_FAILURE:
nextState = {
...state,
loading: false,
errors: prepareErrorMessages(payload, 'Failed to update. Please, try again.'),
};
break;
And also - why is it better to update there?
Personally, I think its right to do it in the reducer.
That is where you handle the responses. Action creators should only set the payload which could be some static data or a promise.
Dont see why you cannot transform/modify the received data there.
Personally, I would prefer to have it in the saga because I think it is the right place of handling this kind of logic.
I prefer my reducers to only be responsible for changing state, not for data transformation.
But it is my personal opinion.
We are using a Transformer for transforming the response getting from the Api. Transformer is the function which takes the input and provide the desired output. Designing the transformer makes the code clean and easy to test.
For example :-
function* updateSomethingFlow(action) {
try {
const response = yield call(updateSomething, action.payload);
// after getting the response from the api pass through the transformer.
const transformedResponse =apiTransformer(action.payload);
if (response) {
yield put({
type: UPDATE_SUCCESS,
data: trasnformedResponse
});
}
} catch (error) {
yield put({
type: UPDATE_FAILURE,
error: error)
});
}
}
const apiTransformer = function(apiResponse) {
// implement the logic. This function returns the transformed Response
}
Using this you can move reducer free from the error. Makes the code testable and making mocks easy.
For global backend Errors make a global errorHandler using Redux Middleware, like this
const errorTracking = store => next => action => {
if (/_FAILURE$/.test(action.type)) {
const errorCode = parseInt(
_.get(action, ['error', 'response', 'status'])
)
// this was for my use case
if (errorCode === 403) {
// launch an Global error handler action
return next(ErrorHandlerAction())
} else return next(action)
}
return next(action)
}
While for not genric error You can implement HOC wrap it around the component for visualisation. Thus you can have global implementation for the errors.

angular2 wait for error http response

I have a problem with angular2 http response.
I want to catch the error in my component.
How does my app work.
In my Component, I Call a function in a personal service :
var response = this.apiUser.login(username, password);
alert(response);
In my Service, I try to auth :
this.http.post(this.httpApiAdress + '/' + this.httpUserAutenticate, body, { headers: contentHeaders })
.subscribe(
response => {
localStorage.setItem('id_token', response.json().token);
this.router.navigate(['home']);
},
error => {
return error.json();
},
() => { }
);
When the auth is ok, all work fine. But when the Auth fail, i can't catch the response in my Component.
(Its undefinied because the alert is executed before the http call...)
Can u help me please !!! (It was working when all the code was only in my Component, but I wanted to slip my code...)
Ty.
Return the observable by using map() instead of subscribe()
return this.http.post(this.httpApiAdress + '/' + this.httpUserAutenticate, body, { headers: contentHeaders })
.map(
response => {
localStorage.setItem('id_token', response.json().token);
this.router.navigate(['home']);
},
);
and then use subscribe where you want to execute code when the response or error arrives
var response = this.apiUser.login(username, password)
.subscribe(
response => alert(response),
error => alert(error),
);

Chaining RxJS Observables with interval

my first question to the community out here!
i'm working on an app which does communicates to the API in the following way
step1: create request options, add request payload --> Post request to API
API responds with a request ID
Step2: update request options, send request ID as payload --> post request to API
final response: response.json
Now the final response can take a bit of time, depending on the data requested.
this can take from anywhere between 4 to 20 seconds on an average.
How do i chain these requests using observables, i've tried using switchmap and failed (as below) but not sure how do i add a interval?
Is polling every 4 second and unsubscribing on response a viable solution? how's this done in the above context?
Edit1:
End goal: i'm new to angular and learning observables, and i'm looking to understand what is the best way forward.. does chaining observable help in this context ? i.e after the initial response have some sort of interval and use flatMap
OR use polling with interval to check if report is ready.
Here's what i have so far
export class reportDataService {
constructor(private _http: Http) { }
headers: Headers;
requestoptions: RequestOptions;
payload: any;
currentMethod: string;
theCommonBits() {
//create the post request options
// headers, username, endpoint
this.requestoptions = new RequestOptions({
method: RequestMethod.Post,
url: url,
headers: newheaders,
body: JSON.stringify(this.payload)
})
return this.requestoptions;
}
// report data service
reportService(payload: any, method: string): Observable<any> {
this.payload = payload;
this.currentMethod = method;
this.theCommonBits();
// fetch data
return this._http.request(new Request(this.requestoptions))
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || {};
}
private handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
in my component
fetchData() {
this._reportService.reportService(this.payload, this.Method)
.switchMap(reportid => {
return this._reportService.reportService(reportid, this.newMethod)
}).subscribe(
data => {
this.finalData = data;
console.info('observable', this.finalData)
},
error => {
//console.error("Error fetcing data!");
return Observable.throw(error);
}
);
}
What about using Promise in your service instead of Observable, and the .then() method in the component. You can link as much .then() as you want to link actions between them.

Resources