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(...)
Related
In my application, I m using React with Redux and axios.
I am trying to refresh my JWT token if needed via axios interceptors.
I am able to intercept the requests, check if my access token is valid, and if it is expired I am able to request a new pair of tokens using my refresh token.
However, after I obtain my new access and refresh token, my app does not continue with the original request, but it stops and tries to render my components with errors (given that I am missing the response from the original request).
I believe there is some error in how I manage my async functions
I configured an axios instance as follows
in axiosHelper.js
import axios from 'axios';
import jwt_decode from "jwt-decode"
import dayjs from 'dayjs'
let authTokens = localStorage.getItem('authTokens') ? JSON.parse(localStorage.getItem('authTokens')) : null
const instance = axios.create({ baseURL: 'http://localhost:8000'});
instance.interceptors.request.use(async (req) => {
if(!authTokens){
authTokens = localStorage.getItem('authTokens') ?
JSON.parse(localStorage.getItem('authTokens')) : null
}
const user = jwt_decode(authTokens.access)
const isExpired = dayjs.unix(user.exp).diff(dayjs()) < 1
if (!isExpired) return req
const {tokens} = await axios.post(`/refresh-url/`, {
refresh: authTokens.refresh
})
console.log('refreshed1')
const data = authTokens
data.refresh = tokens.refresh
data.access = tokens.access
console.log('refreshed2')
localStorage.setItem('authTokens',JSON.stringify(data))
req.headers.Authorization = `Bearer ${tokens.access}`
console.log('refreshed3')
return req
}, error => {
return Promise.reject(error);
});
export default instance;
In my actions I import the instance as in this example
in actions.js
import axios from '../axiosHelper.js '
...(here my imports and actions)...
const action = async () =>{
const config = { headers: {
'Content-type': 'application/json',
Authorization: `Bearer ${token.access}` // token from the redux store
}
const { data } = await axios.get(
`/api/my-url/`,
config
)
...(send data to reducer)...
}
then in my components, I call my actions via dispatch in useEffect.
in Home.js
const Home = (props) => {
const dispatch = useDispatch()
useEffect(() =>{
dispatch(action())
}, [dispatch])
return (<my component/>)
}
I believe the problem has something to do with dispatch. If the access token is valid, then everything works fine. However, if it is expired, my interceptor will send the request to refresh the tokens (which is successful), but as soon as the tokens are returned, my component tries to render and the rest of the code in the axios interceptor is ignored (I checked this with console log ...), and the original request is not sent (I checked that it does not arrive at my backend server).
I would really appreciate some help as I cannot figure out hoe to solve the problem!
I solved the issue by importing the redux store into my axiosHelper.js script, and then using store.dispatch(refreshaction(authTokens.refresh)) inside my axios interceptor.
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 am trying to pull a recipe list from a server that is expecting an auth token from local storage. I created a local axiosWithAuth file because I am going to be hitting multiple endpoints from the API. My auth file looks like this...
import axios from 'axios';
const axiosWithAuth = () => {
const token = localStorage.getItem('token');
return axios.create({
baseURL: 'https://secret-fam-recipes.herokuapp.com/api',
headers: { Authorization: token }
});
};
export default axiosWithAuth
I am then using this axios call with Redux actions so that the data that is returned updates the store for my project. I have tried calling the API two different ways. One with async await which looks like this...
export const listRecipes = () => async (dispatch) => {
dispatch({type: actions.FETCH_RECIPES_REQUEST});
try {
const {recipes} = await axiosWithAuth.get("/recipes")
console.log("recipes", recipes)
dispatch({type: actions.FETCH_RECIPES_SUCCESS, payload: recipes})
} catch(error) {
dispatch({type: actions.FETCH_RECIPES_FAILURE,
payload:
error.resposne && error.response.message
? error.response.message
: error.message
})
}
}
and another without async await which looks like this...
export const listRecipes = () => (dispatch) => {
dispatch({type: actions.FETCH_RECIPES_REQUEST});
axiosWithAuth.get("/recipes")
.then(data => {
return dispatch({type: actions.FETCH_RECIPES_SUCCESS, payload: data})
})
.catch(error => {
dispatch({type: actions.FETCH_RECIPES_FAILURE,
payload:
error.resposne && error.response.message
? error.response.message
: error.message
})
})
}
I also imported my dependencies like so...
import axiosWithAuth from "../utils/axiosWithAuth";
import * as actions from "../constants/recipeConstants";
When I try the async await action I get a 500 error back from the server. When I use the non async await action I get a type error when trying to load the page that says the following..
TypeError: _utils_axiosWithAuth__WEBPACK_IMPORTED_MODULE_0__.default.get is not a function
so obviously there is something wrong with my actions or my axiosWithAuth format. I have tried changing my axiosWithAuth function to use the auth keyword instead of headers but it does not work still. I am not sure if my action is written improperly or if there is something else I should look at. Any help would be greatly appreciated. Thank you!
I was just working with someone and we figured it out. axiosWithAuth is a function so I should have called the function in my action. The correct line is
const {recipes} = await axiosWithAuth().get("/recipes")
More short approach is not to return a function from your custom axios but return the actual axios instance. Since you're just setting a header, you can do this:
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: 'https://secret-fam-recipes.herokuapp.com/api'
});
axiosInstance.interceptors.request.use((config) => {
config.headers['Authorization'] = localStorage.getItem('token');
return config;
});
export default axiosInstance;
And then, just import and use the axios instance:
import axios from './custom-axios';
axios.get('/url-here');
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'm attempting to add an Axios plugin to Nuxt as described here, but it doesn't seem to work.
This is my plugins/axios.js file...
export default function({ $axios }) {
console.log('Im in the axios plugin')
$axios.defaults.baseURL = `https://localhost:5001/api`
$axios.defaults.headers = {
Accept: 'application/json',
'Content-Type': 'application/json'
}
$axios.onRequest((config) => {
console.log('Making request to ' + config.url)
})
}
This is my nuxt.config.js
plugins: ['~/plugins/axios'],
modules: ['#nuxtjs/axios']
And this is where I use Axios in a file called services/BookService.js:
import axios from 'axios'
export default {
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
}
I get the console.log('Im in the axios plugin') from within my plugin, but nothing else. $axios.onRequest doesn't appear to run, and the baseURL doesn't appear to be set correctly when getBooksFiltered is triggered. I get a 404 when it tried to hit the address http://localhost:3000/Home/Filters. As described in my plugin, the address should be https://localhost:5001/api/Home/Filters
I've also tried the following in my nuxt.config.js, but it doesn't work:
axios: {
baseURL: 'https://localhost:5001/api'
}
Any ideas?
Edit
I've modified my services/BookService.js based on the suggestion below to the following...
export default {
getBooks(axios) {
console.log('Im in getBooks')
return axios.get('/Home')
}
}
My action request that makes my api call is the following....
import BookService from '~/services/BookService.js'
export const fetchBooks = (context) => {
console.log('Im in fetchBooks action')
return BookService.getBooks(this.$axios)
.then((response) => {
context.commit('SET_BOOKS', response.data.booksList)
})
.catch((error) => {
console.log(error)
})
}
And my method in my component that calls the actions...
async fetch({ store, error }) {
try {
console.log('Im in index -> fetch')
await store.dispatch('fetchBooks')
} catch (e) {
error({
statusCode: 503,
message: 'Unable to fetch books at this time'
})
}
}
I'm aware that I may be mixing async/await with promises incorrectly but I don't believe it's the cause of this issue.
Console returns the following...
My network tab contains a single request to http://localhost:3000/ which seems incorrect. It should be https://localhost:5001/api/Home based on the plugin and the address specified in the action. It is also never entering $axios.onRequest
The axios-module sets up an Axios instance on the Nuxt app instance. When you import Axios from axios, and use it directly, you're not using the previously setup Axios instance.
To fix the issue, you could either reference the preconfigured Axios instance from window.$nuxt.$axios (only in the browser), or setup your service to take an Axios instance as a parameter:
// services/BookService.js
export default axios => ({
getBooks() {
return axios.get('/Home')
},
getBooksFiltered(payload) {
return axios.post('/Home/Filters', payload)
}
})
// store.js
import BookService from '~/services/BookService.js'
export default {
actions: {
async getBooks({ commit }) {
const books = await new BookService(this.$axios).getBooks()
commit('SET_BOOKS', books)
}
}
}
Another solution from nuxt-community/axios-module #28:
~/plugins/axios-port.js
import { setClient } from '~/services/apiClient'
export default ({ app, store }) => {
setClient(app.$axios)
}
~/services/apiClient.js
let client
export function setClient (newclient) {
client = newclient
}
// Request helpers
const reqMethods = [
'request', 'delete', 'get', 'head', 'options', // url, config
'post', 'put', 'patch' // url, data, config
]
let service = {}
reqMethods.forEach((method) => {
service[method] = function () {
if (!client) throw new Error('apiClient not installed')
return client[method].apply(null, arguments)
}
})
export default service
Use:
import apiClient from '~/services/apiClient'
export default {
async current () {
return apiClient.get('...')
}
}
In my case I exported a customized axios instance as the doc suggested in my axios.js
export default function ({ $axios }, inject) {
const api = $axios.create({
baseURL:'/api'
})
// Inject to context as $api
inject('api', api)
}
Then use this.$api.get or this.$api.post in your getBook service
The above one works for me
As I have just tested, in each request we should use $axios.
Example: this.$axios.get('....'), or in another context this.$nuxt.$axios.get('...');
Because axios extension use with the app context instance, if we import, it will create a new instance which plugin cannot extend.
I have put test code on stackblitz: here
It seems you need to yarn add #nuxtjs/axios or npm install #nuxtjs/axios like the setup instruction here before it can work: https://axios.nuxtjs.org/setup
I haven't experienced with nuxt yet but I don't think by adding some line of code into some js file without actually installing will make the package available into your repo.