axios is returning wrap function but I want to use axios.CancelToken in it. Any idea on why this is happening?
import axios from "axios";
const instance = axios.create({
baseURL: appUrl,
timeout: 0,
});
instance.interceptors.request.use(
function(config) {
console.log(instance);
}
);
export const $axios = instance;
CancelToken is a static property of axios. It's not an instance property.
If you're using ES6 module imports, an easy way to reference it is by importing it
import axios, { CancelToken } from "axios"
const instance = axios.create({
baseURL: appUrl,
timeout: 0,
});
const source = CancelToken.source()
instance.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel("Operation cancelled by the user.");
This is really just a shorter version of
import axios from "axios"
const CancelToken = axios.CancelToken
Related
I'm using Vue 3.2 <script setup>, If I try to acess Pinia's store inside an API Service It throws the following error;
Uncaught ReferenceError: Cannot access 'store' before initialization at api.js?:9:1 (anonymous) # api.js?9:9
src/services/api.js:
import axios from 'axios';
import store from '../stores/index';
// eslint-disable-next-line no-undef
const api = axios.create({ baseURL: import.meta.env.VITE_APP_API_URL });
if (store) {
const { token } = store;
if (token) {
api.defaults.headers.Authorization = `Bearer ${token}`;
}
}
console.log(api);
export default api;
src/stores/index.ts:
import { defineStore } from 'pinia'
import Project from '../models/Project';
import { grantAuthSshd, revokeAuth, parseJwt } from '../services/auth';
const initialUser = JSON.parse(sessionStorage.getItem('Orcamento:token') || '{}');
const useProject = defineStore('project-store', {
state: () => ({
loading: false as boolean,
}),
actions: {
loadingDataTable(status: ((status: boolean) => void) & boolean) {
this.loadingDataTable = status;
},
}
});
I tried to use Pinia's interceptors but the error persists:
import axios from 'axios';
import useProject from '../stores/index';
const api = axios.create({ baseURL: import.meta.env.VITE_APP_API_URL });
// use interceptors
api.interceptors.request.use(
(config) => {
const { token } = store;
if ({token}) {
api.config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
const store = useProject();
export default api;
The problem is that there is indirect circular dependency between services/api.js and stores/index.ts modules, to the point they cannot be evaluated correctly.
useProject() returns a singleton, one of reasons why a store is wrapped with a function is that this prevents it from being accessed too soon. Pinia stores are supposed to be accessed only after Pinia is initialized, otherwise this would require to evaluate the modules that depend on it in a specific order that isn't easy to achieve.
In this case useProject is supposed to be used in-place, not on module evaluation:
api.interceptors.request.use(
(config) => {
const store = useProject();
const { token } = store;
...
Due to how ES modules work, this allows to resolve circular dependency.
A way to avoid circular dependency is to move this code from services/api.js to another module that stores/index.ts doesn't depend on, e.g. entry point.
I have a token in the store, but I can't access it in my axios plugin. I used to simply import the store when using just Vue, really struggling to understand how to do this in Nuxt.js
I need to access a token value from my store, and use it in the 'Authorization' attribute below.
Here's my current code;
// /plugins/axios.js
import axios from 'axios'
import { state } from '../store'
export default () => {
const api = axios.create({
baseURL: process.env.BASE_URL,
headers: {
Authorization: `Bearer` + xxxx ACCESS STORE STATE HERE xxxx,
},
})
return api
}
// nuxt.config.js
...
{
plugins: ['~/plugins/persistedState.client.js', '~/plugins/axios'],
}
...
// store/index.js
export const state = () => ({
token: null,
user: null,
isUserLoggedIn: false,
})
As state is returned as a function from my store/index.js, I can't get this to work and clearly isn't the solution!
What I've tried
Looking at docs and at old posts on this it looks like I need to pass { store } as an argument but I get the error Cannot destructure property 'store' of 'undefined' as it is undefined.
For example...
export default ({ store }) => {
const api = axios.create({
baseURL: process.env.BASE_URL,
headers: {
Authorization: `Bearer` + store.state.token,
},
})
return api
}
I've also tried setting the Authorization header in the store itself as an alternative, but this doesn't have any effect when posting to my server, no authorization header is supplied.
// store/index.js
...
export const mutations = {
setToken(state, token) {
state.token = token
state.isUserLoggedIn = !!token
this.$axios.setHeader('Authorization', '123')
},
At a bit of loss with this and any help would be very much appreciated.
Plugin functions should be pure and should not re-assign the value of $this by using a => (fat arrow).
You can tap into the current $axios instance and set the header whenever a request is made:
// plugins/axios.js
export default function ({ $axios, store }) {
$axios.onRequest(config => {
const { token } = store.state
if (token) {
config.headers.common.Authorization = `Bearer ${token}`
}
})
}
Mind you that $axios is a package provided by #nuxtjs/axios and this.$axios will differ from this.axios if you've manually registered axios by its self.
I decided to play a little bit with nuxt for the first time from scratch to finish.
and now, I am trying to add plugins.
the plugin I am trying to add is for my api. But when I inject it, it throws the error "inject is not a function". This is my code below. Every other thing works to the best of my knowledge.
import Vue from 'vue'
import axios from 'axios'
import get from 'lodash/get'
import cookies from 'js-cookie'
import { BASE_URL } from '../config/config'
export default (context, inject) => {
const saveToken = (token) => {
cookies.set('AuthToken', token)
}
const removeToken = () => {
cookies.remove('AuthToken')
}
const getToken = () => {
cookies.get('AuthToken')
}
const token = getToken() || ''
const config = {
baseURL: `${BASE_URL}/api/v1`,
params: {},
headers: {
Authorization: `Bearer ${token}`
}
}
const service = axios.create(config)
service.interceptors.response.use(
response => response,
(error) => {
// src of error.
const data = get(error, 'response.data', {})
Vue.$store.commit('notifications/setNotification', data)
}
)
const ApiService = {
...service,
removeToken,
saveToken
}
inject('ApiService', ApiService)
}
Okay, So, I was able to fix it.
Apparently, the error was caused because I added the plugin in the module array instead of the plugins array as suggested in the Nuxt docs.
After putting it in the plugins as opposed to putting it as a module like I did previously, my dev server has started working again.
I am trying to configure my axios base URL. I found the code below from the following StackOverflow question:
How do I create configuration for axios for default request headers in every http call?
However, I am getting 'Unhandled Rejection (TypeError): Cannot read property 'data' of undefined
(anonymous function)' error. This post is 2 years only and uses a class, but in my code, I am using a function.
The axios call works fine when I do it as normal (not change the base URL). But when I add the axiosConfig and change the base URL I get the error.
If anybody could shine some light on this problem I would be grateful.
axiosConfig.js
import axios from "axios";
const baseURL = process.env.REACT_APP_BASE_URL;
const instance = axios.create({
// .. congigure axios baseURL
baseURL: `${baseURL}`
});
export default instance;
The file where the axios call is made
import axiosConfig from "../axios/axiosConfig";
export const getPosts = () => {
const posts= (dispatch) => {
return axiosConfig
.get('/posts')
.then((response) => {
dispatch({
type: GET_POSTS,
payload: response.data,
});
})
.catch((error) => {
dispatch({
type: POSTS_ERROR,
payload: error.response.data.message,
});
});
};
return posts;
};
It works in the production and development for me.
import axios from "axios";
const api = axios.create({
baseURL: process.env.REACT_APP_BASE_URL || "http://localhost:3030",
});
export default api;
to use I do something like
import api from "../services/api";
const response = await api.post("/sessions", { email, password });
in App.js define
import Axios from "axios";
Axios.defaults.baseURL = "http://127.0.0.1:8000/api/";
in any other components use
insted of http://127.0.0.1:8000/api/user use only user
axios.get('user')
.then((res)=> {
console.log(res)
})
Your axiosConfig.js file it's ok.
You are accessing to 'data' property of an object with value = undefined.
I'm assuming it's response.data
So check https://github.com/axios/axios/issues/883, here you can find more information about why your getting this response
My advise is
Debugg and check the value of response
add .catch(error => {...) at the end of .then(...)
I'm interesting in creating an API wrapper and extending from axios using es6 classes. How is this possible? Axios has a method .create() which allows you to generate a new axios object
class Api extends Axios {
constructor(...args){
super(..args)
this.defaults.baseURL = 'https://api.com'
}
cancelOrder (id) {
return this.put(`/cancel/order/${id}`)
}
}
I know I have access to this let instance = axios.create().
Any thoughts?
Attempt 1
import axios from 'axios'
const Axios = axios.create()
class Api extends Axios {
constructor (...args) {
super(...args)
this.defaults.baseURL = 'https://api.com'
}
cancelOrder (id) {
return this.put(`/cancel/order/${id}`)
}
}
let api = new Api()
api.cancelOrder('hi')
.then(console.log)
.catch(console.log)
Attempt 2
import axios from 'axios'
class Axios {
constructor () {
return axios.create()
}
}
class Api extends Axios {
constructor () {
super()
this.defaults.baseURL = 'https://api.com'
}
cancelOrder (id) {
return this.put(`/cancel/order/${id}`)
}
}
let api = new Api()
console.log(api.__proto__)
api.cancelOrder('hi')
.then(console.log)
.catch(console.log)
axios currently does not currently export the Axios object it uses internally.
The .create() method only instantiates a new instance.
// Factory for creating new instances
axios.create = function create(defaultConfig) {
return new Axios(defaultConfig);
};
I created a pr that exports the Axios class.
https://github.com/reggi/axios/commit/7548f2f79d20031cd89ea7c2c83f6b3a9c2b1da4
And a github issue here:
https://github.com/mzabriskie/axios/issues/320
If you look at the source code of axios they do not seem to expose the "class" for Axios, only an instance.
I do not believe that an instance object can be extended in es6.
Your second attempt seems most viable, but if you want to emulate every single axios method, you may have a lot of overhead.
import axios, { Axios } from 'axios';
class Api extends Axios {
constructor () {
super()
this.defaults.baseURL = 'https://api.com'
}
cancelOrder (id) {
return this.put(`/cancel/order/${id}`)
}
}
you can install this package: npm i axios-es6-class
I also wanted to create class which would allow me to create multiple instances having predefined defaults. Here is my solution.
import axios from 'axios'
export class Axios {
constructor() {
return axios.create({
baseURL: 'http://127.0.0.1:8080/',
headers: {
Authorization: 'AUTH TOKEN FROM INSTANCE',
'Content-Type': 'application/json',
},
})
}
}
const db = new Axios()
db.get('/your_url').then().catch()
Well, many of the answers says that there's no class "Axios" exported from the axios package but that's not true atleast for version 0.26.0. Well if you want to create the Axios instance by yourself and customize it as you wish is pretty simple. I created a example using typescript feel free to use it if you want.
import { Axios, AxiosRequestConfig } from "axios";
class AxiosService<D = any> extends Axios {
constructor(config?: AxiosRequestConfig<D>) {
super(config);
this.defaults.baseURL = 'https://api.com'
this.interceptors.request.use(interceptorConfig => {
// Set up your default interceptor behavior here for requests, if you want
}
);
this.interceptors.response.use(
response => {
return response;
},
error => {
// Same thing here, set the behavior for onError responses
}
);
}
cancelOrder (id) {
return this.put(`/cancel/order/${id}`)
}
}
/*
Here you can choose between exporting the class or a instance of it
in my case i just want to export the instance with some default config
and use it, you may ask yourself why that's because i want to centrilize
the configs on one axios instance instead of multiple instances.
*/
const axiosInstance = new AxiosService({
// You may want to set this so axios automatically parse your data to a object.
transformResponse: res => {
return JSON.parse(res);
},
transformRequest: req => {
return JSON.stringify(req);
},
headers: {
"Access-Control-Allow-Origin": "true",
'Content-Type': 'application/json'
},
});
export { axiosInstance };
Disclaimer: think really well before going into this implementation because 99% of axios default configs, functions, helpers, etc will not be attached to this instance so you will have to insert them manually like we did when we instanciated a axiosService. But if all of axios stuff doesnt matter for you and you want to create a instance from scratch or even a basic instance to be used in some especific places feel free to do it. So most of times just import axios from "axios" and use axios.create.