React-Native dynamically change fetch url - javascript

I have a Mobile App that either uses a cloud server or a local server to serve information.
In my App.js I have:
helperUtil.apiURL().then((url) => {
global.API_URL = url;
})
The function does something like:
export async function apiURL() {
try {
var local = await AsyncStorage.getItem('local')
local = (local === 'true')
if(typeof local == 'undefined') return "https://api.website.com";
else if(!local) return "http://192.168.0.6:8080";
else return "https://api.website.com";
}
catch(err) {
return "https://api.website.com";
}
}
Then my fetch command would be:
fetch(global.API_URL+'/page', {
method: 'GET',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer '+this.state.authtoken },
})
I'm running into problems here where the API_URL ends up undefined so I feel like there might be a better solution to this.
Open to any and all suggestions. Thank you.

Insted of seetting url in global obj always use method which return a Promise, and it will return your global object if exist and if not get data from apiURL function. With async/await syntax fetch will be executed only when getAPI promise will be resolved and there will be no situation that url is empty.
const getAPI = () => {
return new Promise((resolve, reject) =>{
if(global.API_URL) {
resolve(global.API_URL)
} else {
helperUtil.apiURL().then((url) => {
global.API_URL = url;
resolve(url)
})
}
});
const fetchFunc = async () => {
const url = await getAPI()
fetch(url+'/page', {
method: 'GET',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer
'+this.state.authtoken },
})
}

Related

React Native wait for one API call to finish before executing any others

I'm writing an app in react native that needs to make a call to an API to receive a bearer token that it will use for all other API calls but I'm having difficulty making sure that the call to get the token finishes before any other calls are made.
I have a basic APIHelper class which is going to contain wrapper functions for all the API calls the app needs to make, here's a reduced version of it:
export class APIHelper {
constructor(baseURL) {
this.baseURL = baseURL;
this.token = null;
}
setToken(token) {
this.token = token;
}
getToken(username, password) {
var url = this.baseURL + `/token/${username}/${password}`;
fetch(url, {
method: "GET",
header: {
"Content-Type": "application/json"
}).then(response => response.json())
.then((data) => {
this.setToken(data["access_token"]);
});
}
getRequest(endpoint) {
fetch(this.baseUrl + endpoint, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + this.token
}
}).then(response => response.json())
.then((data) => {
console.log(data);
});
}
}
Here is a reduced version of my App.js:
var apiHelper = new APIHelper("URLGoesHere");
class StarterApp extends React.Component {
render() {
apiHelper.getToken("UsernameGoesHere", "PasswordGoesHere");
var users = apiHelper.getRequest("/users");
console.log(users);
return (
//App Content Here
);
}
}
I've tried a number of different things: Making the API calls asynchronous, instituting a check in the getRequest function to wait until token isn't null but that just seems to hang, moving the calls into the constructor for the App. Is what I'm looking to do possible? Or will I need to make a call to get the token every time I want to make an API call?
Also: I understand that this is not a particularly secure API, it's a temporary system for a school project, please do not provide feedback on that.
If you return the fetch calls which are promises from getRequest, getToken you can ensure that the call to getToken completes before getRequest using await. Also run the code in componentDidMount as suggested by Chris G e.g.
export class APIHelper {
constructor(baseURL) {
this.baseURL = baseURL;
this.token = null;
}
setToken(token) {
this.token = token;
}
getToken(username, password) {
var url = this.baseURL + `/token/${username}/${password}`;
return fetch(url, {
method: "GET",
header: {
"Content-Type": "application/json"
}).then(response => response.json())
.then((data) => {
this.setToken(data["access_token"]);
});
}
getRequest(endpoint) {
return fetch(this.baseUrl + endpoint, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + this.token
}
}).then(response => response.json())
.then((data) => {
console.log(data);
});
}
}
var apiHelper = new APIHelper("URLGoesHere");
class StarterApp extends React.Component {
async componentDidMount() {
try {
await apiHelper.getToken("UsernameGoesHere", "PasswordGoesHere");
await var users = apiHelper.getRequest("/users");
console.log(users);
} catch (e) {
console.log(e)
}
}
render() {
return (
//App Content Here
);
}
}

How to wait until request.get finish then conduct the next block in node.js

I am new to NodeJS and I am working on a request.get problem. My goal is simply have a function that request the web, and when request finished, the function returns the result, otherwise it returns an error message.
Here's the function that I used for request:
var artistNameIdMap = {};
var getPopularArtists = async () => {
//https://nodejs.org/api/http.html#http_http_request_options_callback
var options = {
url: CONSTANTS.API_ENDPOINTS.playlist_endpoint + subpath,
headers: { 'Authorization': 'Bearer ' + access_token,
'Accept': 'application/json',
'Content-Type': 'application/json'},
json: true
}
request.get(options, function(error, response, body) {
if (response.statusCode === 200){
console.log("inside");
artistNameIdMap = getArtistNameIdMap(body, artistNameIdMap);
} else {
res.send("get popular error");
return {};
}
})
console.log("outside");
return artistNameIdMap;
module.exports = {
GetPopularArtists: getPopularArtists
}
And this function is included in a getPopular.js file. I would like to call the function in another file playlist.js.
In playlist.js, I wrote
const getPopular = require('./controllers/getPopular');
router.get("/BPM/:BPM", (req, res) =>{
const artistNameIdMap = getPopular.GetPopularArtists();
console.log(artistNameIdMap);
let BPM = req.params.BPM;
res.send(BPM);
})
However the result I got is
outside
Promise { {} }
inside
It seems like the return was before the request gives back the information. I wonder what should I write to make sure that I can obtain the correct artistNameIdMap at playlist.js.
Though you've already accepted an answer, there are a couple of additional things I can add. First, the request() library has been deprecated and it is not recommended for new code. Second, there is a list of recommended alternatives here. Third, all these alternatives support promises natively as that is the preferred way to program asynchronous code in modern nodejs programming.
My favorite alternative is got() because I find it's interface simple and clean to use and it has the features I need. Here's how much simpler your code would be using got():
const got = require('got');
let artistNameIdMap = {};
async function getPopularArtists() {
const options = {
headers: { 'Authorization': 'Bearer ' + access_token,
'Accept': 'application/json',
'Content-Type': 'application/json'},
};
const url = CONSTANTS.API_ENDPOINTS.playlist_endpoint + subpath;
let results = await got(url, options).json();
// update local cache object
artistNameIdMap = getArtistNameIdMap(results, artistNameIdMap);
return artistNameIdMap;
}
module.exports = {
GetPopularArtists: getPopularArtists
}
Note: The caller should supply error handling based on the returned promise.
GetPopularArtists().then(results => {
console.log(results);
}).catch(err => {
console.log(err);
});
Since you want to use Promises, use it like this
const getPopularArtists = () => new Promise((resolve, reject) {
const options = {
url: CONSTANTS.API_ENDPOINTS.playlist_endpoint + subpath,
headers: {
'Authorization': 'Bearer ' + access_token,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
json: true
}
request.get(options, (error, response, body) => {
if (error) {
reject(error);
} else if (response.statusCode === 200) {
console.log("inside");
resolve(getArtistNameIdMap(body, artistNameIdMap));
} else {
reject("get popular error");
}
});
});
module.exports = {
GetPopularArtists: getPopularArtists
}
And use it like
const getPopular = require('./controllers/getPopular');
router.get("/BPM/:BPM", async (req, res) =>{
try {
const artistNameIdMap = await getPopular.GetPopularArtists();
console.log(artistNameIdMap);
let BPM = req.params.BPM;
res.send(BPM);
} catch(err) {
res.send(err);
}
})
Alternatively, without promises, you'll need to use a callback
Using callbacks:
const getPopularArtists = (callback) => {
const options = {
url: CONSTANTS.API_ENDPOINTS.playlist_endpoint + subpath,
headers: { 'Authorization': 'Bearer ' + access_token,
'Accept': 'application/json',
'Content-Type': 'application/json'},
json: true
}
request.get(options, function(error, response, body) {
if (error) {
callback(error);
} else if (response.statusCode === 200){
console.log("inside");
callback(null, getArtistNameIdMap(body, artistNameIdMap));
} else {
callback("get popular error");
}
})
};
module.exports = {
GetPopularArtists: getPopularArtists
}
And use it like:
const getPopular = require('./controllers/getPopular');
router.get("/BPM/:BPM", (req, res) =>{
getPopular.GetPopularArtists((err, artistNameIdMap) => {
if (err) {
// handle error here
} else {
console.log(artistNameIdMap);
let BPM = req.params.BPM;
res.send(BPM);
}
});
});

Post action API with object parameter within the URL

I've got an API where some of the parameters need to be given within the URL.
Example of how my api url looks like: https://www.server.com/api/actions/execute?auth_type=apikey&data={"Name": "name","Email" : "email"}
What my code looks like right now
register = async () => {
let data = {"Name":this.state.name, "Email":this.state.email}
data = JSON.stringify(data)
let URL = 'https://www.server.com/api/actions/execute?auth_type=apikey&data=';
fetch(URL, {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json'
}),
body: data
})
.then((response) => response.text())
.then((responseText) => {
alert(responseText);
})
.catch((error) => {
console.error(error);
});
}
The response I get on my device:
{"code":"succes","details":{"userMessage":["java.lang.Object#2e56000c"],"output_type":void","id:"20620000000018001"},"message":"function executed succesfully"}
This is alle working fine when I test it in postman but I can't get it to work within React-Native. I've tried stuff like 'Content-Type':'application/x-www-form-urlencoded' already.
First install the package axios from the url https://www.npmjs.com/package/react-native-axios
Then create two service for handling get and post request so that you can reuse them
GetService.js
import axios from 'axios';
let constant = {
baseurl:'https://www.sampleurl.com/'
};
let config = {
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json'
}
};
export const GetService = (data,Path,jwtKey) => {
if(jwtKey != ''){
axios.defaults.headers.common['Authorization'] = 'Bearer '+jwtKey;
}
try{
return axios.get(
constant.baseUrl+'api/'+Path,
data,
config
);
}catch(error){
console.warn(error);
}
}
PostService.js
import axios from 'axios';
let constant = {
baseurl:'https://www.sampleurl.com/'
};
let config = {
headers: {
'Content-Type': 'multipart/form-data',
'Accept': 'application/json'
}
};
export const PostService = (data,Path,jwtKey) => {
if(jwtKey != ''){
axios.defaults.headers.common['Authorization'] = 'Bearer '+jwtKey;
}
try{
return axios.post(
constant.baseUrl+'api/'+Path,
data,
config
);
}catch(error){
console.warn(error);
}
}
Sample code for using get and post services is given below
import { PostService } from './PostService';
import { GetService } from './GetService';
let uploadData = new FormData();
uploadData.append('key1', this.state.value1);
uploadData.append('key2', this.state.value2);
//uploadData.append('uploads', { type: data.mime, uri: data.path, name: "samples" });
let jwtKey = ''; // Authentication key can be added here
PostService(uploadData, 'postUser.php', jwtKey).then((resp) => {
this.setState({ uploading: false });
// resp.data will contain json data from server
}).catch(err => {
// handle error here
});
GetService({}, 'getUser.php?uid='+uid, jwtKey).then((resp) => {
// resp.data will contain json data from server
}).catch(err => {
// handle error here
});
If you need to pass parameters via URL you should use GET, if you use POST then the parameters should be passed in the body

fetch() injecting a query variable/header value to every call

Right now, in my React-Native app I have the following:
fetch('http://localhost/SOMETHING', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer '+this.state.authtoken
}
})
Goal: Have my API know what UID is making the call. I know this should be in authtoken but different users can have the same authtoken.
My initial thought is to add a ?uid=${UID} to the end of every url. However, I have GET, POST, PATCHs, with their own set of queries
Another thought would be add a header value with the UID data.
Regardless of what I choose, it would be awesome to be able to add this value to every FETCH without having to do much else work.
Is this something that is possible? Open to suggestions on what you would do.
If You can then best would be to switch to Axios (https://github.com/axios/axios) - it's much easier to do that there.
But if You need to use fetch then https://github.com/werk85/fetch-intercept is your solution.
Example code
fetchIntercept.register({
request: (url, config) => {
config.headers = {
"X-Custom-Header": true,
...config.headers
};
return [url, config];
}
});
Not sure if you're willing to step away from fetch, but we use apisauce.
import { create } from 'apisauce';
const api = create({
baseURL: 'http://localhost',
headers: { 'Accept': 'application/json' },
});
api.addRequestTransform(request => {
if (accessToken) {
request.headers['Authorization'] = `bearer ${accessToken}`;
}
});
api.get('/SOMETHING');
edit
If you want to keep it close to fetch, you could make a helper function.
let authToken = null;
export const setAuthToken = token => {
authToken = token;
};
export const fetch = (url, options) => {
if (!options) {
options = {};
}
if (!options.headers) {
options.headers = {};
}
if (authToken) {
options.headers['Authorization'] = `Bearer ${authToken}`;
}
return fetch(url, options);
};
You will probably only use the setAuthToken function once.
import { setAuthToken } from '../api';
// e.g. after login
setAuthToken('token');
Then where you would normally use fetch:
import { fetch } from '../api';
fetch('http://localhost/SOMETHING');
I would not consider creating a onetime helper function and an extra import statement for each fetch a lot of "extra work".
You can build a wrapper function for fetching with uid
function fetchWithUid(baseUrl, uid, authtoken, options) {
const { method, headers, body, ...rest } = options;
const fetchOptions = {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + authtoken,
...headers,
},
method,
...rest,
};
if (body) {
fetchOptions.body = JSON.stringify(body);
}
return fetch(`${baseUrl}?uid=${uid}`, fetchOptions);
}
Use the fetchWithUid function like this, the fetchOptions just mimic the original fetch function's option.
const fetchOptions = {
method: 'POST',
body: {
hello: 'world',
},
};
fetchWithUid('http://localhost/SOMETHING', 123, 'abcd', fetchOptions);

coverting javascript to python

I have a yale smart alarm and come across the the below javascript that allows you to access the alarm to get the status and set it. I'm wanting to use this in my home assistant set to which uses python.
const fetch = require('node-fetch');
const setCookie = require('set-cookie-parser');
const urls = {
login: 'https://www.yalehomesystem.co.uk/homeportal/api/login/check_login',
getStatus: 'https://www.yalehomesystem.co.uk/homeportal/api/panel/get_panel_mode',
setStatus: 'https://www.yalehomesystem.co.uk/homeportal/api/panel/set_panel_mode?area=1&mode=',
};
function getSessionCookie(username, password) {
let sessionCookie = null;
return fetch(urls.login, {
method: 'POST',
body: `id=${encodeURIComponent(username)}&password=${password}&rememberme=on&notify_id=&reg_id=Name`,
headers: {
'Accept': 'application/json, application/xml, text/plain, text/html, *.*',
'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
},
})
.then((res) => {
sessionCookie = res.headers._headers['set-cookie'];
return res.json();
}).then(json => {
if (json.result === '0') {
return Promise.reject('Incorrect account details');
}
else {
return sessionCookie[0];
}
})
}
function getStatus(sessionCookie) {
return fetch(urls.getStatus, {
method: 'POST',
headers: {
'Cookie': sessionCookie,
},
}).then(res => res.text()).then(textResponse => {
// When initially writing this code I found if cookie payload
// was invalid I got this text response so I added this code to
// handle this, shouldn't happen but good to have an error message
// for this use case
if (textResponse === 'Disallowed Key Characters.') {
return Promise.reject('Invalid request');
}
else {
try {
// Hopefully if we got to this point we can parse the json
const json = JSON.parse(textResponse);
if (json.result === '0') {
return Promise.reject('Unable to get status');
}
else {
return json;
}
} catch (error) {
// If you get this error message I likely have not handled
// a error state that I wasnt aware of
return Promise.reject('Unable to parse response');
}
}
});
}
function setStatus (sessionCookie, mode) {
return new Promise((resolve, reject) => {
if (!sessionCookie || sessionCookie.length === 0) {
reject('Please call getSessionCookie to get your session cookie first');
}
if (mode !== 'arm' && mode !== 'home' && mode !== 'disarm') {
reject('Invalid mode passed to setStatus');
}
resolve(fetch(`${urls.setStatus}${mode}`, {
method: 'POST',
headers: {
'Cookie': sessionCookie,
},
}));
});
}
module.exports = {
getSessionCookie,
getStatus,
setStatus,
}
i'm every new to coding but was able to piece the below together to return the current status of my alarm. the problem is I'm unable to get it to work. based on the above code could someone please tell me what I'm missing, or if I'm going down the wrong rabbit hole....
import requests
import webbrowser
url = “https://www.yalehomesystem.co.uk/homeportal/api/login/check_login”
payload = {‘username’: ‘email#domaim.com’, ‘password’: ‘mypass’}
with requests.session() as s:
# fetch the login page
s.get(url, data=payload)
url1='https://www.yalehomesystem.co.uk/homeportal/api/panel/get_panel_mode'
# post to the login form
r = s.post(url1, data=payload)
print(r.text)
To add more contexts I'm getting the following error
{"result":"0","message":"system.permission_denied","code":"999"}

Categories

Resources