React - fetch-intercept modify all headers - reactjs

What is the proper way to intercept all requests and add headers using react with fetch-intercept? I have a config file that contains the call to fetchIntercept.register(). I have separate files for component api calls that import the fetchIntercept config file. I added the unregister() call after the api is called but the headers are not being added.
api/config.js
import fetchIntercept from 'fetch-intercept';
const unregister = fetchIntercept.register({
request: function (url, config) {
// Modify the url or config here
const withDefaults = Object.assign({}, config);
withDefaults.headers = defaults.headers || new Headers({
'AUTHORIZATION': `Bearer ${JSON.parse(sessionStorage.credentials).authToken}`
});
return [url, withDefaults];
},
requestError: function (error) {
// Called when an error occured during another 'request' interceptor call
return Promise.reject(error);
},
response: function (response) {
// Modify the reponse object
return response;
},
responseError: function (error) {
// Handle an fetch error
return Promise.reject(error);
}
});
export default unregister;
api/packageApi.js
import unregister from '../api/config';
class PackageApi {
static getAllPackages() {
const request = new Request('/get/my/packages', {
method: 'GET'
});
return fetch(request).then(response => {
return response.json();
}).catch(error => {
return error;
});
}
}
unregister();
export default PackageApi;

I adding the working example for the fetch-intercept in separate file, its works for me perfectly.
https://stackblitz.com/edit/react-fetch-intercept-bi55pf?file=src/App.js
App.js
import React from 'react';
import './style.css';
import { AuthInterceptor } from './AuthInterceptor';
export default class App extends React.Component {
componentDidMount() {
AuthInterceptor();
fetch('http://google.com', {
headers: {
'Content-type': 'application/json',
},
});
}
render() {
return (
<div>
<h1>Hello StackBlitz!</h1>
<p>Start editing to see some magic happen :)</p>
</div>
);
}
}
AuthInterceptor.js
import fetchIntercept from 'fetch-intercept';
export const AuthInterceptor = () => {
fetchIntercept.register({
request: function (url, config) {
// Modify the url or config here
config.headers.name = 'Aravindh';
console.log(config);
return [url, config];
},
requestError: function (error) {
// Called when an error occured during another 'request' interceptor call
return Promise.reject(error);
},
response: function (response) {
// Modify the reponse object
return response;
},
responseError: function (error) {
// Handle an fetch error
return Promise.reject(error);
},
});
};
You can see the updated header value in the console.
Thanks

The use of unregister seems incorrect. You have unregistered before any calls are made.

This is pretty straight forward using axios instead of fetch.

Related

React MERN App - Not passing ID to fetch api

I am creating a react app with full crud functionality. It allows users to create job postings and i wanted to click on a specific job to view more details.
I am having trouble as everytime i try to click a "job" it says that ID is undefined specifically:
show function called with id: undefined
SyntaxError: Unexpected end of JSON input
My app currently displays the list of all jobs and creates.
I already confirmed the following:
Made sure the backend server is running and listening on port 3001.
Verified that the endpoint i am trying to fetch actually exists. Tried on postman
Made sure that my frontend code is using the correct URL to make requests to the backend.
I am using hooks and functions.
DetailsPage.js
import styles from './DetailsPage.module.css';
import React, { useState, useEffect } from 'react';
import jobsService from '../../utils/jobsService';
export default function DetailPage(props) {
const [job, setJob] = useState({});
const [isLoading, setIsLoading] = useState(true);
const { id } = props.match?.params || {};
useEffect(() => {
const fetchData = async () => {
try {
const { data } = await jobsService.show(id);
setJob(data);
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
fetchData();
}, [id]);
return (
<>
{isLoading ? (
<div>Loading...</div>
) : (
<div className={styles.list}>
<div className={styles.Grid}>
<h3>{job.title}</h3>
<p>{job.description}</p>
</div>
</div>
)}
</>
);
}
jobsService.js
async function getAll(){
const response = await fetch('http://localhost:3001/api/jobs')
const data = await response.json()
return data
}
async function create(item) {
try {
const response = await fetch('http://localhost:3001/api/jobs/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(item)
});
return await response.json();
} catch (error) {
console.error(error);
}
}
async function show(id) {
console.log("show function called with id:", id);
return fetch(`http://localhost:3001/api/jobs/${id}`, {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
})
.then(res => res.json());
}
export default {
getAll,
create,
show,
}
destruct id form empty object ?
instead this
const { id } = props.match?.params || {};
test this
const { id } = props.match?.params || {id: 0};
or use ternary operator in function calling
or
async function show(id = 0) {
console.log("show function called with id:", id);
return fetch(`http://localhost:3001/api/jobs/${id}`, {
method: 'GET',
headers: {
'Content-type': 'application/json',
},
})
.then(res => res.json());
}

ReactJs how to add interceptor in axios

I've been working on this for hours, and I have no idea where did it go wrong.
I want to have an axios interceptor for my ReactJs
this is my interceptor axiosHandler.js
import axios from "axios";
const axiosHandler = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
headers: {
Accept: "application/json",
},
});
axiosHandler.interceptors.request.use(
(config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = "Bearer " + token;
}
return config;
},
(error) => {
Promise.reject(error);
}
);
//axiosHandler.interceptors.response
export default axiosHandler;
And here is how I use the handler in my other component
import axiosHandler from "../services/axiosHandler";
const getData = async () => {
await axiosHandler
.get(`/path`)
.then((response) => {
//do something
})
};
And I get an error of below
services_axiosHandler__WEBPACK_IMPORTED_MODULE_0_.get is not a function
I've read many other solutions, but I can't find the difference as how it leads to the error of mine.
Where do I put it wrong?
Thank you
inside axios.index
import axios from "axios";
import { API_URL } from "../config/config";
const axiosHttp = axios.create({
baseURL: `${API_URL}`,
});
axiosHttp.interceptors.request.use(
(config) => {
const token = "Your Token here"
return {
...config,
headers: {
...(token !== null && { Authorization: `${token}` }),
...config.headers,
},
};
},
(error) => {
return Promise.reject(error);
}
);
axiosHttp.interceptors.response.use(
(response) => {
//const url = response.config.url;
//setLocalStorageToken(token);
return response;
},
(error) => {
if (error.response.status === 401) {
//(`unauthorized :)`);
//localStorage.removeItem("persist:root");
//removeLocalStorageToken
//window.location.href = "/login";
}
return Promise.reject(error);
}
);
export default axiosHttp;
Then inside your API function use it like below
import axiosHttp from "./utils/axios";
const getData = async ()=>{
try{
const response = await axiosHttp.get('/path')
return resposne;
}
catch(error){
//handle error here...
}
}
Last but not least, you shouldn't use await when using callback (then/catch)

Method returns early before Axios returns fetched data in reactjs

I'm fetching data from server using axios.post method but it returns early .I have used async and await but data is not updated
apiService.js
export const getAppAuthUser = async (page, authorizedType) => {
await axios.post(APIURL.apiURL, JSON.stringify({
page: page,
authorized: authorizedType
}), {
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
console.log(res);
return res.data;
}).catch(err => {
console.log(err);
});
}
component.js
import * as Users from '../api/apiService';
class User extends Component {
sortedInfo = {};
componentDidMount() {
this.data=Users.getAppAuthUser(1,true);
console.log(this.data);
}
}
when I console it return PromiseĀ {}
Please help
That's what async functions do: they return promises. async/await exists to make the syntax for working with promises easier, but it does not change the fact that promises are involved. To get the value inside the promise, you'll need to use the promise's .then method, or put your code in an async function and await its result.
You also have a problem in your getAppAuthUser function that you're not returning anything, and so the promise will resolve to undefined. It's a lot easier to make this kind of problem when you're mixing the .then style with the async/await style. I'd strongly recommend just picking one style and using that consistently.
export const getAppAuthUser = async (page, authorizedType) => {
try {
const res = await axios.post(APIURL.apiURL, JSON.stringify({
page: page,
authorized: authorizedType
}), {
headers: {
'Content-Type': 'application/json'
}
})
console.log(res);
return res.data;
} catch (err) {
console.log(err);
}
}
import * as Users from '../api/apiService';
class User extends Component {
sortedInfo = {};
async componentDidMount() {
this.data = await Users.getAppAuthUser(1,true);
console.log(this.data);
}
}
JavaScript is asynchronous, we can't use like this this.data=Users.getAppAuthUser(1,true) if you wish to use same like this then use async-await like this async componentDidMount() {
this.data= await Users.getAppAuthUser(1,true);
console.log(this.data);
}
Or you can use promise like this Users.getAppAuthUser(1,true).then(data=>{
console.log(data);
})

React - Login success redirect and auth headers

I have a login function that should set the user's credentials in sessionStorage upon successful login and then redirect to a new route, calling another api with the sessionStorage auth token added to the headers. For some reason the initial api call after successful login is failing because the auth token isn't added to the request headers. However, if I reload the page after the redirect the request header is added, resulting in a successful response. What is the correct way to save credentials to sessionStorage and configure all headers for subsequent requests.
components/Login.jsx
login(event) {
event.preventDefault();
this.props.dispatch(loginUser(this.state.creds));
}
actions/loginActions.js
export function loginUser(user) {
return function(dispatch) {
return LoginApi.login(user).then(creds => {
dispatch(loginUserSuccess(creds));
}).catch(error => {
throw(error);
});
};
}
export function loginUserSuccess(creds) {
sessionStorage.setItem('credentials', JSON.stringify(creds.data));
hashHistory.push('/packages');
return {
type: types.LOGIN_USER_SUCCESS,
state: creds.data
}
}
api/config.js
import axios from 'axios';
sessionStorage.credentials ? axios.defaults.headers.common['Authorization'] = 'Bearer ' + JSON.parse(sessionStorage.credentials).authToken : undefined;
api/packageApi.js
import './config';
import axios from 'axios';
class PackageApi {
static getAllPackages() {
return axios.get('/get/my/packages')
.then(function (response) {
console.log(response);
return response;
})
.catch(function (error) {
console.log(error);
});
}
}
Edit
// same behavior
sessionStorage.getItem('credentials') ? axios.defaults.headers.common['Authorization'] = 'Bearer ' + JSON.parse(sessionStorage.credentials).authToken : undefined;
// Fixed - api/config.js
import axios from 'axios';
const axiosInstance = axios.create();
axiosInstance.interceptors.request.use(
config => {
sessionStorage.getItem('credentials') ? config.headers['Authorization'] = 'Bearer ' + JSON.parse(sessionStorage.credentials).authToken : undefined;
return config;
},
error => Promise.reject(error)
);
export default axiosInstance;
// Fixed - api/packageApi.js
import axiosInstance from './config';
class PackageApi {
static getAllPackages() {
return axiosInstance.get('/get/my/packages')
.then(function (response) {
console.log(response);
return response;
})
.catch(function (error) {
console.log(error);
});
}
}
export default PackageApi;
Your config file is parsed and evaluated as soon as the browser loads it. At that time there is no token in your sessionStorage. What you need to do is to write a function which is only defined at parse time but then you call it at runtime to get the token:
class PackageApi {
static getAllPackages() {
const token = window.sessionStorage.getItem('credentials')
return axios.get('/get/my/packages', {
headers: {'Authorization': `Bearer ${ token }`}
})
.then(function (response) {
console.log(response);
return response;
})
.catch(function (error) {
console.log(error);
});
}
}
Here I did not define a function because sessionStorage.getItem is already a function but it doesn't stop you to write stuff like this:
const getFromStorage = key => {
const value = window.sessionStorage.getItem(key);
return JSON.parse(value);
}
axios.get('/get/my/packages', {
headers: {'Authorization': `Bearer ${ getFromStorage('credentials') }`}
})

Put error response interceptor on redux-axios-middleware

I have a problem with https://github.com/svrcekmichal/redux-axios-middleware.
I want to set the interceptor response (error). But can't successfully set it up.
Here is my code:
function interceptorResponse({ dispatch, getState, getAction }, response) {
console.log(response);
}
export const client = axios.create({
baseURL: API_URL,
headers: {
Accept: 'application/json',
},
});
export const clientOptions = {
interceptors: {
request: [interceptorRequest],
response: [interceptorResponse],
},
};
the console.log(response) only respond if the response is 200. How can I set it to accept an error response?
I've tried set it like this
function interceptorResponse({ dispatch, getState, getAction }) {
return response => response.data, (error) => {
const meta = error.response.data.meta;
const { code, status } = meta;
console.log(meta);
};
}
but still never show anything.
Any soluion?
Here is an example usage with ES6 :
import axios from 'axios'
import axiosMiddleware from 'redux-axios-middleware'
const options = {
// not required, but use-full configuration option
returnRejectedPromiseOnError: true,
interceptors: {
request: [
({ getState, dispatch }, config) => {
// Request interception
return config
}
],
response: [
{
success: ({ dispatch }, response) => {
// Response interception
return response
},
error: ({ dispatch }, error) => {
// Response Error Interception
return Promise.reject(error)
}
}
]
}
}
export default axiosMiddleware(axios, options)
Note that the created middleware should be passed to createStore()

Resources