This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
So I want to have fetch request in separate file. Just for testing. And then call it in componentDidMount. I still get undefined. What is the proper way of doing it?
This is my function:
const fetchNewWord = () => {
fetch("https://wordsapiv1.p.rapidapi.com/words/?lettersMax=11&random=true", {
headers: {
"X-Rapidapi-Host": "wordsapiv1.p.rapidapi.com",
"X-Rapidapi-Key": "myKey"
}
})
.then(data => data.json())
.then(data => {
return data.word;
});
};
export default fetchNewWord;
You have return your fetch callback results as function's response:
export const fetchNewWord = () => {
return fetch("https://wordsapiv1.p.rapidapi.com/words/?lettersMax=11&random=true", {
headers: {
"X-Rapidapi-Host": "wordsapiv1.p.rapidapi.com",
"X-Rapidapi-Key": "myKey"
}
})
.then(data => data.json())
.then(data => data.word);
};
When you do const data = fetchNewWord(); console.log(data);, you'll see the result.
You can create a separate service for fetch in your code and use it as a independent providers
Here is the httpRequest.js which you can use default fetch api:
import axios from 'axios';
class HttpRequest {
constructor(baseURL) {
this.axios = axios.create({
baseURL,
});
}
reponseInterceptor() {
// Add a response interceptor
this.axios.interceptors.response.use(
response => (
// Do something with response data
response
),
error => (
// Do something with response error
Promise.reject(error)
),
);
}
requsetInterceptor() {
this.axios.interceptors.request.use(
config => (
// Do something before request is sent
config),
error => (
// Do something with request error
Promise.reject(error)
),
);
}
fetch(url, params, config = {}) {
return this.axios.get(url, {
params,
...config,
});
}
create(url, data, config = {}) {
return this.axios.post(url, data, {
...config,
});
}
update(url, data, config = {}) {
return this.axios.put(url, data, {
...config,
});
}
patch(url, data, config = {}) {
return this.axios.patch(url, data, {
...config,
});
}
remove(url, params, config = {}) {
return this.axios.delete(url, {
params,
...config,
});
}
}
export default HttpRequest;
Here is how you can create your words.js services:
import config from 'config';
import HttpRequest from './httpRequest';
export default class WordService extends HttpRequest {
constructor(servicePath) {
super(config.markMeHost);
this.path = `${config.markMeHost}/${servicePath}`;
}
listWords() {
return this.fetch(this.path, {});
}
createWords(data) {
return this.create(this.path, data);
}
updateWords(data, id) {
return this.update(`${this.path}/${id}`, data);
}
deleteWords(id) {
return this.remove(`${this.path}/${id}`);
}
}
Your api service index.js:
import WordService from './words';
// Give arg to provider to start endpoint with specific path for example = abc.com/api/words
export default new WordService('words');
For further details you can check my github account for axios service https://github.com/m-nathani/markme/tree/master/frontend/src/service/api
Related
I want to build my next js project in which i am using
https://www.npmjs.com/package/#react-oauth/google
but when I build it i get the following :
this is layout.js and in _app.js I have all the components wrapped in GoogleOAuthProvider
import { GoogleLogin } from '#react-oauth/google';
import {FcGoogle} from "react-icons/Fc"
import { useGoogleLogin } from '#react-oauth/google';
export default function Layout({ children }) {
const client_id = ""
const responseGoogle = (response) => {
console.log(response);
}
CUTTED (NOT RELEVANT)
const login = useGoogleLogin({
onSuccess: codeResponse => {
const { code } = codeResponse;
console.log(codeResponse)
axios.post("http://localhost:8080/api/create-tokens", { code }).then(response => {
const { res, tokens } = response.data;
const refresh_token = tokens["refresh_token"];
const db = getFirestore(app)
updateDoc(doc(db, 'links', handle), {
refresh_token : refresh_token
})
updateDoc(doc(db, 'users', useruid), {
refresh_token : refresh_token
}).then(
CUTTED (NOT RELEVANT)
)
}).catch(err => {
console.log(err.message);
})
},
onError: errorResponse => console.log(errorResponse),
flow: "auth-code",
scope: "https://www.googleapis.com/auth/calendar"
});
return (
<>
CUTTED (NOT RELEVANT)
</>
)
}
Everything works perfect in dev mode but it does not want to build
I've faced this issue too. So I use 'GoogleLogin' instead of 'useGoogleLogin', then you can custom POST method on 'onSuccess' property.
import { GoogleLogin, GoogleOAuthenProvider} from '#react-oauth/google';
return(
<GoogleOAuthProvider clientId="YOUR CLIENT ID">
<GoogleLogin
onSuccess={handleLogin}
/>
</GoogleOAuthProvider>
The async function will be like...
const handleLogin = async = (credentialResponse) => {
var obj = jwt_decode(credentialResponse.credential);
var data = JSON.stringify(obj);
console.log(data);
const data = {your data to send to server};
const config = {
method: 'POST',
url: 'your backend server or endpoint',
headers: {},
data: data
}
await axios(config)
}
Spending whole day, this solve me out. Just want to share.
You have to wrap your application within GoogleOAuthProvider component. Please keep in mind that you will need your client ID for this.
import { GoogleOAuthProvider } from '#react-oauth/google';
<GoogleOAuthProvider clientId="<your_client_id>">
<SomeComponent />
...
<GoogleLoginButton onClick={handleGoogleLogin}/>
</GoogleOAuthProvider>;
I am using Remix, along with Remix-Auth and using the Twitch API/OAuth, which requires that I check in with their /validate endpoint every hour docs. I had someone recommend that I use a resource route and POST to that if the validation endpoint returned a status of 401, however, I need as I stated before the request needs to be sent every hour I figured maybe I could use something like React-Query to POST to the resource route every hour.
Just pointing out that I use createCookieSessionStorage with Remix Auth to create the session
Problem
I haven't been able to achieve the actual session being destroyed and a user being re-routed to the login page, I have left what actual code I have currently any help or suggestions to actually achieve the session being destroyed and be re-routed to the login page if the validation fails would be greatly appreciated.
// React Query client side, checks if the users token is still valid
const { error, data } = useQuery("TV-Revalidate", () =>
fetch("https://id.twitch.tv/oauth2/validate", {
headers: {
Authorization: `Bearer ${user?.token}`,
},
}).then((res) => res.json())
);
The above React Query returns this
// My attempt at the resource route
// ~routes/auth/destroy.server.ts
import { ActionFunction, redirect } from "#remix-run/node";
import { destroySession, getSession } from "~/services/session.server";
export const action: ActionFunction = async ({request}) => {
const session = await getSession(request.headers.get("cookie"))
return redirect("/login", {
headers: {
"Set-Cookie": await destroySession(session)
}
})
}
// Second attempt at resource route
// ~routes/auth/destroy.server.ts
import { ActionFunction, redirect } from "#remix-run/node";
import { destroySession, getSession } from "~/services/session.server";
export const action: ActionFunction = async ({request}) => {
const session = await getSession(request.headers.get("cookie"))
return destroySession(session)
}
I attempted using an if statement to POST to the resource route or else render the page, however, this definitely won't work as React errors out because functions aren't valid as a child and page is blank.
//index.tsx
export default function Index() {
const { user, bits, vali } = useLoaderData();
console.log("loader", vali);
const { error, data } = useQuery("TV-Revalidate", () =>
fetch("https://id.twitch.tv/oauth2/validate", {
headers: {
Authorization: `Bearer ${user?.token}`,
},
}).then((res) => res.json())
);
if (data?.status === 401)
return async () => {
await fetch("~/services/destroy.server", { method: "POST" });
};
else
return ( ... );}
You could use Remix' useFetcher hook.
https://remix.run/docs/en/v1/api/remix#usefetcher
// Resource route
// routes/api/validate
export const loader: LoaderFunction = async ({ request }) => {
const session = await getSession(request);
try {
const { data } = await fetch("https://id.twitch.tv/oauth2/validate", {
headers: {
Authorization: `Bearer ${session.get("token")}`
}
});
return json({
data
}, {
headers: {
"Set-Cookie": await commitSession(session),
}
});
} catch(error) {
return redirect("/login", {
headers: {
"Set-Cookie": await destroySession(session)
}
});
}
}
And then in your route component something like this:
const fetcher = useFetcher();
useEffect(() => {
if (fetcher.type === 'init') {
fetcher.load('/api/validate');
}
}, [fetcher]);
useEffect(() => {
if(fetcher.data?.someValue {
const timeout = setTimeout(() => fetcher.load('/api/validate'), 1 * 60 * 60 * 1000);
return () => clearTimeout(timeout);
}
},[fetcher.data]);
https://codesandbox.io/s/login-authentication-usecontext-66t9t?file=/src/index.js
Here how we can pass token in headers for any other pages in codesandbox link. In my code i have action file like this. im getting my response in localstorage.how can i pass my accesstoken here as headers in this page.
import axios from 'axios';
export const upiAction = {
upi,
};
function upi(user) {
return (dispatch) => {
var data = {
upiId: user.upiId,
accountNumber: user.accountNumber,
};
axios
.post('http://localhost:9091/upiidcreation', data
)
.then((res) => {
console.log("res", (res));
const { data } = res;
alert(JSON.stringify(data.responseDesc));
// window.location.pathname = "./homes";
if (data.responseCode === "00") {
window.location.pathname = "./home"
}
})
.catch(err => {
dispatch(setUserUpiError(err, true));
alert("Please Check With details");
});
};
}
export function setUserUpi(showError) {
return {
type: 'SET_UPI_SUCCESS',
showError: showError,
};
}
export function setUserUpiError(error, showError) {
return {
type: 'SET_UPI_ERROR',
error: error,
showError: showError,
};
}
I'm building an App with Next.js, and I need to connect to specific API routes (set up with API Platform) and populate pages with the route's responses.
The API is working fine, but no matter how I try to implement my Axios call inside the getServerSideProps, I always get the same error, ECONNREFUSED, from my Node stack.
I tried to get the data from useEffect() and it's working fine, but I would like to know if there's a way to call it directly in getServerSideProps.
I'm using a Node container for Docker, and the routes are authenticated through a JWT Token (stored in the session and the client cookies for the server-side connection)
Here are is my code:
pages/accounts.js:
export async function getServerSideProps(context) {
const cookies = new Cookies(context.req.headers.cookie)
const adminToken = cookies.get('jwtToken')
const res = await getAllAccounts(adminToken)
return {
props: {
testdata: ''
},
}
}
lib/accounts.js:
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`).then((response) => {
}).catch((error) => {
console.dir(error)
})
}
HTTP wrapper:
import axios from 'axios';
import jwt_decode from "jwt-decode";
import mockAdapter from 'axios-mock-adapter';
const service = ({ jwtToken = null, store = null, mockURL = null, mockResponse = null, multipart = false } = {}) => {
const options = {};
options.baseURL = process.env.NEXT_PUBLIC_API_URL + '/api';
if(multipart === true) {
options.headers = {
'Content-Type': 'multipart/form-data'
}
} else {
options.headers = {
'Content-Type': 'application/ld+json',
accept: 'application/ld+json'
}
}
const instance = axios.create(options);
instance.interceptors.response.use(response => {
return response;
}, error => {
return Promise.reject(error);
})
if (mockURL !== null && mockResponse !== null) {
let mock = new mockAdapter(instance);
mock.onAny(mockURL).reply(200, mockResponse)
}
return instance;
};
export default service;
Through the error dump in the node stack, I managed to see that the request headers are correct, and the JWT correctly passed through.
Do not use Axios. Just use fetch().
Next.js polyfills fetch() by default on both the client and server, so you can just use it:
In addition to fetch() on the client-side, Next.js polyfills fetch() in the Node.js environment. You can use fetch() in your server code (such as getStaticProps/getServerSideProps) without using polyfills such as isomorphic-unfetch or node-fetch.
Source.
getServerSideProps works well with axios if we return response.data
export const getServerSideProps: GetStaticProps = async ({ params }) => {
const { brandName } = params as IParams;
const brandData = await $host.get(`api/brand/${brandName}`).then(response => response.data);
return {
props: {
brand: brandData,
},
};
};
Your problem is that your async method does not return a promise.
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`);
return res;
}
In my NextJS begining I followed this tutorial , and I changed fetch to axios in this way:
export const getStaticPaths = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await res.json();
const paths = data.map((ninja) => {
return {
params: { id: ninja.id.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const data = await res.json();
return {
props: { ninja: data },
};
};
I applied the change using useEffect()
useEffect(() => {
// const data = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
// const res = await data.json();
// setninja(res);
const fetchData = async () => {
const result = await axios(`https://jsonplaceholder.typicode.com/users/${id}`);
setninja(result.data);
};
fetchData();
console.log(data);
}, []);
I hope this info will be useful for you.
I Used Axios in getServerSideProps without any problems.
export const getServerSideProps: GetServerSideProps = async({
params,
res
}) => {
try {
const response = await axios.get(`/api/test`);
return {
props: {
data: response.data
},
}
} catch {
res.statusCode = 404;
return {
props: {}
};
}
};
I'm creating an autocomplete function, so basically whenever I type on search box it will cancel the previous http get request. I checked the jQuery ui autocomplete and that's what they did. Is that possible in axios if yes how can I implement it. So far here's my code for http get request:
export function autocompleteSearchTest(value){
return axios.get(`https://jqueryui.com/resources/demos/autocomplete/search.php`,{
params: {
q: value
}
})
.then(response => {
return response.data.response;
})
.catch(error => {
const result = error.response;
return Promise.reject(result);
});
}
Here's the screenshot when I type on the search box:
as you can see it doesn't cancel the previous http get request.
Since axios v0.15 you can cancel request:
You can also create a cancel token by passing an executor function to
the CancelToken constructor:
var CancelToken = axios.CancelToken;
var cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
});
// cancel the request
cancel();
For more information please have a look Cancellation. And according to your code:
import React from 'react'
import axios from 'axios';
export function autocompleteSearchTest(value) {
if (cancel != undefined) {
cancel();
}
return axios.get(`https://jqueryui.com/resources/demos/autocomplete/search.php`, {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
}),
params: {
q: value
}
})
.then(response => {
return response.data.response;
})
.catch(error => {
const result = error.response;
return Promise.reject(result);
});
}
var CancelToken = axios.CancelToken;
var cancel;
export class AutoComplete extends React.Component {
constructor() {
super()
this.state = {
search: ''
};
this.handleSearchChange = this.handleSearchChange.bind(this);
}
handleSearchChange(event) {
const target = event.target;
const value = target.value;
this.setState({
search: value
});
autocompleteSearchTest(value)
}
render() {
return (
<div className="App-intro Dialog-section">
<h2>AutoComplete</h2>
<div className="form-control">
<label htmlFor="password">Lookup :</label>
<input name="search" type="text" value={this.state.search}
onChange={this.handleSearchChange}
id="password" ref="password" placeholder="Enter line"/>
</div>
</div>
)
}
}
export default AutoComplete;
And here it's
use: react, axios and redux-axios-middleware
user actions:
import axios from 'axios';
import * as type from 'constants/user';
const CancelToken = axios.CancelToken;
let cancel;
export const addUser = (data) => {
if (cancel !== undefined) cancel();
return ({
types: [type.ADD_USER_REQUEST, type.ADD_USER_SUCCESS, type.ADD_USER_FAILURE],
payload: {
request: {
url: '/api/user',
cancelToken: new CancelToken(c => cancel = c),
method: 'POST',
data,
},
},
});
};
You can create such a small helper wrapper and use it anywhere where you need to cancel previous request:
// ../services.js
function createApi(baseURL) {
const axiosInst = axios.create({
baseURL,
});
axiosInst.defaults.headers.common['Content-Type'] = 'application/json';
return (type = 'get') => {
let call = null;
return (url, data, config) => {
if (call) {
call.cancel('Only one request allowed!');
}
call = axios.CancelToken.source();
const extConf = {
cancelToken: call.token,
...config,
};
switch (type) {
case 'request':
return axiosInst[type](extConf);
case 'get':
case 'delete':
case 'head':
return axiosInst[type](url, extConf);
default:
return axiosInst[type](url, data, extConf);
}
};
};
}
export const baseApi = createApi('http://localhost:5000/api/')
And then use it anywhere like this:
import baseApi from '../servises.js';
const fetchItemsService = baseApi('post');
//...
componentDidMount() {
fetchItemsService('/items').then(() => {});
}
//...
You wanna use something like RxJS for this, then you can delay the input if the users search, before the requests are made.
use faxios
// building..
let req = faxios()
.baseURL("http://jsonplaceholder.typicode.com")
.url("posts", 1, "comments")
// fetching..
req // =>
.FETCH // => Promise
.then(res => {})
.catch(err => {});
// canceling...
req.cancel();
Based on your code, you can cancel token using axios with this
export function autocompleteSearchTest(value){
const cancelToken = axios.CancelToken.source()// 1st
return axios.get(`https://jqueryui.com/resources/demos/autocomplete/search.php`,{
params: {
q: value
}
})
.then(response => {
return response.data.response;
})
.catch(error => {
if (axios.isCancel(e)) return //3rd
const result = error.response;
return Promise.reject(result);
});
return () =>{ // 2nd
cancelToken.cancel();
}
}