Axios custom instance not working properly with next JS - javascript

So firstly i create custom axios instance with baseurl and export it like this:
import axios from 'axios';
const instance = axios.create({
baseURL: process.env.BACKEND_URL,
});
instance.defaults.headers.common['Authorization'] = 'AUTH TOKEN';
instance.defaults.headers.post['Content-Type'] = 'application/json';
export default instance;
The problem is in my saga or in any component in general (ONLY client side) when importing this custom axios instance. I use next-redux-wrapper, and when i prefetch data (using getStaticProps) for my component everything works fine and the axios.defaults.baseURL property works just fine.
However the problem is on client-side, whenever i import the same axios instance in any component or in saga but i call it from lets say componentDidMount, the same axios.default.baseURL is undefined, so if i want to make get request i have to type in the full backend + queries URL. What could the problem be? EXAMPLE:
export function* fetchTPsSaga() {
try {
console.log(axios.defaults.baseURL);
const url = `/training-programs`;
const res = yield axios.get(url);
const tPs = res.data.data;
yield put(fetchTrainingProgramsSuccess(tPs));
} catch (err) {
yield put(fetchTrainingProgramsFail(err));
}
}
// The first time it renders (on server side), it's the valid baseURL property, however if i call the same saga from client-side (when component is rendered) it's UNDEFINED, so i have to type the full url

process.env only work on server-side. You can use publicRuntimeConfig to access environment variables both on client and server-side.
next.config.js
module.exports = {
publicRuntimeConfig: {
// Will be available on both server and client
backendUrl: process.env.BACKEND_URL,
},
}
axios instance file
import axios from 'axios';
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
const instance = axios.create({
baseURL: publicRuntimeConfig.backendUrl,
});
By the way, if you are using Next.js versions 9.4 and up, the Environment Variables provide another way.

Loading Environment Variables Rules
In order to expose a variable to the browser you have to prefix the variable with
NEXT_PUBLIC_
. For example:
NEXT_PUBLIC_BACKEND_URL='http://localhost:3000'
Then you can access this env variable in Axios as its client-side rendering
import axios from 'axios';
const instance = axios.create({
baseURL: process.env.NEXT_PUBLIC_BACKEND_URL,
});
*Note: You have to use process.env.NEXT_PUBLIC_BACKEND_URL instead of process.env.BACKEND_URL

Related

How do libraries to provide users a bindData function?

I need to copy the behaviour of, for example, the "create" function of Axios:
// fileOne.js
import axios from 'axios';
const instance = axios.create({
baseURL: 'https://some-domain.com/api'
});
Then to use that baseURL in another file, with another function like this:
// fileTwo.js
import axios from 'axios';
axios.get('/user/12345'); // https://some-domain.com/api/user/12345
Getting this as result:
https://some-domain.com/api/user/12345
How does Axios to bind the baseURL data in his library.
I'm looking that library but i don't understand how they do that.
Please see the official documentation Config Defaults section.
You can specify config defaults that will be applied to every request.
Global axios defaults
axios.defaults.baseURL = 'https://some-domain.com/api';
// use it later
axios.get('/user/12345')
Custom instance defaults
const instance = axios.create({
baseURL: 'https://some-domain.com/api'
});
// use it later
instance.get('/user/12345')
Source code explanation of axios v1.2.1
The Axios class has a defaults
property, axios will merge config when dispatch a request. The axios package will call createInstance to create an axios instance of the Axios class with built-in default config.
There is a buildFullPath function to build the full request URL use baseURL and requestedURL(Absolute or relative URL to combine, in your case, it's /user/12345)

Nuxt.Js axios not using baseURL despite it being set correctly

I want to call an API in asyncData()
async asyncData({ $axios, params, store }) {
let itemUUID = params.item;
let item = await $axios.get("/item/" + itemUUID);
return {item};
}
Problem: Axios is still making the request on http://localhost:3000
if I do a console.log($axios.defaults.baseURL) the correct baseURL of my API is printed.
This also works if I use my store action & make the call by using this.$axios
I am using #nuxtjs/axios 5.13.1 with Nuxt 2.15.6 in SSR mode and configured it with the correct baseURL in the nuxt.config.js
Interestingly, if I edit my page content and a hot module reload is triggered, the correct URL is used. Maybe the question should be if Axios is triggered in the right time, on the server?
Edit: I checked the request that was made on HMR and this was triggered in the client.js.
If I call my store inside the created() hook the request gets executed successfully.
My nuxt.config.js:
publicRuntimeConfig: {
axios: {
baseURL: process.env.EXPRESS_SERVER_URL
}
},
privateRuntimeConfig: {
axios: {
baseURL: process.env.EXPRESS_SERVER_URL,
}
},
I'm not sure what is the NODE_TLS_REJECT_UNAUTHORIZED=0 thing doing but your frontend configuration (Nuxt) is working well so far.
Sorry if I cannot help on the Express part.
Maybe try to setup HTTPS locally on Nuxt: How to run NUXT (npm run dev) with HTTPS in localhost?
TLDR; This was not related at all - I forgot to set the auth token for my backend. At the time of axios init it's not present. $axios object doesn't have auth - backend fails.
On page load the nuxt function nuxtServerInit() is used to get the auth token out of the acces_token cookie.
I am using a plugin to initialize Axios - with the token from the store.
But of couse the token is not present at the time axios is initialized as nuxtServerInit is called after plugin init.
In my axios.js plugin I changed:
export default function({ app, error: nuxtError, store }) {
const token = const token = store.state.user.token;
app.$axios.setToken(token, "Bearer");
}
to;
export default function({ app, error: nuxtError, store }) {
const token = app.$cookies.get("access_token");
app.$axios.setToken(token, "Bearer");
}
Now the token is present & used for every request happening server-side.

How to modify the baseURL in axios depending on the current domain

What I'm trying to do is to create an Axios instance in my nuxt app and modify the baseURl to be the same as the domain but with some prefixes and import this instance from an external js file.
for example, I tried to interceptors the request and modify the domain but in the normal Axios package there is no referer to the current domain in '#nuxt/axios' module there is the referer of the current domain but only if I use the module:
axiosInstance.js:
import axios from 'axios';
// Note I don't want to use baseURL to be static.
const api = axios.craete();
api.interceptors.request.use( (config) => {
const prefixedDomain = prefixDomian(config.headers.common.refere)
config['baseURL'] = prefixedDomain
return config;
}, (error)=> {
return Promise.reject(error);
});
export default api
test.js:
import api from 'axiosService';
const settings = async () => {
const setting = await api.get('/settings');
return settings
}
export default settings
but this code doesn't work because the header is empty inside the config.
The only result that I want is to create an Axios instance with the baseURL prefixed and can import it and use it in an external file.

Use Redux state to initialize Axios client

I have a React/Electron application I'm working on in which I want to use data from my Redux store to initialize my Axios client. The use case is, for example, on first load of the app the user enters some information, like their username. This is pushed to the Redux store (and persisted in localStorage for future use), then used in the baseURL of the axios client for subsequent network requests.
The problem is, I can't get axios to work with react-redux and the connect() function. Axios' function exports seem to be hidden by the exported HOC, and any time I call one of its functions I get the following error:
TypeError: _Client2.default.get is not a function
My client looks something like this:
import axios from "axios";
import { connect } from "react-redux";
const Client = ({ init }) => {
return axios.create({
baseURL: `http://${init.ip}/api/${init.username}`
});
};
const mapStateToProps = state => {
return { init: state.init };
};
export default connect(
mapStateToProps,
{}
)(Client);
What am I doing wrong here?
Here in react-redux documentation https://react-redux.js.org/api/connect#connect-returns it says that The return of connect() is a wrapper function that takes your component and returns a wrapper component with the additional props it injects. So it returns react component that wraps react component. Your function returns axios client, it doesn't render anything.
I prefer to use action creators and make api calls there(Therefore I don't pass axios client or whatever). But if I decided to that I would initialize axios client inside reducer and keep in the store. And then pass it to clients as props.
const mapStateToProps = state => {
return { axios: state.axios };
};
On top of #Ozan's answer, In this case what you can do is create a main component, connect it with redux and dispatch an action on mount to initialize axios client.
You should initiate AXIOS client before you load App.js. I recommend you can use redux-axios as redux middleware and use action to call api.
https://github.com/svrcekmichal/redux-axios-middleware

ES6 swap module at runtime

I have api.js which loads either mock.js or server.js and exports it. The result is that based on server environment variables I can swap between backends.
Problem is, I want to do this at Runtime without affecting all the code that is already using the "api" of the mock.js and server.js modules.
I want my app to use either mock.js or server.js depending on connection status. The following code will work during initialization only, not runtime.
import server from './server'
import mock from './mock'
let backend = null
if (process.env.NODE_ENV === 'development' && Math.random() > 0.5 ) {
backend = mock
} else {
backend = server
}
export default backend
You can take advantage of ES6 live binding if you want, e.g.
import server from './server';
import mock from './mock';
export { backend as default };
let backend = server;
setInterval(() => {
backend = Math.random() > 0.5 ? mock : server;
}, 1000);
which will change the default export randomly every second.
In ES6, imported variables are live references to the variable in the module that exports the value, so you can just
import backend from "./api";
and the backend value will change over time.
Specifically in your case, changing
export default backend;
to
export { backend as default};
is critical, as the first doesn't work because it is short for
const uniqueTempVal = backend;
export { uniqueTempVal as default };
which as you can see means reassigning backend later does not affect the exported value.
Put the object you want to export in a property of the exported object:
import server from './server'
import mock from './mock'
let backend = { api: null };
if (process.env.NODE_ENV === 'development' && process.env.NODE_MOCK === true) {
backend.api = mock;
} else {
backend.api = server;
}
export default backend;
Now just use the api property in the importing code.

Categories