This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 15 days ago.
var cachekey = null;
function redis_post(req, res, next) {
client.get(keyName, (err, redis_data) => {
if (err) {
throw err;
}
else if (!redis_data) {
client.set(keyName, JSON.stringify(key));
cachekey = JSON.parse(redis_data);
} else if (redis_data) {
cachekey = JSON.parse(redis_data);
}
// next()
})
// console.log("check"+cachekey) // undefined
}
const axiosInstance = axios.create({
baseURL: process.env.API_BASE_URL,
headers: {
'Content-type': 'application/json',
'accept': 'application/json',
'Authorization': cachekey // undefined
},
credentials: true
});
the problem was simple async await query, I just put async before redis_post func and await before client.get(key....)
The problem is that you are doing some async code and depnding on it you assign a value. Console.log is called before you got the response fom the async function.
The link in the comment section from jabaa refers to this problem.
We use Interceptors in axios instance maybe it solve your problem
const axiosInstance = axios.create({
baseURL: process.env.API_BASE_URL,
headers: {
'Content-type': 'application/json',
'accept': 'application/json',
'Authorization': cachekey // undefined
},
credentials: true
});
axiosInstance.interceptors.request.use(function (config) {
// Do something before request is sent
config.headers.Authorization = cachekey;
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
Related
I have this function in my context provider I want it to return a promise so that I can access the returned data from axios
const sendRquest =(service,data,method,url)=>{
let base = api.find(item => item.name ===service )
let config = {
method: method,
url: `${base.url}/${url}`,
headers: {
'x-clientId': clientId,
'Content-Type': 'application/json',
},
data: data
};
axios(config)
.then(function (res) {
return res
})
.catch(function (error) {
return error
});
}
And the result I'm looking for is to write such code in every other component whenever I needed
sendRquest('usermanagement',data,'post','foo/bar').then(res=>console.log(res.data)).catch(err=>console.log(err))
You have to return the promise.
Something like
const sendRquest =(service,data,method,url)=>{
let base = api.find(item => item.name ===service )
let config = {
method: method,
url: `${base.url}/${url}`,
headers: {
'x-clientId': clientId,
'Content-Type': 'application/json',
},
data: data
};
return axios(config)
.then(function (res) {
return res
})
.catch(function (error) {
return error
});
}
Be careful tho because you are "swallowing" errors by using .catch like this. The promise returned by sendRquest will never "fail" (reject) but will succeed (resolve) with either data or error payloads.
the complete answer is like this
may help you all
const sendRquest =(service,data,method,url)=>{
let base = api.find(item => item.name ===service )
let config = {
method: method,
url: `${base.url}/${url}`,
headers: {
'x-clientId': clientId,
'Content-Type': 'application/json',
'Authorization': token
},
data: data
};
return axios(config)
}
I want to use axios in api test. To initialize client I need first to set auth token, which I expect to get with axios as well.How to get it form async code?
const a = require('axios');
getToken = () => {
var axios =a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {'Content-Type': 'application/json'},
});
return axios.post('/credentials/login', {
username: '1#test.com',
password: 'Test#1234'
})
.then(function (response) {
return response.data.data.token;
})
.catch(function (error) {
console.log(error);
});
}
const client=a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {
'Content-Type': 'application/json',
'Authorization': getToken()
},
});
module.exports = client;
First, getToken should be async (or just return promise):
async getToken() {
var axios = a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {'Content-Type': 'application/json'},
});
try {
const response = await axios.post('/credentials/login', {
username: '1#test.com',
password: 'Test#1234'
})
return response.data.data.token;
} catch (error) {
console.error(error);
}
}
Then you can mock request:
const client = a.create({
baseURL: 'http://local.v2.app.com/api/auth/v1.0',
headers: {
'Content-Type': 'application/json'
}
})
const request = client.request
const addToken = (token) => (data, headers) => {
headers.Authorization = token
return data
}
client.request = (config = {}) => new Promise(async (resolve, reject) => {
token = await getToken()
if (!config.transformRequest) config.transformRequest = []
config.transformRequest.push(addToken(token))
request(config).then(resolve, reject)
})
module.exports = client
Yes, bit messy, feel free to refactor!
axios has request function which makes requests, others like get, post are aliases. This code patches request to get token first and then continue request.
transformRequest is took from axious readme on github, there is a comment "You may modify the headers object" so it is legal.
P.S good question, don't know why -1
I am new to react and I am trying to do DRF (Django Rest Framework) with React so I am using the fetch() with required attributes to hit the API endpoint and getting some data. Now in my case, I need to hit two API endpoints. But I wanted to reuse the fetch() by putting it in a function which will return an object and I'll save using setState.
My problem is the endpoint URL for the second API call is in the object returned by the first API call.
Example:
fetch(enpoint1, data) -> returns {name: 'something', url: 'endpoint2'}
if I do setState here the object is not updated as setState is Asynchronous and so I cannot use it immediately.
I might have a super silly mistake. Any advice on the "way I am doing it" or "on how to correct it" would be appreciated.
componentDidMount() {
let currentComponent = this;
try {
const data = {username: 'username', password: 'password'};
//gets me the token
fetch(URL(Just example), {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
}).then(function(response) {
return response.json();
}).then(function(data){
return data.token
}).then(function(token){
var project_object = currentComponent.fetchFunction(currentComponent.props.endpoint, token)
console.log(project_object)
//This is UNDEFINED
})
} catch (e) {
console.log(e);
}
}
fetchFunction(endPoint, token) {
let currentComponent = this;
var object = {}
console.log(endPoint)
try {
//fetch for project object
fetch(endPoint, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'authorization': 'Token ' + token
}
}).then(function(response) {
return response.json();
}).then(function(object){
console.log(object)
object = object
})
} catch (e) {
console.log(e);
}
return object
}
I have a function that will return a token
var accessToken;
function TestToken(){
return cy.request({
method: 'POST',
url: "https://site/token",
headers: {
'Content-Type': 'application/json',
},
body: {
client_id: '1234',
client_secret: '1234',
audience: 'https://site/api/v1/',
grant_type: 'credentials'
}
}).then(response => {
return new Cypress.Promise((resolve, reject) => {
const rbody = (response.body);
var tokenPattern = "(?i)\"access_token\":\\s*\"([^\"]*)\"";
const authToken = rbody.access_token;
accessToken = authToken
cy.log(accessToken);
resolve(accessToken);
return accessToken;
})
});
I call that function in another cypress command, as I need to use the token in the authorization header
Cypress.Commands.add("ResetPwd", () => {
const authToken = TestToken();
cy.log(authToken);
cy.request({
method: 'POST',
url: "https://site/api/v2/users/1234",
headers: {
'Content-Type': 'application/json',
'authorization': 'Bearer '+authToken,
},
body: {
password: 'Test4321',
connection: 'DB',
}
});
});
When I execute the command, I don't see the token being used. Instead I see "authorization": "Bearer [object Object]" in the request.
In the TestToken funtion, I added cy.log(authToken);, to verify it was correctly grabbing the token. The cypress test failed saying
CypressError: cy.then() failed because you are mixing up async and
sync code.
but I could still see the correct token in the console.
Here is the cypress test I am trying to run
describe('Token Test', function() {
before(() => {
cy.ResetPwd();
})
it('Go to website', function() {
cy.visit('https://site/login')
});
});
functions is synchronous so in moment of call it the result will be equal Promise, promise is object and when you try to stringify object (by + operator) you get [object Object].
After some time promise will be resolved and value in authToken variable will be correct.
To fix it is suggest use Cypress.Promise like in example id cypress docs: https://docs.cypress.io/api/utilities/promise.html#Basic-Promise
I am assuming the TestToken() function is a promise and resolves asynchronously causing the authToken to be [ object Object ]
You could use async/await and the code should work fine.
Add async before the arrow function and use await const authToken = TestToken()
I solved this, after lots of work using cy.task:
cy.request({
method: 'POST',
url: '/where/you/retrieve/your/token',
body: {username: user, password: password},
failOnStatusCode: true
}).then((response) => {
cy.task('setToken', response.body);
});
then:
cy.task('getToken').then((token) => {
cy.request({
method: 'GET',
url: '/your/protected/endpoint/here',
headers: {
authorization: `Bearer ${token}`
},
failOnStatusCode: true
}).then((response) => {
expect(response.something).to.eq(something.else);
});
});
I am attempting to build a global API request function considering the repetitiveness of it. The problem I have is despite the responseBody object not being null when the function ends, the response seems to be null?
I can only assume this is in part due to the object being returned before it is updated.
Here is the function:
export function restRequest(url, method, content, body) {
fetch(API_BASE_URL + url, {
method: method,
headers: new Headers({
'Content-Type': content,
'Access-Control-Request-Method': method,
// 'Authorization': localStorage.getItem(ACCESS_TOKEN)
}),
body: body
}).then(
function (response) {
response.json().then((data) => {
let json = JSON.parse(JSON.stringify(data));
let responseBody = {
code: response.status,
body: json
};
//at this point the responseBody is not null
return responseBody;
});
}
)
.catch(function (err) {
console.log('Fetch Error :-S', err);
});
However if I make a call:
let response = restRequest('/app/rest/request', 'GET', 'application/json;charset=UTF-8', null);
response is always null.
What is the best way to handle this?
It's asynchronous, so any call of restRequest will not immediately return the responseBody = you need to properly chain the promises, and call .then on the restRequest call. Return the fetch call from the restRequest function, and avoid the promise-callback antipattern by returning response.json() immediately, rather than nesting a .then inside it:
export const restRequest = (url, method, content, body) => (
fetch(API_BASE_URL + url, {
method: method,
headers: new Headers({
'Content-Type': content,
'Access-Control-Request-Method': method,
// 'Authorization': localStorage.getItem(ACCESS_TOKEN)
}),
body
})
.then(response => Promise.all([response.status, response.json()])
.then(([code, body]) => ({ code, body }))
.catch(function(err) {
console.log('Fetch Error :-S', err);
})
);
then do
restRequest('/app/rest/request', 'GET', 'application/json;charset=UTF-8', null)
.then(response => {
// do stuff with response. (if there was an error, response will be undefined)
});