How pass URL from API to Action (ReactJS) - javascript

Get API file
import { apiUrl } from '../config'
const baseUri = apiUrl
const uri = {
outline : '/course/income/outline'
}
const getURI = (key) => baseUri + uri[key]
module.exports = { apiMiddleware, get, post, put, ...{ delete: del }, uri, getURI }
Try pass this to my axios URL
import Api from '../middleware/api'
export function IncomeList () {
return dispatch => {
return (
axios.post(Api.getURI(outline),{}, {
headers: { 'X-Authenticated-Userid': '15000500000#1' }
}).then(function (response) {
console.log(response.data);
dispatch(receiveData(response.data.body));
})
.catch((error) => {
console.log(error);
})
)
}
}
But I get error Uncaught ReferenceError: outline is not defined. How to Pass correct URL ?

Pass string literal to getURI method:
Api.getURI('outline')
Calling Api.getURI(outline) makes interpreter look for outline variable, which is undefined in the current scope (hence ReferenceError).
Protip: linter, like ESLint, would catch this error early.

Related

SWR not working properly with async fetch

Recently updated SWR - now for some reason my data is not fetching properly.
const { data: expressionsData, error: expressionsError } = useSWRImmutable(
[`dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
apiRequest
);
Using this fetching,
import firebase from "./firebase";
export async function apiRequest(path, method = "GET", data) {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
//this is a workaround due to the backend responses not being built for this util.
if (path == "dashboard/get-settings") {
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.error === "error") {
throw new CustomError(response.code, response.messages);
} else {
return response;
}
});
}
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
console.log("error", response);
if (response.status === "error") {
// Automatically signout user if accessToken is no longer valid
if (response.code === "auth/invalid-user-token") {
firebase.auth().signOut();
}
throw new CustomError(response.code, response.message);
} else {
return response.data;
}
});
}
// Create an Error with custom message and code
export function CustomError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
// Check if a indexDb database exists
export function indexedDbdatabaseExists(dbname, callback) {
const req = window.indexedDB.open(dbname);
let existed = true;
req.onsuccess = function () {
req.result.close();
if (!existed) window.indexedDB.deleteDatabase(dbname);
callback(existed);
};
req.onupgradeneeded = function () {
existed = false;
callback(existed);
};
}
Now I'm looking at this StackOverflow thread,
useSWR doesn't work with async fetcher function
And thinking I'll just remake the fetcher to be without Async. I'm just wondering why this has stopped working though in general, and if I can just keep my existing codebase.
The error is a 400 message, it only happens with this expressions API call which takes longer to load due to the amount of data I think,
xxxx/dashboard/expression/get-expression-analytics?startTime=1648183720488&endTime=1650865720488 400 (Bad Request)
with error log
These calls are working fine, they have substantly less data though.
const { data: overall, error: psychometricError } = useSWRImmutable(
`dashboard/psychometric/get-psychometric-home?starttime=infinite`,
apiRequest
);
const { data: sentimentData, error: sentimentError } = useSWRImmutable(
[`dashboard/sentiment/get-sentiment-timefilter?startTime=${startDate}&endTime=${endDate}`, startDate, endDate],
fetchSentiment
);
Made an update to the fetch call to be more readable and specifically about the URL pathway.
import firebase from './firebase';
// Create an Error with custom message and code
export function CustomError(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
export async function expressionsRequest(path, method = 'GET') {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
return fetch(`/api/${path}`, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
})
.then((response) => {
if (!response.ok) {
throw `Server error: [${response.status}] [${response.statusText}] [${response.url}]`;
}
return response.json();
})
.then((receivedJson) => {
if (receivedJson.status === 'error') {
// Automatically signout user if accessToken is no longer valid
if (receivedJson.code === 'auth/invalid-user-token') {
firebase.auth().signOut();
}
throw new CustomError(receivedJson.code, receivedJson.message);
} else {
return receivedJson.data;
}
})
.catch((err) => {
console.debug('Error in fetch', err);
throw err;
});
}
Additionally, this is what the lambda function (using next API folder) looks like,
const requireAuth = require('../../_require-auth');
const { db } = require('../../_sql');
export default requireAuth(async (req, res) => {
const { uid: id } = req.user;
const startTime = Math.round(req.query.startTime * 0.001);
const endTime = Math.round(req.query.endTime * 0.001);
const parameters = [id, startTime, endTime];
//sql injection definitely possible here, need to work out better method of dealing with this.
const sqlText = `SELECT a,b,c,d,e,f,g,h,i FROM tablename WHERE a=$1 AND i BETWEEN $2 AND $3;`;
try {
const { rows } = await db.query(sqlText, parameters);
return res.status(200).json({
code: 0,
data: rows,
});
} catch (error) {
return res.status(200).json({
code: 0,
message: 'Error occurred in getting tablename',
error,
});
}
});
using postman with the same query, i.e.,
curl --location --request GET 'http://localhost:3000/api/dashboard/expression/get-expression-analytics?startTime=1648387240382&endTime=1651069240382' \
--header 'Authorization: Bearer xxxx' \
--data-raw ''
Successfully returns a response with data attached.
Based on your first code blocks, the startDate value is getting passed into the fetcher as method, and the endDate value is getting passed into the fetcher as data. This is based on the useSWR docs about passing in an array for the key argument: https://swr.vercel.app/docs/arguments#multiple-arguments
If the code you provided is correct, I'd assume the 400 is coming from trying to pass in a random value for the method option for fetch.
This should be fixed by only passing the API endpoint path into useSWR instead of an array:
const { data: expressionsData, error: expressionsError } = useSWRImmutable(
`dashboard/expression/get-expression-analytics?startTime=${startDate}&endTime=${endDate}`,
apiRequest
);

How to pass in Apps Metadata to a Class Method?

In refactoring my Bug Report code I moved a function into a new Class 'Logger', and call the static method as seen below:
$("#bugForm").submit((e) => {
e.preventDefault()
const input = document.getElementById('nameInput');
bugInfo = {
"name": `[${ticket.id}] Bug report`,
"story_type" : "Bug",
"description": `+ ${urlHelper.zendeskTicketUrl}` + " \n" + `+ ${input.value}`,
}
Logger.logInfo(bugInfo).then(collapse.collapse('toggle'))
})
});
however when I run the static method I receive the following error:
Uncaught (in promise) ReferenceError: metadata is not defined
Logger.js
class Logger {
constructor(settings) {
this.settings = settings;
}
static async logInfo(data = {}) {
console.log('Hello!')
const url = 'exampleUrl'
const response = fetch(url, {
method: 'POST',
headers: {
"Token": `${metadata.settings.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
return response.json();
}
}
In an attempt to fix this, I placed the following line in my code:
const logger = new Logger(metadata.settings);
And received the following error:
Uncaught (in promise) ReferenceError: Cannot access 'Logger' before initialization
I originally only made the class to use its static method, does the need for metadata prevent me from doing this? Am I not using this correctly?
So the problem the way you're passing the metadata.setting
I have changed way you use setting. Here's a working snippet
$("#bugForm").submit((e) => {
e.preventDefault()
const input = document.getElementById('nameInput');
// logic here
const bugInfo = {
info: "Hello"
}
// changed here as I removed static
logger.logInfo(bugInfo).then(console.log('print'))
});
class Logger {
constructor(settings) {
// getting the settings here and assigning it to the constructor variable
this.settings = settings;
console.log('hello', this.settings)
}
// removed static
async logInfo(data = {}) {
console.log('Hello!')
const url = 'exampleUrl'
console.log(data);
console.log(this.settings)
const response = fetch(url, {
method: 'POST',
headers: {
// using it here while calling the method
"Token": `${this.settings.token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
return response.json();
}
}
const metadata = {
settings: {
token: 'hello'
}
}
const logger = new Logger(metadata.settings);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="bugForm">
<button type="submit">
Submit
</button>
</form>

Passing query string parameters in Lambda functions (Netlify)

I am trying to pass a query string into my serverless function but it keeps returning an empty object.
search = (searchTerm) => {
// let url = `${URL}${searchTerm}`;
return fetch(`/.netlify/functions/token-hider?search=${searchTerm}`)
.then((response) => response.json())
.then((result) => {
console.log(result.results);
return results;
});
form.addEventListener("submit", (e) => {
e.preventDefault();
let searchTerm = input.value;
search(searchTerm);
});
const axios = require("axios");
const qs = require("qs");
exports.handler = async function (event, context) {
// apply our function to the queryStringParameters and assign it to a variable
const API_PARAMS = qs.stringify(event.queryStringParameters.search);
console.log(event);
// const API_PARAMS = qs.stringify(event.queryStringParameters);
console.log("API_PARAMS", API_PARAMS);
// Get env var values defined in our Netlify site UI
// TODO: customize your URL and API keys set in the Netlify Dashboard
// this is secret too, your frontend won't see this
const { KEY } = process.env;
const URL = `https://api.unsplash.com/search/photos?page=1&per_page=50&client_id=${KEY}&query=${API_PARAMS}`;
console.log("Constructed URL is ...", URL);
try {
const { data } = await axios.get(URL);
// refer to axios docs for other methods if you need them
// for example if you want to POST data:
// axios.post('/user', { firstName: 'Fred' })
return {
statusCode: 200,
body: JSON.stringify(data),
};
} catch (error) {
const { status, statusText, headers, data } = error.response;
return {
statusCode: error.response.status,
body: JSON.stringify({ status, statusText, headers, data }),
};
}
};
it works when i hard code the query string, and i can console log the search term and it is defined.
Since Netlify redirect mechanism is not able to provide you the data of which rule it matched, you could try to match the original request in your function to determine what it should do.
Hope this helps you solve your specific issue!
Here is the reference

axio. to get the data in a different file

I hope you can help me.
I'm trying to get a response from an API and use that information in another file.
I have 3 files:
api.jsx
import axios from 'axios';
export const api = (url, data) => {
const { path, method } = url;
let result ={};
axios({
method: method,
url: path,
data: data
})
.then(res => {
result = res.data;
console.log(res.data);
})
.catch(err => console.error(err));
return result;
};
url.jsx
export const URL = {
users:
{
getAllUsers: { path:'/users', method: 'post'},
login: { path:'/login', method: 'post'},
register: { path:'/register', method: 'post'},
version: { path:'/', method: 'get'},
}
}
app.js (within the render)
const data = {
email: 'hello#world.com',
password: '12345',
};
let result = api(URL.users.login, data);
console.log(result);
In the api file i get the proper response but in the react component no. I am aware that It's a problem of sync as i get first the console of app.jsx and later on the console of the api.jsx but i would like to respect the current structure or make something similar.
Any idea how to fix this without many changes?
PS. sorry about the mess. I tried to highlight all the code but for some reason it is not working fine.
You want to return a Promise in api.jsx
api.jsx
export const api = (url, data) => {
const { path, method } = url
return axios({ // the axios call returns a promise because of its .then; you can just return it
method: method,
url: path,
data: data
})
.then(res => {
return res.data;
})
.catch(err => {
console.error(err)
})
}

Fetch returning TypeError: Type error on iPhone 5

I am having an issue making an API call on the iPhone 5.
I have the following API call made inside a component
import FavouritesService from "../../api/FavouritesService";
const Favourites = () => {
const getFavs = () => {
favouritesService.getFavourites(0, 10, "FULL").then(response => {
if (response.success === false) {
//...
} else {
//...
}
});
};
};
export default Favourites;
My FavouritesService that gets the api endpoint and calls a function inside an import is as follows.
import api from "./api";
class FavouriteService {
getFavourites(page, pageSize, view) {
return api.get(
"/api/social-groups?page=" +
encodeURIComponent(page) +
"&pageSize=" +
encodeURIComponent(pageSize) +
"&view=" +
encodeURIComponent(view) +
"&type=FAVOURITE"
);
}
}
export default FavouriteService;
My api.js file where I do the API itself is as follows...
import _ from "lodash";
import "babel-polyfill";
import "isomorphic-fetch";
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
function handleFetchError(error) {
// Here is where I get TypeError: Type error
if (error) {
return { success: false, error: error };
}
}
function payloadOptions(method, body) {
var postBody = body;
var contentType = "application/x-www-form-urlencoded";
if (typeof postBody === "object") {
postBody = JSON.stringify(postBody);
contentType = "application/json";
}
return {
method: method,
body: postBody,
headers: {
"Content-Type": contentType
}
};
}
const defaultOptions = {
redirect: "error",
headers: {
Accept: "application/json"
}
};
class API {
request(url, options) {
return fetch(url, _.defaultsDeep(options || {}, defaultOptions))
.then(handleErrors)
.then(response => response.json())
.catch(handleFetchError);
}
get(url, options) {
return this.request(
url,
_.defaultsDeep(options || {}, payloadOptions("GET"))
);
}
}
export default new API();
This is where the error occurs in the handleFetchError, error returns TypeError: Type error. When I console.log this, that's all I get, I'm not able to drill down any further to actually inspect what's actually happened here.
I've tried to google this but no one else seems to have this specific issue so I assume I have gone wrong at some step when making this GET request.
Any help would be greatly appreciated as I have been stuck on this for some time.
I found that I had to use a polyfill to allow me to fetch.
This answer helped to figure out I had to install whatwg-fetch

Categories