I am relatively new in React but I am trying to create a class/method for network call. Nothing complex just a way to make the code readable.
I have a class:
class Auth {
getToken(username, password) {
const endpointOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: `${username}`, password: `${password}` })
};
fetch(`${Constant.BASE_WP_URL}${Constant.TOKEN_ENDPOINT}`, endpointOptions)
.then(async response => {
const data = await response.json();
if (!response.ok) {
// get error message from body or default to response status
const error = (data && data.message) || response.status;
throw error;
}
return data;
})
.catch(error => {
throw error;
});
}
}
export default Auth;
I am trying to call it using :
import Auth from '../../data/network/Auth';
requestSignIn = (event) => {
event.preventDefault();
this.setState({loading: true})
try {
const authData = Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem('authToken', authData.token)
} catch (error) {
console.log("Connection to WP - Auth Token failed ")
console.error(error);
}
}
but React is complaining because getToken is not a function. I am trying to create a class Auth to have inside all methods/functions I need related to Auth process.
Also, is it the right way to handle the result ? is the try/catch as done works or should I do it differently as the getToken is an API call.
Any idea ?
pretty sure, it's easy but I can't find any interesting topics on Google.
Thanks
I think, if you want to use function directly in OOP of JavaScript, you must put static keyword in front of the function name.
In your auth file
static class Auth {
static getToken(username, password) {
...
}
}
In your index file
import Auth from '../../data/network/Auth';
const authData = Auth.getToken(`${this.state.email}`, `${this.state.password}`);
If you don't have static in front of the function name. You have to create a new instance of the class Auth in order to use the function inside.
import Auth from '../../data/network/Auth';
const AuthInit = Auth();
authData = AuthInit.getToken(`${this.state.email}`, `${this.state.password}`);
===========================
Update for applying asynchronous method
// ====== auth file
static class Auth {
static async getToken(username, password) {
...
// assign fetched data to data_fetch
const data_fetch = fetch(`${Constant.BASE_WP_URL}${Constant.TOKEN_ENDPOINT}`, endpointOptions)
.then(async response => {
const data = await response.json();
if (!response.ok) {
// get error message from body or default to response status
const error = (data && data.message) || response.status;
throw error;
}
return data;
})
.catch(error => {
throw error;
});
return data_fetch;
}
}
// ======= index file
import Auth from '../../data/network/Auth';
...
requestSignIn = async (event) => { // put async in front of your function
// the function outside (requestSignIn) must be async type
// in order to use await keyword for getToken() function
event.preventDefault();
this.setState({loading: true})
try {
// because your getToken function is now a async function, you can
// use "await" keyword in front of it to wait for fetching data to finish
const authData = await Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem('authToken', authData.token)
} catch (error) {
console.log("Connection to WP - Auth Token failed ")
console.error(error);
}
}
Hope this would help
but React is complaining because getToken is not a function
You've defined getToken as a method of an Auth instance, not a static function.
But you don't need an Auth class here at all, just use the proper exports/imports.
replace the Auth-class with:
export function getToken(username, password) {
//...
};
and you can either
/// import all exports from that file under the name `Auth`
import * as Auth from '../../data/network/Auth';
// ...
const authData = Auth.getToken(...);
or
// import these specific exports from that file.
import { getToken } from '../../data/network/Auth';
// ...
const authData = getToken(...);
The last option has the advantage that it can be tree-shaken. If You have some build-process, the compiler can eliminate all the pieces of code that you don't use; especially useful for libraries.
Edit:
Even if you want to keep the default import and import the entire thing, imo. it makes more sense to use a simple Object rather than a class with static methods.
function getToken(username, password) {
//...
}
export default {
getToken
};
In you class definition add static in front of your function to be
class Auth {
static async getToken(username, password) {
const endpointOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: `${username}`, password: `${password}` })
};
try {
const response = await fetch(`${Constant.BASE_WP_URL}${Constant.TOKEN_ENDPOINT}`, endpointOptions)
const data = await response.json();
if (!response.ok) {
const error = (data && data.message) || response.status;
throw error;
}
return data;
} catch (error) {
throw error
}
}
}
export default Auth;
then you will be able to call it as static function.
and requestSignIn will be using it in the following code
requestSignIn = async (event) => {
event.preventDefault();
this.setState({ loading: true })
try {
const authData = await Auth.getToken(`${this.state.email}`, `${this.state.password}`);
sessionStorage.setItem('authToken', authData.token)
} catch (error) {
console.log("Connection to WP - Auth Token failed ")
console.error(error);
}
}
Related
Im making a user authorization process with JWT tokens.
How does the flow look like?
User logs in - gets an access token and a refresh token from a server, as a response
Access token comes in json body and is saved in local storage. Refresh token comes in a httpOnly cookie.
User can use getAllUsers method untill access token is valid.
Whenever getAllUsers method returns 401 unauthorized (when access token expires), there is a request being sent to refresh token endpoint - getRefreshToken, which returns new access token that is being saved to local storage
Refresh token expires and user is being logged out.
Whole flow in Postman works but i have got problem at frontend side.
Function getAllUsers works until access token expires.
Thats why I made a global function in a util file that checks if a response is 401 and if so, it sends a request to get a new access token and calls a function which returned that error.
However it does not work.
I think that the problem is in getAllUsers function which immediately goes to catch block (when cant fetch list of users because of 401) and does not invoke that global function from util file. Console logs from both functions (getDataFromResponse, getRefreshToken) does not work so it does not even get there.
Any ideas??
API utils file
import { AxiosResponse } from "axios";
import { apiService } from "./api.service";
type ApiServiceMethods = keyof typeof apiService;
export const getDataFromResponse = async (
response: AxiosResponse,
funName: ApiServiceMethods,
...args: any
): Promise<any> => {
if (response.status === 401) {
console.log("error");
await apiService.getRefreshToken();
return await apiService[funName](args);
}
return response.data;
};
API Service:
import { getDataFromResponse } from "./api.utils";
import axios from "./axios";
type LoginArgs = {
password: string;
username: string;
};
const apiServiceDef = () => {
const login = async (args: LoginArgs) => {
try {
const response = await axios.post("/login", {
username: args.username,
password: args.password,
});
const { data } = response;
const { token } = data;
localStorage.setItem("accessToken", token);
return response;
} catch (e) {
throw new Error("Custom");
}
};
/* problem here */
const getAllUsers = async () => {
const Token = localStorage.getItem("accessToken");
try {
const response = await axios.get("/users", {
headers: {
Token,
},
});
return await getDataFromResponse(response, "getAllUsers");
} catch (e) {
console.log(e);
}
};
/* problem here */
const getRefreshToken = async () => {
try {
console.log("fetch new access token");
const response = await axios.get("/refreshToken");
if (response.status === 401) {
localStorage.removeItem("accessToken");
throw new Error("TokenExpiredError");
}
const { data } = response;
const { token } = data
localStorage.setItem("accessToken", token);
return response;
} catch (e) {
console.log(e);
}
};
return { login, getRefreshToken, getAllUsers };
};
export const apiService = apiServiceDef();
I usually use a wrapper around the async functions or just use axios interceptors (https://stackoverflow.com/a/47216863/11787903). Be sure that err.response.status is right property, not sure about that, but this solution should work for you.
const asyncWrapper = async (handler) => {
try {
return handler()
} catch (err) {
if (err.response.status === 401) {
// refresh token then again call handler
await refreshToken()
return handler()
}
}
}
const getAllUsers = asyncWrapper(() => {
const Token = localStorage.getItem("accessToken");
return axios.get("/users", {
headers: {
Token,
},
});
});
having a real problem with getting this code to work. I have everything set up working great with Appwrite. I'm getting a response back from the server, but in my promise.then it finishes the other code and returns undefined from the login function. so then the post async function is sending a blank array in the try block. I've tried setting this code up every way I can think of but it never works. Sorry, i'm still trying to wrap my head around the promises and async js.
import { Appwrite } from 'appwrite';
export async function post({ locals, request }) {
const { email, password } = await request.json();
function login() {
// add logic to authenticate user with external service here
const sdk = new Appwrite();
sdk
.setEndpoint('https://') // API Endpoint
.setProject('') // project ID
;
let promise = sdk.account.createSession(email, password);
let userLogin;
promise.then(function (response) {
console.log(response); // Success
userLogin = response.providerUid;
console.log(userLogin);
}, function (error) {
console.log(error); // Failure
});
console.log('login.json.js', { email, password: !!password });
console.log(userLogin);
return userLogin;
}
try {
const user = login();
locals.user = user;
return {
status: 200
};
} catch (error) {
const message = `Error in endpoint /api/login.json: ${error}`;
return {
status: 500,
body: message
};
}
}
You're returning userLogin in login before it's even populated in the asynchronous promise.then chain
Also, since you're currently handling the rejection in your promise.then(onFulfilled, onRejected) that would mean any rejection is handled inside login and your try/catch (once written correctly) would never have an error to catch since login handled it already
One more potential issue - if const { email, password } = await request.json(); rejects, then the error will be thrown to whatever called post - is that what you want? or did that also need to be handled inside post?
Anyway here's how to fix your code:
import { Appwrite } from 'appwrite';
export async function post({ locals, request }) {
// note: if this throws then the error will be handled by whatever calls `post`
const { email, password } = await request.json();
function login() {
// add logic to authenticate user with external service here
const sdk = new Appwrite();
sdk
.setEndpoint('https://') // API Endpoint
.setProject('') // project ID
;
const promise = sdk.account.createSession(email, password);
return promise.then(function(response) {
let userLogin = response.providerUid;
return userLogin;
// or without redundant `userLogin` variable
// return response.providerUid;
});
}
try {
const user = await login();
locals.user = user;
return { status: 200 };
} catch (error) {
const message = `Error in endpoint /api/login.json: ${error}`;
return { status: 500, body: message };
}
}
Or, making login async
import { Appwrite } from 'appwrite';
export async function post({ locals, request }) {
// note: if this throws then the error will be handled by whatever calls `post`
const { email, password } = await request.json();
async function login() {
// add logic to authenticate user with external service here
const sdk = new Appwrite();
sdk
.setEndpoint('https://') // API Endpoint
.setProject('') // project ID
;
let response = await sdk.account.createSession(email, password);
let userLogin = response.providerUid;
return userLogin;
}
try {
const user = await login();
locals.user = user;
return {
status: 200
};
} catch (error) {
const message = `Error in endpoint /api/login.json: ${error}`;
return {
status: 500,
body: message
};
}
}
Or, removing inner Login function completely
import { Appwrite } from 'appwrite';
export async function post({ locals, request }) {
// note: if this throws then the error will be handled by whatever calls `post`
const { email, password } = await request.json();
try {
const sdk = new Appwrite();
sdk.setEndpoint('https://') // API Endpoint
.setProject(''); // project ID
const response = await sdk.account.createSession(email, password);
console.log(response); // Success
locals.user = response.providerUid;
return { status: 200 };
} catch (error) {
const message = `Error in endpoint /api/login.json: ${error}`;
return { status: 500, body: message };
}
}
I have a React Native app using Axios.
Im trying to authenticate the user and then set the authorization header from the response but im getting a weird order of execution.
The setClientToken is not being called before the code after it to do a get request is to be called. ie this is what i get logged:
Loggin in....
Authenticating...
Getting Recipes...
Auth... undefined
Done.
... [Error: Request failed with status code 401] //getRecipes call
Setting token
So you see setting token is done last and that means every axios after that will work but not the recipe.js call.
//app.js
async componentDidMount() {
console.log("Loggin in....");
await LogIn(getUser().username, getUser().password);
console.log("Done.");
}
render () {
//Recipe component rendered.
}
//recipesApi.js
export function getRecipes(category, offset, count) {
console.log("Getting Recipes...");
const url = `search?category=${category}¤tPage=${offset}&pageSize=${count}`;
console.log("Auth...", APIKit.defaults.headers.common["Authorization"]);
return APIKit.get(getUrl(url));
}
//user.js
import APIKit, { setClientToken } from "./apiKit";
export default async function LogIn(email, password) {
console.log("Authenticating...");
APIKit.post("/users/authenticate", {
username: email,
password: password,
})
.then((token) => setClientToken(token))
.catch((error) => console.log(error));
}
//APIKit.js
import axios from "axios";
// Create axios client, pre-configured with baseURL
let APIKit = axios.create({
baseURL: "http://192.168.1.4:4000/api",
timeout: 10000,
});
APIKit.interceptors.response.use(function (response) {
return response.data;
});
export const setClientToken = (token) => {
console.log("Setting token...", token.token);
APIKit.interceptors.request.use(
function (config) {
config.headers.Authorization = `Bearer ${token.token}`;
return config;
}, null, { synchronous: true }
);
};
I'm new to Next Js and functional comoponents. I'm trying to retrieve data from /api/retrieve2
//this is retrieve page
export default function Retrieve() {
const onSubmit = async data => {
const { user } = await axios.post("/api/retrieve2", data);
console.log(user) // user here is undefined
};
return (...);
}
//this is retrieve2, inside the API folder
export default async (req, res) => {
try {
const { data } = await axios.post(myBackendUrl, req.body);
console.log(data) //this is printing the right data - { email: 'casas#gmail.com', code: '123123' }
res.json(data);
} catch (e) {
res.json({ err: e.message || e });
}
};
What am I missing, is this something about Next? About functional components?
You should read about ES6 destructuring
You try to destructure user but the axios respons witch is a object doesnt contain the key user
For data it works because there is a data property in the response
Here are all properties that you can destructure:
{ data, status, statusText, headers, config, request }
You need to get the full URL to make http request to using getInitialProps, here Home is the name of your component
const Home = ({ENDPOINT}) => {
const onSubmit = async data => {
const { data } = await axios.post(`${ENDPOINT}/api/retrieve2`, data);
// consider changing `user` here to `data` since Axios stores response in data object
console.log(data) // should be defined
};
return (...);
}
Home.getInitialProps = ctx => {
const ENDPOINT = getEndpoint(ctx.req);
return { ENDPOINT };
};
// You should store this somewhere you can reuse it
export function getEndpoint(req) {
return !!req
? `${req.headers['x-forwarded-proto']}://${req.headers['x-forwarded-host']}`
: window.location.origin;
}
having 2 api's. method POST-Login method GET-data. and server has cors enabled. Login api working fine, but when call api with GET method it gets failed.
Code:
->api Login-POST
const login = async (email, password) => {
console.log("in auth service");
const userDetail = {
username:email,
// email,
password
};
try {
// unsetHeadersWithUserToken();
const afterSuccess = await api.post(apiDetail.auth.url, userDetail);
if (afterSuccess) {
return afterSuccess.data;
}
} catch (error) {
console.log("error: ", error.response.error);
if (error.category === 'User Permissions') {
// forceLogout();
}
throw error;
}
};
->api-GET
try{
// console.log("url : ", apiDetail.partnerLocations.url);
let token = sessionStorage.getItem('token');
setHeadersWithUserToken(token);
let apiResponse = await api.get(apiDetail.partnerLocations.url);
return apiResponse;
}catch(error){
console.info('##### demand-response.js:11 #####');
console.info('========================= Start =========================');
console.error('error = ', JSON.stringify(error));
// console.log(error.response.data)
console.info('========================== End ==========================');
throw error;
}
->axios call
import axios from 'axios';
import { environment } from '../../utils/constants';
let api;
let apiDetail = {
baseURL: environment.baseURL,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
};
const setAPI = apiDetail => {
api = axios.create(apiDetail);
};
setAPI(apiDetail);
const setHeadersWithUserToken = token => {
api.defaults.headers.common['Authorization'] = token;
};
export {
api,
setHeadersWithUserToken,
};
Image-1
showing console error
Image-2
network call response
Try this
const proxyurl = "https://cors-anywhere.herokuapp.com/"
cosnt url = 'Your URL'
axios.get(proxyurl + url)
I faced the same issue and this works nicely.
Add the "proxy" property (found at the bottom here) to package.json:
"proxy": "http://localhost:<PORT-GOES-HERE>"
Now, instead of making HTTP requests like this:
axios.get("http://localhost:8080/example")
You should write them like this:
axios.get("/example")