Make API handler wrapper to reduce repetition - javascript

Right now to call a private endpoint I need to copy this code to each component. Which is messy, unsustainable, and error-prone. How would I wrap this behavior in a function call and put it where I want utilities to go? So I could import { ApiUtil } from .. and ApiUtil.post('/user', user)
import axios from 'axios';
import { useAuth0 } from '../../../react-auth0-spa';
const authContext = useAuth0();
const {
getTokenSilently
} = authContext;
const token = await getTokenSilently();
await axios({
method: 'post',
headers: {
Authorization: `Bearer ${token}`,
},
url: '/user',
data: user,
});

You can create a instance of axios and can be reused in each component
import axios from 'axios';
//token code sample
const instance = axios.create({
baseURL: API_URL,
headers: { Authorization: `Bearer ${token}` }
});
export default instance;

You could create a new file and export an instance of axios
// ApiUtil.js
import axios from 'axios';
import { useAuth0 } from '../../../react-auth0-spa';
const authContext = useAuth0();
const { getTokenSilently } = authContext;
const token = await getTokenSilently();
export default axios.create({
headers: {
Authorization: `Bearer ${token}`,
},
});
Then, you could use it like this:
import ApiUtil from './ApiUtil';
// …
await ApiUtil.post('/user', { user });

Related

Axios Bad Request 400

That is my axios code but is throwing me a error of Request failed with status code 400
import axios from "axios";
import { BASE_URL } from "../../enums/baseUrl";
import { UploadUrls } from "../../enums/imageupload/urls";
import { axiosInstance } from "../config";
export const upload = async (formData: FormData
): Promise<string> => {
const { data } = await axiosInstance.post(
`${BASE_URL.DEVELOPMENT}/${UploadUrls.UPLOAD}`, formData, {
headers: { 'Authorization': 'Bearer ************',
'Content-Type': 'multipart/form-data' },
transformRequest: formData => formData,
});
return data;
};

Can axios request wait until the response is received?

I need to get IP address and port number details from an external source. These details are required to make some other requests. Following is the code I am trying on:
import axios from 'axios'
let ServerURL
axios.get('http://some/path')
.then((response) => {
ServerURL = 'http://' + response.data.server_ip_address + ':' + response.data.server_ip_port
})
.catch((error) => {
console.log(error)
})
const apiClient = axios.create({
baseURL: ServerURL,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
export default {
getConfigInfo () {
return apiClient.get('/config')
}
}
My problem is that by the time the exported function getConfigInfo() is called, still, the ServerURL is undefined.
How can I handle this kind of problem? Any help is highly appreciated.
Yes you can:
import axios from 'axios'
let apiClient;
const getApiClient = async () => {
if (apiClient) {
return apiClient;
}
const {
data: {
server_ip_address: ipAdress,
server_ip_port: ipPort,
}
} = await axios.get('http://some/path');
const serverUrl = `http://${ipAddress}:${ipPort}`;
apiClient = axios.create({
baseURL: ServerURL,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
});
}
export default {
async getConfigInfo() {
const apiClient = await getApiClient();
return apiClient.get('/config')
}
}
Pattern used: singleton

Vuex store and access to Vue.prototype

I'm starting work with Vue. I tried find solution in another posts but without success. So:
I have 2 parts of code:
//main.js
...
const axiosInstance = axios.create({
mode: 'no-cors',
baseURL: process.env.VUE_APP_BASE_URL,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
})
Vue.prototype.$http = axiosInstance
Vue.config.productionTip = false
new Vue({
router,
store,
vuetify,
i18n,
render: h => h(App),
}).$mount('#app')
and
//store.js
export default new Vuex.Store({
...
actions: {
[AUTH_REQUEST]: ({commit, dispatch}, user) => {
return new Promise((resolve, reject) => {
commit(AUTH_REQUEST)
this.$http.post('/auth/login', user)
.then(resp => {...}
})
}
})
when I trigger this.$store.dispatch(AUTH_REQUEST, ...) in component method, I see error in console, like as Uncaught (in promise) TypeError: Cannot read property '$http' of undefined.
I tried by this.$http, by this._vm.$http - the same.
Problem is that attaching the axiosInstance to Vue.prototype makes it available only in subsequent child vue components and not the store.
To access axiosInstance from the store, you will need to import it.
A way could be to refactor the axios instance to its own file and include the file in main.js so it is executed.
Then in the newly created axios file, export axiosInstance so it can be imported by the store file.
HTH
You can reach this instance only in vue js components. You need wrap axios another js file after that you have to import to vuex file. Im using like that it's useful.
Thanks for answers. I made like this
//utils/axiosHelper.js
import Vue from 'vue';
import axios from 'axios';
import router from '#/router';
const axiosInstance = axios.create({
// withCredentials: true,
mode: 'no-cors',
baseURL: 'correctUrl',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
// 'Access-Control-Allow-Origin': '*',
},
})
axiosInstance.interceptors.request.use(
config => {
if (!config.url.includes('auth')) {
if (localStorage.getItem('user-token') !== null) {
config.headers['Authorization'] = localStorage.getItem('user-token')
}
}
return config;
})
axiosInstance.interceptors.response.use(
resp => {
return resp
}, error => {
if (error.response.status === 401) {
router.push('/auth/login');
localStorage.setItem('isAdmin', null);
localStorage.setItem('user-token', null);
} else if (error.response.status >= 400) {
console.log(error.response.data);
alert(error.response.data.error);
}
return Promise.reject(error);
})
Vue.prototype.$http = axiosInstance;
export default Vue.prototype.$http;
and then
//store.js
...
import $http from '#/utils/axiosHelper'
...
export default new Vuex.Store({
...
actions: {
[AUTH_REQUEST]: ({commit, dispatch}, user) => {
return new Promise((resolve, reject) => {
commit(AUTH_REQUEST)
this.$http.post('/auth/login', user)
.then(resp => {...})
})
},
...
})
and then
//main.js
...
import $http from "#/utils/axiosHelper"
...
Vue.use($http)
and still I see Cannot read property '$http' of undefined when I try trigger AUTH_REQUEST
As mentioned above, you should move your Axios instantiation code to its own file.
File: app/src/libs/axios.js
import Vue from 'vue'
// axios
import axios from 'axios'
import updateToken from '#/auth/middleware/updateToken'
const axiosIns = axios.create({
// You can add your headers here
// ================================
// baseURL: 'https://some-domain.com/api/',
// timeout: 1000,
// headers: { 'X-Custom-Header': 'foobar' },
})
// Or you can use an interceptor if adding tokens etc.
// ======================================
axiosIns.interceptors.request.use(async config => {
const token = await updateToken()
config.headers.common.Authorization = `Bearer ${token}`
return config
})
Vue.prototype.$http = axiosIns
export default axiosIns
File: app/src/store/index.js
...
import axiosIns from '#/libs/axios'
axiosIns.get('/some/url').then(data => { console.log(data) })
...

How to mock axios dependency using mocha in TypeScript?

Here is my sample src/main.ts file
import axios from 'axios';
export async function main() {
const URL = 'test url';
const secretKey = 'Test key'
const response = await axios.get(URL, {
headers: { 'Content-Type': 'application/json', 'KEY': secretKey },
});
I want to write my test case in spec/test.ts file using mocha. Can someone show me how to create a mock and stub for axios dependency.
For mock/stub axios in typestript I recommend axios-mock-adapter, for expect functions chai
Here is an example of how to do this
request.ts
import axios from 'axios';
const apiConfig = {
returnRejectedPromiseOnError: true,
timeout: 30000,
headers: {
common: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
},
};
const request = axios.create(apiConfig);
export default request;
main.ts
import request from './request';
export const URL = 'https://httpbin.org/get';
export const secretKey = 'secret_key';
export async function main() {
const response = await request.get(URL, {
headers: {
KEY: secretKey,
},
});
// response logic
return response;
}
main.spec.ts
import MockAdapter from 'axios-mock-adapter';
import { expect } from 'chai';
import request from './request';
import { main, URL, secretKey } from './main';
describe('Request test', () => {
let stub: MockAdapter;
const receivedData = { data: 'data' };
before(() => {
stub = new MockAdapter(request);
stub.onGet(URL, {
headers: {
KEY: secretKey,
},
}).replyOnce(200, receivedData);
// replyOnce if you assume that your code sends a single request
});
it('test', async () => {
const response = await main();
expect(response.status).to.be.equal(200);
expect(response.data).to.be.deep.equal(receivedData);
});
after(() => {
stub.restore();
});
});

Switching from vue-resource to axios

With vue-resource, we could set the root url in main.js like so:
Vue.http.options.root = 'http://localhost:3000/api'
I tried replacing that with:
axios.defaults.baseURL = 'http://localhost:3000/api';
Vue.prototype.$http = axios
However, now my post calls don't work as expected, and Vue.http.post throws an error.
How is this achieved?
With axios, one can create another instance having a custom config
var my_axios = axios.create({
baseURL: 'http://localhost:3000/api',
});
From here one can use my_axios for operations. You could prototype the custom axios instance into Vue:
Vue.prototype.$http = my_axios
import axios from 'axios';
export const HTTP = axios.create({
baseURL: `http://localhost:3000/api/`,
headers: {
Authorization: 'Bearer {token}'
}
})
You could now use HTTP like so
<script>
import {HTTP} from './http-common';
export default {
data: () => ({
posts: [],
errors: []
}),
created() {
HTTP.get(`posts`)
.then(response => {
this.posts = response.data
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>

Categories

Resources