Problem with dynamic request header in axios template - javascript

In my react app, I created an api.js file which creates an api object with axios.create and exports it as default. So, I use that template to make the API requests. The problem is that, one of the headers in created axios api object must be dynamic.
For example, see the locale header below:
At first it may be something like this:
export default api = axios.create({
baseURL: process.env.REACT_APP_API_URL,
headers: {
post: {
"Content-Type": "application/json;charset=utf-8",
"Access-Control-Allow-Origin": "*",
locale: "en",
},
get: {
locale: "en",
},
},
});
But after some time it can be updated to some other locale, like "en" should be changed with "fr" for example. How can I update it, and make sure when it gets updated it changes in every place api is imported.
I can't use ContextApi etc, because I need to use that api in index.js file too, which, because of not being a react component, doesn't support use of hooks.

Sounds like a job for Axios interceptors...
import axios from "axios"
// some kind of storage for your locale
let locale = "en"
// some way of changing it
export const setLocale = (newLocale) => {
locale = newLocale
}
const api = axios.create({
baseURL: process.env.REACT_APP_API_URL,
})
// register a synchronous request interceptor
api.interceptors.request.use(config => ({
...config,
headers: {
...config.headers,
locale // merge the "locale" into the request config headers
}
}), null, { synchronous: true })
export default api
Also, Access-Control-Allow-Origin is a response header that comes from the server. It does not belong in your request and in general will more than likely cause CORS errors.
Also, the default content-type when posting JS objects in Axios is application/json so you typically don't need to set it.

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)

Vue 3 Instagram Feed - CORS

I'm trying to fetch the latest Instagram posts from a feed using Vue 3 and Axios. Instagram has introduced a strict-origin-when-cross-origin policy, so I keep getting blocked. Access tokens and everything is already set up.
Using the guide for Instagram Basic Display API isn't exactly helpful, big surprise there.
main.js
import { createApp } from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import App from './App.vue'
const app = createApp(App)
app.use(BootstrapVue3, VueAxios, axios)
app.provide('axios', app.config.globalProperties.axios)
app.mount('#app')
App.vue
<template>
<Home />
</template>
<script>
import { inject } from 'vue'
import Home from './components/Home.vue'
export default {
name: 'App',
components: { Home },
setup() {
inject('axios')
}
}
</script>
Home.vue
export default {
name: 'Home',
mounted() {
axios.get('https://api.instagram.com/me?fields=id&access_token=myToken')
.then(response => {
console.dir(response)
}).catch(err => {
console.error(err)
})
}
}
</script>
Access to XMLHttpRequest at 'https://api.instagram.com/me?access_token={myToken}'
from origin 'http://localhost:1234' has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource.
CORS Header Proxy or cors-anywhere would be helpful of course, but I want to do this properly... and I don't know how to implement CORS Header Proxy in Vue :)
What you are trying to achieve is not possible as-is, and if it were, it would be considered a browser bug. CORS is meant to restrict resources that the browser will load via XMLHttpRequest (XHR).
You have two choices:
Proxy the request
As #Phil mentioned in their comment, you will need to proxy the request through some backend where CORS is ignored. Thus, the backend can make the request, fetch the results and return the results to your Vue app. The backend logic should be very simple and you can use axios there as well.
Note: This can be unsafe as you will need to forward the access token of the user to the backend.
Write a browser extension that adds in the access-control-allow-origin header
A browser extension has the ability to modify request and response headers. Writing an extension to do this is quite straightforward as well. You need a background script with content similar to this:
browser.webRequest.onHeadersReceived.addListener((e) => {
const { tabId, responseHeaders } = e
// Add logic to ensure that the header modification only happens on _your_ requests
responseHeaders.push({
name: 'access-control-allow-origin',
value: '<your domain>' // You can throw in '*', which is not a valid value but browsers ignore this error
})
return { responseHeaders }
},
{ urls: ['https://api.instagram.com/*'] },
['blocking', 'responseHeaders'])

Using https with Axios request in Nestjs

I currently have a Nestjs server setup and am attempting to perform an Axios request when one of the endpoints is hit with a GET request. Here is the controller.ts code:
#Controller()
export class TestController {
constructor(private readonly testService: TestService) {}
#Get('testData')
testData() {
return this.testService.testData();
}
}
Service.ts:
#Injectable()
export class TestService {
status(): string {
return 'OK'
}
testData(): Promise<any> {
return helper.getTestData();
}
}
Where helper.getTestData() is just a call to a helper file with the following function:
export async function getTestData(): Promise<any> {
const result = await axios({
url: tempURL,
method: 'GET',
timeout: 3000,
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
});
I am able to hit this endpoint tempURL but encounter the following error message: Cannot read property 'Agent' of undefined. I know that the endpoint I am attempting to hit requires a cert, which is why I must include the httpsAgent argument inside the Axios request. If I don't include the httpsAgent argument, I receive the following message Error: unable to verify the first certificate in nodejs.
Is there a way to configure Nestjs to work with https? Or is there another way to handle this authorization issue inside of Nestjs? Using Postman everything works fine so I'm assuming it is a Nestjs issue. Any help is appreciated.
instead of import https from 'https'; you should use the namespace import: import * as https from 'https'; or set the esModuleInterop to true in your tsconfig file (under compilerOptions)

Disabling Cache in ApolloClient exported from apollo-boost

I want to disable caching or restrict the cache to 24 hours. My ApolloClient runs exclusively on the Server side.
My environment:
apollo-boost 0.4.3
graphql 14.1.1
apollo-link-batch-http - 1.2.12
Right now, that's how I configure my ApolloClient.
new ApolloClient({
ssrMode: true,
cache: new InMemoryCache(),
link: WithApollo.BatchLink(),
credentials: 'same-origin',
});
The closest thing I saw in docs is FetchOptions ... But it doesn't specify what options i can actually pass to achieve my need for disabling or restricting the cache.
This is not possible with Apollo Boost. You need to migrate to migrate to using Apollo Client. This will allow you to provide a defaultOptions option to your ApolloClient constructor as shown in the docs:
const defaultOptions = {
watchQuery: {
fetchPolicy: 'no-cache',
},
query: {
fetchPolicy: 'no-cache',
},
}
The fetchPolicy option can actually be set on each individual query call or Query component -- by providing a defaultOptions object, you avoid having to specify no-cache as the fetch policy on each individual Query component you use. That also means if you're bent on keeping Boost, you could just do this on each of your components. However, the above is how to effectively "turn off" caching for the whole client.
Maybe someone want to know how to disable caching of apollo-boost ApolloClient exactly, so let's talk about it.
#Daniel said is truth, we cannot disable the caching when we do new ApolloClient of apollo-boost directly, but we can set fetchPolicy when we send out the request. The code as below:
// create client first
import ApolloClient from "apollo-boost";
const client = new ApolloClient({ uri: GRAPHQL_URL })
// Set the fetchPolicy when we send request
import { gql } from 'apollo-boost';
client.query({
query: gql`
query someInfo($id: ID!) {
info(id: $id) {
id
name
}
}`,
variables:{id: '123'},
fetchPolicy: 'no-cache'
})
The valid value for fetchPolicy you can find from there.

How to call restfull API inside router using express

I want to retrieve data that is at this link: https://api.rajaongkir.com/starter/cost using express.js.
I created the Single Page Application website using react.js for frontend so i need to call this route : /shipping/check/cost in my backend for get the data. but i dont know how to request inside router express.
I have never done a Restful API from someone else's website.
I just copied what was in the documentation, but in the documentation using the node.js not express.js. https://rajaongkir.com/dokumentasi/starter#cost-response
when I run this I get nothing.
My routes
import {Router} from 'express';
import * as ShippingRouter from './controller'
const routes = new Router();
routes.post('/shipping/check/cost',ShippingRouter.checkCost);
export default routes;
Controller
import db from '../../config/conn';
import keys from '../../config/keys';
import jwt from 'jsonwebtoken';
import qs from 'querystring';
import request from 'request';
export const checkCost =(req,res)=>{
var options = {
"method": "POST",
"hostname": "api.rajaongkir.com",
"port": null,
"path": "/starter/cost",
"headers": {
"key": "mykey",
"content-type": "application/x-www-form-urlencoded"
}
};
var reqCost= https.request(options,function(ress){
var chunks = [];
ress.on("data", function (chunk) {
chunks.push(chunk);
});
ress.on("end", function () {
var body = Buffer.concat(chunks);
res.json(body.toString());
});
})
reqCost.write(qs.stringify({
origin: '501',
destination: '114',
weight: 1700,
courier: 'jne'
}));
reqCost.end();
}
The first thing I would check is if the POST HTTP verb you are using is the correct one. You said you wanted to get data from the API, in this case, you should be using GET instead of POST (check the API documentation to know more).
Besides that, please check this repository where I'm using express and how I handle calls.

Categories