Set axios authorization header depends on store changes - vue.js

I am new to Vue (I am a react person) and I am having this problem.
axios.js
import store from '../../store/index';
import axios from 'axios'
const API_URL = process.env.API_URL;
const token = store.getters.auth.token;
export default axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}
})
store/index.js
import auth from './modules/auth'
Vue.use(Vuex);
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
state: {},
getters : {},
mutations: {},
actions:{},
modules: {
auth
},
strict: debug,
})
modules/auth
import { AUTH_SUCCESS, AUTH_GUEST } from '../actions/auth'
import axios from '../../util/axios/axios'
import Vue from "vue";
const state = {
token: localStorage.token || '',
};
const getters = {
token: state => state.token
};
const actions = {
[AUTH_GUEST]: async ({commit}) => {
await axios.post('auth/register',)
.then(response => {
commit(AUTH_SUCCESS, response);
})
.catch(error => {
console.log(error);
});
},
};
const mutations = {
[AUTH_SUCCESS]: (state, resp) => {
state.token = resp.data.token;
},
}
export default {
state,
getters,
actions,
mutations,
}
when trying to get the store from store/index it returns undefined.
probably the axios has been called before the store has been initialized.
but how can I deal with it?
the flow of the app is.
user register->get token->update store with this token->add to the axios header.
so for now on, all calls to the api will have the token provided.

First of all, you should be careful with Vue's reactivity caveats which affect Vuex aswell. In your case, you're adding a new property inside an object in a mutation.
Back to the main issue, your axios.js file is being executed before the Store instance is built, that's why you cannot access to it and you get undefined.
What I'd do is:
axios.js
import axios from 'axios';
const API_URL = process.env.API_URL;
export default (store) => axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${store.getters.auth.token}`
}
});
and then in your main file, where you have the main Vue instantiation I'd just run the function there, exporting the return of that function.

Related

Why do I have TypeError: Cannot read property 'protocol' of undefined using axios with vue?

I'm trying to do a simple get request to my backend server with axios. I'm getting this stacktrace in the console:
TypeError: Cannot read property 'protocol' of undefined
at isURLSameOrigin (isURLSameOrigin.js?3934:57)
at dispatchXhrRequest (xhr.js?b50d:109)
at new Promise (<anonymous>)
at xhrAdapter (xhr.js?b50d:12)
at dispatchRequest (dispatchRequest.js?5270:52)
Here are the code files: https://gist.github.com/DavidMarcu/68f6bd20fa1e21568464f10e2a2baa6a
Code:
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import bookService from '#/service/BookService.js';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
books: []
},
mutations: {
SET_BOOKS(books) {
this.state.books = books
}
},
actions: {
fetchBooks(context) {
bookService.getAllBooks()
.then(response => {
console.log(response)
context.commit('SET_BOOKS', response.data)
})
.catch(error => {
console.log(error)
})
}
},
modules: {
}
})
Home.vue(view component) - template is irrelevant
<script>
import BookElement from "#/components/BookElement.vue";
export default {
components: {
BookElement
},
created() {
this.$store.dispatch("fetchBooks");
},
computed: {
books() {
return this.$store.state.books;
}
}
};
</script>
<style scoped>
</style>
BookService.js
import axios from 'axios';
const apiClient = axios.create({
baseUrl: `http://localhost:9001/books`,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
const bookService = {
getAllBooks() {
return apiClient.get()
}
}
export default bookService
I tried the plugin solution(vue-axios), but I don't understand all that hassle just for making a get request that is not even in a .vue file, but in a .js file which I import in my component. The axios package is indeed installed. What I'm expecting is to see in the Home component in the vue devtools that in the books computed property I have the response body.
Edit: I added the code in the question as well.
You didn't pass url to get:
Change it like this:
return apiClient.get('/')

Cannot access axios.all in vue component

I initialize my axios instance in one file and attempt to use it in another but I cannot seem to get all working:
// axios.js
import axios from 'axios'
const baseURL = "http://api.lvh.me:3000/api";
const token = '';
export default axios.create({
baseURL: baseURL,
headers: { 'authentication': token }
});
I then use this instance in my components:
// component
import axios from "../axios"
export default {
data() {
startDate: null,
endDate: null
},
created() {
const g_sheet = axios.get(`/dashboards/google_sheets.json?&start_date=${ this.startDate }&end_date=${ this.endDate }`)
const leads = axios.get(`/dashboards/potential_clients/simple?&start_date=${ this.startDate }&end_date=${ this.endDate }`)
axios.all([g_sheet, leads]).then(axios.spread((...responses) => {
const g_response = responses[0]
const lead_response = responses[1]
console.log(g_response, lead_response)
})).catch(errors => {
console.log(errors)
})
}
Calls like get have been working, but all has not. I see the error:
vue.runtime.esm.js?2b0e:1888 TypeError: _axios__WEBPACK_IMPORTED_MODULE_8__.default.all is not a function

Using axios instance with vuex

I am building a Vue frontend with Rails backend.
On frontend I am using Axios and I have set these interceptors for authentication:
import axios from 'axios'
const API_URL = 'http://localhost:3000'
const securedAxiosInstance = axios.create({
baseURL: API_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
})
const plainAxiosInstance = axios.create({
baseURL: API_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
})
securedAxiosInstance.interceptors.request.use(config => {
const method = config.method.toUpperCase()
if (method !== 'OPTIONS' && method !== 'GET') {
config.headers = {
...config.headers,
'X-CSRF-TOKEN': localStorage.csrf
}
}
return config
})
securedAxiosInstance.interceptors.response.use(null, error => {
if (error.response && error.response.config && error.response.status === 401) {
// If 401 by expired access cookie, we do a refresh request
return plainAxiosInstance.post('/refresh', {}, { headers: { 'X-CSRF-TOKEN': localStorage.csrf } })
.then(response => {
localStorage.csrf = response.data.csrf
localStorage.signedIn = true
// After another successfull refresh - repeat original request
let retryConfig = error.response.config
retryConfig.headers['X-CSRF-TOKEN'] = localStorage.csrf
return plainAxiosInstance.request(retryConfig)
}).catch(error => {
delete localStorage.csrf
delete localStorage.signedIn
// redirect to signin if refresh fails
location.replace('/')
return Promise.reject(error)
})
} else {
return Promise.reject(error)
}
})
export { securedAxiosInstance, plainAxiosInstance }
On main.js I am making them available this way:
import VueAxios from 'vue-axios'
import { securedAxiosInstance, plainAxiosInstance } from './axios'
Vue.use(VueAxios, {
secured: securedAxiosInstance,
plain: plainAxiosInstance
})
new Vue({
el: '#app',
router,
store,
securedAxiosInstance,
plainAxiosInstance,
render: h => h(App)
})
And in components I can successfully use them like:
this.$http.secured.get('/items')
The problem is that I am unable to use them in store where I get:
Cannot read property 'secured' of undefined"
I tried in store among others:
import { securedAxiosInstance, plainAxiosInstance } from '../axios'
const store = new Vuex.Store({
secured: securedAxiosInstance,
plain: plainAxiosInstance,
.....
What is the correct way to do it?
You can use this._vm inside the store which refers to the Vue instance of the current application.
So in your case:
this._vm.$http.secured.get('/items')
As alternative you can pass the Vue instance as payload to your mutation/action, like:
this.$store.commit('myMutation',this)

Vue Axios local stored token is undefined

In Vue I am building a small Userprofile page. It is build on token-authentication using Axios. When mounting this page the token is undefined.
with login a token is placed in the localStorage.
The Axios Get request is build outside the Vue component
Api.js
import axios from 'axios'
export default () => {
return axios.create({
baseURL: `http://localhost:8081/`
})
}
Get request
import Api from '#/services/Api'
let config = {
headers: {
'Content-Type': 'application/json',
'Authorization': localStorage.getItem('token')
}
}
export default {
index () {
return Api().get('user/profile', config)
}
}
Vue
<script>
export default {
data: () => ({
user: {}
}),
mounted () {
this.user = UserProfileService.index
console.log(UserProfileService.config)
}
}
</script>
There are many advices and I tried them all. With tics, with commas etc. Who sees the big picture?
Use a request interceptor to set the Authorization header:
// Api.js
export default () => {
const instance = axios.create({
baseURL: `http://localhost:8081/`
})
instance.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
}, function (error) {
// Do something with request error
return Promise.reject(error)
})
return instance
}
I added the code from digital drifter, and solved the problem (with help) with changing the mounted function in Vue.
mounted () {
console.log('mounted')
UserProfileService.index()
.then((res) => {
this.user = res.data
})
}

Get $store data in ~/plugins/axios

I'm new with VueJs, I'm finding best way to get $store data in ~/plugins/axios in VueJS in order to use it for APIs. But some errors occur. Hope your helps
~/plugins/axios.js
import axios from 'axios'
import store from '~/store'
var api = axios.create({
baseURL: 'http://localhost:8000/api/v1/',
headers: {'Authorization': 'JWT ' + store.state.token}
})
export default api
Errors:
Issues: Can't tranmit store in to axios.create, so store is not defined
My ~/Store.JS:
const AuthenticationStore = () => {
return new Vuex.Store({
state: {
token: null
},
mutations: {
SET_TOKEN: function (state, token) {
state.token = token
instance.defaults.headers = { Authorization: 'Bearer ' + token }
}
},
actions: {
....
}
})
}
const createStore = () => {
return AuthenticationStore
}
export { AuthenticationStore }
export default createStore
Error:
Cannot read property 'token' of undefined in 'headers': {'Authorization': 'JWT ' + store.state.token}
You could do this in your mutation:
{
SET_TOKEN: function (state, token) {
state.token = token;
Vue.axios.defaults.headers.common['Authorization'] =
(token ? ('JWT ' + token) : '');
}
Every time you set a token in a mutation you update your default token, so you won't have to set it in each request you do. Could do this in an action as well.
That's assuming you have your axios available globally via this.axios. There's a VueAxios library just for that. Make sure you do this in your entry js file (main.js probably):
import axios from 'axios';
import VueAxios from 'vue-axios';
Vue.use(VueAxios, axios);
That way you'll never have to import your store or axios when you need to make a request. Your typical request would look like this:
this.axios.get('URL', {data}, {options});
import axios from 'axios'
import store from '../store'
var api = axios.create({
baseURL: 'http://localhost:8000/api/v1/',
headers: {'Authorization': 'JWT ' + store.state.token}
})
export default api
You need to import your store.
If you are using Nuxt.js the default store will be in classic mode. It will export a function that create a vuex store.
import Vuex from 'vuex'
import mutations from './mutations'
const createStore = () => {
return new Vuex.Store({
state: {
counter: 0
},
mutations
})
}
export default createStore
So you can fix the new error by switching to modules mode https://nuxtjs.org/guide/vuex-store#modules-mode.
Or edit your store/index.js to the following strucutre
import Vuex from 'vuex'
import mutations from './mutations'
cosnt store = new Vuex.Store({
state: {
counter: 0
},
mutations
})
const createStore = () => {
return store
}
export { store }
export default createStore
and change the import statement in ~plugins/axios.js to import { store } from '../store'