Cancelling promise in nuxt-axios response interceptor - axios

Basically, my question is the exact one here https://github.com/axios/axios/issues/583. It can be done via throw new axios.Cancel('Operation canceled by the user.');.. But how can I do this in nuxt axios module?? I can not see it in the document and I tried $axios.Cancel('Error') but returned $axios.Cancel is not a constructor
Basically, the something like the snippet below is what I am looking for:
axios.interceptors.response.use(function (response) {
throw new axios.Cancel('Operation canceled by the user.');
}, function (error) {
return Promise.reject(error);
});
Emphasis on throw new axios.Cancel

While #nuxtjs/axios does not expose axios.Cancel, you could still import axios directly to get that symbol. Note axios is already a dependency of #nuxtjs/axios, so no extra dependency necessary.
Example (tested with #nuxtjs/axios v5.11.0):
// plugins/axios.js
import { Cancel } from 'axios'
export default function ({ $axios }) {
$axios.onResponse((response) => {
if (response.code !== 200){
throw new Cancel(response.msg)
}
})
}

With nuxt/axios v5.8.0 IsCancel available
v5.8.0 add CancelToken and isCancel to axios instance

Seems like nuxt-axios does not have any exact equivalent but I found a work around.
plugins/axios.js
export default function({ $axios, req, store, redirect, app }, inject) {
// const source = $axios.CancelToken.source()
const timeout = process.env.API_TIMEOUT || 10000
const errorHandling = function(error) {
console.log(`API ${error}`)
return new Promise(() => {})
}
$axios.onResponse((response) => {
// Any condition that could be considered an response based on standard response
if(response.code !== 200){
throw response.msg
}
})

Related

How to get headers from AxiosResponse-Observable?

Using NestJS, Axios returns an Observable<AxiosResponse>.
How can I get the headers of a GET- or HEAD-Request?
Lets say I make a HEAD-request:
import { HttpService } from '#nestjs/axios';
const observable = this.httpService.head(uri);
How can I get the headers from the result?
Update:
I found a nice workaround that just works with a single line of code.
There is another library called https with is more powerful:
import http from "https";
await http.request(uri, { method: 'HEAD' }, (res) => {
console.log(res.headers);
}).on('error', (err) => {
console.error(err);
}).end();
The headers of the response are available in the subscribe callback with the headers property.
this.httpService.head(uri).subscribe(res => {
console.log(res.headers)
});
Playground
According to https://github.com/axios/axios#request-config:
For request headers you should use something like this:
this.httpService.axiosRef.interceptors.request.use(function (config) {
// Do something before request is sent
console.log(config);
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
You should use it onModuleInit (to prevent working a few interceptors in a time)
Also you can make own module like in this answer: https://stackoverflow.com/a/72543771/4546382
For the response headers you can just use response.headers

How to detect cancelled error request in Nuxt Axios plugin globally?

"#nuxtjs/axios": "5.13.6"
I want to get custom error handler to cancelled requests, but isCancel is falsy everytime.
How to handle it correctly?
axios plugin:
$axios.onError(async (error) => {
if ($axios.isCancel(error)) { return Promise.reject(error.response); }
...
});
I tried to add token to onRequest too:
$axios.onRequest(async (config) => {
const source = $axios.CancelToken.source();
config.cancelToken = source.token;
return config;
})

React useQuery - useMutation onError not catching

I'm using a combination of Axios and react-query to make a POST request to a server that may answer with response code 400 and validation errors.
export const axiosDefault = axios.create({
baseURL: API_LINK,
headers: {
'Content-Type': 'application/json'
},
})
const contactMutation = useMutation(
(data) => axiosDefault.post('/contact', data),
{
onSuccess: (response) => {
console.log('Success', response)
},
onError: (error) => {
console.log('Error', error)
}
}
)
However, when calling contactMutation.mutate(someData) the error response from the server does not get processed by the react query library and instead propagates upward. Neither the onSuccess or onError handlers get called, the isError property of the mutation is also not set.
I've spent hours tearing my hair out over this, what am I doing wrong?
hi i also had same issue i solved by wrapping api function to try/catch block and throw the catched Error.
(data) => {
try {
axiosDefault.post('/contact', data),
} catch (error) {
throw error
}
}

Unable to get Moxios stubRequest to work

I'm having issues getting stubRequest to work properly. Here's my code:
it('should stub my request', (done) => {
moxios.stubRequest('/authenticate', {
status: 200
})
//here a call to /authenticate is being made
SessionService.login('foo', 'bar')
moxios.wait(() => {
expect(something).toHaveHappened()
done()
})
})
This works fine:
it('should stub my request', (done) => {
SessionService.login('foo', 'bar')
moxios.wait(async () => {
let request = moxios.requests.mostRecent()
await request.respondWith({
status: 200
})
expect(something).toHaveHappened()
done()
})
})
The second method just get's the last call though, and I'd really like to be able to explicitely stub certain requests.
I'm running Jest with Vue.
I landed here with a similar goal and eventually solved it using a different approach that may be helpful to others:
moxios.requests has a method .get() (source code) that lets you grab a specific request from moxios.requests based on the url. This way, if you have multiple requests, your tests don't require the requests to occur in a specific order to work.
Here's what it looks like:
moxios.wait(() => {
// Grab a specific API request based on the URL
const request = moxios.requests.get('get', 'endpoint/to/stub');
// Stub the response with whatever you would like
request.respondWith(yourStubbedResponseHere)
.then(() => {
// Your assertions go here
done();
});
});
NOTE:
The name of the method .get() is a bit misleading. It can handle different types of HTTP requests. The type is passed as the first parameter like: moxios.requests.get(requestType, url)
it would be nice if you show us the service. Service call must be inside the moxios wait func and outside must be the axios call alone. I have pasted a simplified with stubRequest
describe('Fetch a product action', () => {
let onFulfilled;
let onRejected;
beforeEach(() => {
moxios.install();
store = mockStore({});
onFulfilled = sinon.spy();
onRejected = sinon.spy();
});
afterEach(() => {
moxios.uninstall();
});
it('can fetch the product successfully', done => {
const API_URL = `http://localhost:3000/products/`;
moxios.stubRequest(API_URL, {
status: 200,
response: mockDataSingleProduct
});
axios.get(API_URL, mockDataSingleProduct).then(onFulfilled);
const expectedActions = [
{
type: ACTION.FETCH_PRODUCT,
payload: mockDataSingleProduct
}
];
moxios.wait(function() {
const response = onFulfilled.getCall(0).args[0];
expect(onFulfilled.calledOnce).toBe(true);
expect(response.status).toBe(200);
expect(response.data).toEqual(mockDataSingleProduct);
return store.dispatch(fetchProduct(mockDataSingleProduct.id))
.then(() => {
var actions = store.getActions();
expect(actions.length).toBe(1);
expect(actions[0].type).toBe(ACTION.FETCH_PRODUCT);
expect(actions[0].payload).not.toBe(null || undefined);
expect(actions[0].payload).toEqual(mockDataSingleProduct);
expect(actions).toEqual(expectedActions);
done();
});
});
});
})

Put / Update to my MongoDb with Express, immutable error of cross _id field

So I am sending data of a mission, the startdate and the finish date. However I am not able to put any changes into the database as it believes I get an immutable error with mongoDB... I would like to stick with using the .then method for my js code.
My other methods are working properly, I just can't get this update method right...
app.put('/missions/:id', function (req, res) {
if (req.user) {
model.Mission.findById(req.params.id).then(function(Mission){
console.log("req.body.secret_mission: ",req.body.secret_mission)
Mission['secret_mission'] = req.body.secret_mission;
Mission['start'] = req.body.start;
Mission['complete'] = req.body.complete;
Mission.update().then(function(){
res.set('Access-Control-Allow-Origin', '*');
res.sendStatus(201);
});
});
}else{
res.sendStatus(401);
}
});
Error I receive in the command line
I was able to use all the help provided to come up with a working solution and keep consistent to how I am calling the rest of my code. A big thanks to those that responded!!
Rather than setting the elements in my collection beforehand I am supposed to do it in the update request. Instead of calling 2 methods I used the findOneAndUpdate.
app.put('/missions/:id', function (req, res) {
if (req.user) {
model.Mission.findOneAndUpdate(
{'_id' : req.params.id},
{ $set: {"secret_mission" : req.body.secret_mission,
"start" : req.body.start},
"complete" : req.body.complete
}).then(function(err, missions){
if (err) return res.json({Error: err});
res.json(missions);
});
} else {
res.sendSatus(401);
}
});
I've cleaned up Your code:
removed req.user check to middleware,
included cors module to not to play with CORS headers in every handler
used async/await stuff to give it more synchronous look
mission/:id route handler just have concrete logic without garbage
Check this solution:
const cors = require('cors'); // install: npm i --save cors
const _ = require('lodash'); // install: npm i --save lodash
app.use(cors());
const isUserAuthorized = (req, res, next) => {
if (!req.user) return res.status(401).send();
next();
}
app.put(
'/missions/:id',
isUserAuthorized,
async (req, res) => {
try {
const mission = await model.Mission.findById(req.params.id);
if (!mission) return res.status(404).send();
mission.set(_.pick(req.body, ['secret_mission', 'start', 'complete']);
await mission.save();
res.status(201).send();
}
catch (error) {
console.log(error);
res.status(500).send();
}
});
or if You don't care if record in db exist or not, so You can just push update directly:
app.put(
'/missions/:id',
isUserAuthorized,
async (req, res) => {
try {
const data = _.pick(req.body, ['secret_mission', 'start', 'complete']);
await model.Mission.update({_id: req.params.id}, {$set: data});
res.status(201).send();
}
catch (error) {
console.log(error);
res.status(500).send();
}
});