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

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);

Related

Unable to invoke "btoa" and "item.slice" method in my script for retrieving the playlist

Whenever I am trying to invoke the "btoa" method, I am not able to use this within my script. I created a variable to store the client id: client_secret in base64. The id and secrets are being retrieved from the ".env" file.
I have also tried to use the Buffer method, but unable to use this as well. I am getting the error "invalid from" in Buffer.
can someone help me?
Please look at the full code,
const client_id = process.env.SPOTIFY_CLIENT_ID;
const client_secret = process.env.SPOTIFY_CLIENT_SECRET;
const refresh_token = process.env.SPOTIFY_REFRESH_TOKEN;
const basic = btoa(`${client_id}:${client_secret}`);
const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;
const TOP_TRACKS_ENDPOINT = `https://api.spotify.com/v1/me/top/tracks`;
const TOKEN_ENDPOINT = `https://accounts.spotify.com/api/token`;
const getAccessToken = async () => {
const response = await fetch(TOKEN_ENDPOINT, {
method: 'POST',
headers: {
Authorization: `Basic ${basic}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token
})
});
return response.json();
};
export const getNowPlaying = async () => {
const { access_token } = await getAccessToken();
return fetch(NOW_PLAYING_ENDPOINT, {
headers: {
Authorization: `Bearer ${access_token}`
}
});
};
export const getTopTracks = async () => {
const { access_token } = await getAccessToken();
return fetch(TOP_TRACKS_ENDPOINT, {
headers: {
Authorization: `Bearer ${access_token}`
}
});
};
Using the above script I am trying to embed the customized Spotify play on my site. This wrapper is intended to display the top track as well.
Also, whenever I am trying to run the wrapper used to display the top tracks, it displays the following error,
Full code for displaying the top tracks:
import { type NextRequest } from 'next/server';
import { getTopTracks } from 'lib/spotify';
export const config = {
runtime: 'experimental-edge'
};
export default async function handler(req: NextRequest) {
const response = await getTopTracks();
const { items } = await response.json();
const tracks = items.slice(0, 10).map((track) => ({
artist: track.artists.map((_artist) => _artist.name).join(', '),
songUrl: track.external_urls.spotify,
title: track.name
}));
return new Response(JSON.stringify({ tracks }), {
status: 200,
headers: {
'content-type': 'application/json',
'cache-control': 'public, s-maxage=86400, stale-while-revalidate=43200'
}
});
}
The problem is that you misspelled the Bytes to ASCII function, it is btoa, not btao.
If you are looking to do it the other way around, spell it atob.

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

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

React-Native dynamically change fetch url

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 },
})
}

axios post request with headers and body are not working

In my project, I use this modified axios function to make an API request:
// api.js
function request (method, path, { query, params }, fields) {
query = escapeQuery(query); // make query string
return axios[method](`${API.URL}${path}?${query}`, {
...fields,
params
});
}
let methods = {};
['post', 'get', 'patch', 'delete', 'put'].forEach(method => {
methods[method] = (...args) => request(method, ...args);
});
So in actual case, like this:
import API from 'api.js';
...
getSomePages (state) {
var config = {
headers: { 'x-access-token': state.token }
};
let query = { 'page': state.selected_page };
return new Promise((resolve, reject) => {
API.get(`/myapipath`, {query}, config).then(...);
});
},
...
When I call API.get() like the above, it works very well. The problem is when I call API.post. It seems that it cannot distinguish the correct field. For example, when I call:
likePost (state, post_id, user_id) {
var config = {
headers: {
'x-access-token': state.token,
'Content-Type': 'application/x-www-form-urlencoded'
}
};
var data = {
post_id, user_id
};
return new Promise((resolve, reject) => {
API.post(`/myapipath`, {data}, config).then(...);
});
},
This API.post call keeps failing, so I looked up the request and found that it doesn't have headers - all the fields I've sent are in the request body.
So I tried something like:
API.post(`/myapipath`, {}, config, data)...
API.post(`/myapipath`, data, config)...
API.post(`/myapipath`, {data, config})...
etc...
but it all failed. It looks like they all cannot understand which is the header. How can I solve this?

Categories

Resources