I am currently working on a project with nuxt, and I communicate data with axios.
However, if an error occurs in Axios, it requests Axios indefinitely and stops.
If I make a wrong Axios request, I want an error return right away. What did I do wrong?
The error 'cannot read property 'length' of undefined' occurs at first. And there's an infinite number of errors 'cannot read property 'data' of undefined'
This is code written in plugin.
import SecureLS from 'secure-ls'
export default function({ app, $axios, store }) {
if (process.client) {
const ls = new SecureLS({
encodingType: 'aes',
encryptionSecret: 'key'
})
$axios.defaults.timeout = 3000
$axios.onRequest((config) => {
if (ls.get('key')) {
config.headers.common['X-Token'] = `token ${ls.get('key')}`
}
})
$axios.onError((error) => {
if (
error.response.data &&
error.response.data.detail === 'email confirmed required.'
) {
app.router.push({ name: 'check' })
}
})
}
}
Related
I have a locally hosted mongodb database with mongoose, express, axios, and a Vue front end. Right now I'm trying to access a single object from an exported array, but I'm missing the mark and getting "undefined" as the result.
vue.config.js:
module.exports = {
devServer: {
proxy: 'http://localhost:3000',
}
}
here's the front end Vue script meant to use the objects:
import axios from 'axios';
export default {
name: 'Game',
data () {
return {
pages: [],
currentPage: {},
pageTitle: "",
pageText: "",
options: [],
}
},
created () {
this.getPages();
},
methods: {
async getPages() {
try {
let res = await axios.get('/api/pages');
this.pages = res.data;
console.log(this.pages);
this.currentPage = this.pages[0];
console.log(this.currentPage);
return true;
} catch (error) {
console.log(error);
}
},
my "get" endpoint in pages.js:
router.get('/', async (req, res) => {
try {
let pages = await Page.find();
res.send({pages: pages}); //send result of search for pages as list of pages called "pages"
} catch (error) {
console.log(error);
res.sendStatus(500); //500 = server could not fulfill request
}
});
the route in server.js:
const pages = require('./routes/pages');
app.use('/api/pages', pages);
app.listen(3000, () => console.log('Server listening on port 3000!'));
module.exports = app;
and here's the console output, with the "pages" object from vue's data property and the "currentPage" that's supposed to be at pages[0] (printed to console in earlier example):
I can access the api at 'localhost:3000/api/pages' just fine, but how do I break into that array and access the first page object? I want to get an object from the list axios fetches from mongoose, then hold that object in a variable so I can access it's properties. The whole "pages > [[Target]] > pages > [ ]" is part of the problem I'm sure, but I don't know what to tell the code to open it.
Whoops! I realized my mistake. In pages.js I should have sent "res.send(pages);" After a whole couple days too XD
So I am using Vue Draggable and I am to access the event object in VueJs like below:
<draggable group="leads"
:list="newLeads"
#change="seeChange($event, 'New')">
In methods:
async seeChange(event, status) {
console.log(event.added.element.id);
await axios.patch('http://localhost/api/leads/'+event.added.element.id+'/update-status',
{status : status}).then(response => {
this.leads.unshift(response.data.data);
this.getLeads();
}).catch(error => {
console.log(error);
});
},
I can see the id in the console. But when I drag the card (triggering the event) I get a
Cannot read properties of undefined (reading 'element')
error when the card lands. Even the Axios call gets the correct id and the request is sent fine. But the white screen with this error appears nonetheless.
What am I missing?
Change event is called with one argument containing one of the following properties: added, removed, moved. Docs.
event.added contains information of an element added to the array but when you do removed, moved you haven't property added in object event.
You can use it like this:
async seeChange(event, status) {
console.log(event?.added.element.id);
if(event.added) {
await axios.patch('http://localhost/api/leads/'+event.added.element.id+'/update-status', {status : status}).then(response => {
this.leads.unshift(response.data.data);
this.getLeads();
}).catch(error => {
console.log(error);
});
}
}
request will be send only after added event.
If you want to send request on each event you can do it
by using Object.values(event)[0], example:
async seeChange(event, status) {
const id = Object.values(event)[0].element.id;
console.log(id);
await axios.patch('http://localhost/api/leads/'+id+'/update-status', {status : status}).then(response => {
this.leads.unshift(response.data.data);
this.getLeads();
}).catch(error => {
console.log(error);
});
}
in vue 3 you should use draggable library as below:
install:
npm i -S vuedraggable#next
import:
import Draggable from 'vuedraggable';
and this is the link with full examples:
https://github.com/SortableJS/vue.draggable.next
I have a problem that I can't solve with vue.js
I intercept queries that return an error (axios interceptor), and when it passes through this interceptor, the catch of the axios query is still taken into account.
Except that I wait for an error "error.api". which I don't receive, so it generates a console error.
Here is the code:
axios.interceptors.response.use(null, error => {
let path = '/login';
switch (error.response.status) {
case 401: path = '/login'; break;
case 404: path = '/404'; break;
}
store.commit('logout')
router.push(path);
return Promise.reject(error);
});
this error
2.js:376 Uncaught (in promise) TypeError: Cannot read property 'api' of undefined
And finally, the axios query and the error is generated by the last line (err.api[0])
deleteApi(id) {
this.$store.dispatch('deleteApi', id)
.then((res) => {
this.$toast.success(res.data.success)
this.deleteApiModal(id)
})
.catch(err => this.$toast.error(err.api[0]))
},
I finally found a solution but very dirty, which I find repetitive for not much.
It's on each call axios, to put a condition that checks if "err" exists...
I would have preferred to be able to interact on the interceptor to have managed this in only one place.
If someone has a better solution, I'll take it !
deleteApi(id) {
this.$store.dispatch('deleteApi', id)
.then((res) => {
this.$toast.success(res.data.success)
this.deleteApiModal(id)
})
.catch(err => { if(err) this.$toast.error(err.api[0]) })
},
In here, I can both graphQLErrors and networkError:
const errorLink = onError(({ operation, graphQLErrors, networkError, forward }) => {
if (process.env.NODE_ENV !== 'production') {
if (networkError) {
console.log(`[Network Error]:`, networkError.message);
}
if (graphQLErrors) {
graphQLErrors.forEach((error) => {
console.log(`[GraphQL Error]:`, error.message);
});
}
}
});
but when trying to get these errors inside useQuery, at the component level, only networkError is returned while graphQLErrors is an empty array:
let { loading, error, data } = useQuery(GET_ALL_PROJECTS, {
variables: { pageNum: 1, pageSize: 10 },
fetchPolicy: 'network-only',
});
For example, I get an error 403 to onError function from backend, but could not handle this error inside useQuery!
How to fix this issue??
Apollo client passes either graphQLErrors or networkError forward, that's true. I believe it's under an assumption that when the network error happens, the GraphQL error in returned data is more technical and shouldn't be shown to the user. If you really want to access the additional error info sent by your server, you can actually access it in networkError.result.errors.
My code in typescript extracting the error:
const { networkError } = error
const networkGraphQLErrorsPresent =
networkError &&
"result" in networkError &&
networkError.result.errors &&
networkError.result.errors.length > 0
const extractedError =
networkGraphQLErrorsPresent ?
(networkError.result.errors[0] as GraphQLError) :
undefined
I created a new vue project using the command vue create axe using vue-cli-3.0.016beta. Then installed axios using npm install axios --save. In the main.js file I imported axios as shown below.
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false
Vue.use(axios)
new Vue({
render: h => h(App)
}).$mount('#app')
There is not a bit of code change other than this. Still I get an error like the following:
Unhandled promise rejection
TypeError
columnNumber: 7
fileName: "http://localhost:8080/app.js line 1065 > eval"
lineNumber: 57
message: "parsed is undefined"
stack: "isURLSameOrigin#webpack-internal:///./node_modules/axios/lib/helpers/isURLSameOrigin.js:57:7\ndispatchXhrRequest#webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:109:50\nPromise#webpack-internal:///./node_modules/core-js/modules/es6.promise.js:177:7\nxhrAdapter#webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:12:10\ndispatchRequest#webpack-internal:///./node_modules/axios/lib/core/dispatchRequest.js:59:10\nrun#webpack-internal:///./node_modules/core-js/modules/es6.promise.js:75:22\nnotify/<#webpack-internal:///./node_modules/core-js/modules/es6.promise.js:92:30\nflush#webpack-internal:///./node_modules/core-js/modules/_microtask.js:18:9\n"
__proto__: Object { stack: "", … }
I want to axios globally to use interceptors, hence calling it here in main.js. But if I use it in a view-page there is no error!
is this a bug or I'm doing it wrong? Kindly help me to fix this and use axios globally.
Thanks
so the error I see is here
Vue.use(axios)
Vue.use expects a vue installable plugin.
You could have a look at vue-axios
import axios from 'axios'
import VueAxios from 'vue-axios'
Vue.use(VueAxios, axios)
but I would highly discourage it.
It's best to create your own ApiHandler.js file that handles all the remote stuff separately, and you can easily call from anywhere including vue components and vuex.
here is the beginning of my class
<script>
import axios from 'axios';
class ApiHandler{
constructor(apiUrl) {
this.axios = axios;
this.apiUrl = apiUrl || ''; // this line allow passing a custom endpoint for testing
this.config = {
headers: { 'Cache-Control': 'no-cache' }, // can setup to prevent all caching
baseURL: this.apiUrl,
};
}
/**
* #param {Object} payload
* #param {String} payload.username
* #param {String} payload.password
*/
login({ username, password }) {
return new Promise((resolve, reject) => {
this.axios.post('/api/login', { username: username.toLowerCase(), password }, this.config)
.then((response) => {
if (response.code === 200 && response.body && response.body.token) {
resolve(response.body.token);
} else {
reject('Bad Login');
}
})
.catch((err) => {
reject('internal error');
});
});
}
}
</script>
you can then call this from anywhere by...
<script>
import ApiHandler from '../lib/ApiHandler';
const apiRequest = new ApiRequest();
// and then anywhere in the script
let payload = {
username:'someuser',
password:'somepassword',
};
apiRequest.login(payload)
.then(()=>{
// yay - I'm logged in
})
.catch(err => {
// oh oh, display error
})
</script>
this gives you much more flexibility and allows you to separate the remote actions and allows doing first-leg response handling separate of your component, which allows more re-usability.
instead of
Vue.use(axios);
you should
Vue.prototype.$axios = axios;
then you can use it globally
login() {
this.$axios.post('<host>/api/login', data)
.then((res) => { // dosomething })
.catch((err) => { // dosomething });
}
if you want to add globally interceptors with axios, you can
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
// and
Vue.prototype.$axios = axios;