I'm trying to use axios and axios-mock-adapter in one place to aggregate more mocks and then export the axios instance to use it wherever I want:
axios.js
import axios from 'axios';
let api = axios.create({
baseURL: 'http://localhost:3500',
});
export default api
Login.js
import api from '../../../api/axios';
import MockAdapter from 'axios-mock-adapter';
let mock = new MockAdapter(api);
const LOGIN_URL = '/users';
const onSubmit = async data => {
console.log(data);
const user = data.loginEmail;
const pwd = data.password
const response = await mock.onPost(LOGIN_URL,
JSON.stringify({ user, pwd }),
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true
}
);
console.log(response);
}
But I get this response:
{reply: ƒ, replyOnce: ƒ, passThrough: ƒ, abortRequest:ƒ,abortRequestOnce: ƒ, …}abortRequest: ƒ ()abortRequestOnce: ƒ()networkError: ƒ ()networkErrorOnce: ƒ ()passThrough: ƒ passThrough()reply: ƒ reply(code, response, headers)replyOnce: ƒ replyOnce(code, response, headers)timeout: ƒ ()timeoutOnce: ƒ ([[Prototype]]: Object
Can anyone help me? What did I miss?
It sends status 200 but it doesn't send any request to the server and it's like not connecting to the server
It looks like you're trying to mock a GET request to the '/users' endpoint with the axios-mock-adapter, but in your onSubmit function you are making a POST request to the same endpoint. That's why you're getting a 404 error.
You can either change the mock.onGet to mock.onPost or change the onSubmit function to make a GET request.
Also, you should check the baseURL that you are using in the axios.create() method in axios.js and make sure it's the correct one.
Also, check if you have setup your server properly and it's running on the correct port.
If you continue to have issues you might want to check the browser's devtools network tab and check the request being made and the corresponding response.
axios.js
import axios from 'axios';
let api = axios.create({
baseURL: 'http://localhost:3500',
});
export default api
The solution was about to change the Login.js
// ** API
import api from '#api';
const LOGIN_URL = '/auth'
And onSubmit Event
// !! On Submit Event
const onSubmit = async data => {
const user = data.username;
const pass = data.password;
try {
const response = await api.post(LOGIN_URL,
JSON.stringify({ user, pass }),
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true
}
);
.
.
.
}
Related
I am calling an API defined using RTK Query, within a React Native + Redux Toolkit + Expo app. This is secured with an authentication / authorization system in place i.e. access token (short expiration) and refresh token (longer expiration).
I would like to avoid checking any access token expiration claim (I've seen people suggesting to use a Redux middleware). Rather, if possible, I'd like to trigger the access token renewal when the API being requested returns a 403 response code, i.e. when the access token is expired.
This is the code calling the API:
const SearchResults = () => {
// get the SearchForm fields and pass them as the request body
const { fields, updateField } = useUpdateFields();
// query the RTKQ service
const { data, isLoading, isSuccess, isError, error } =
useGetDataQuery(fields);
return ( ... )
the RTK Query API is defined as follows:
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import * as SecureStore from "expo-secure-store";
import { baseUrl } from "~/env";
export const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({
baseUrl: baseUrl,
prepareHeaders: async (headers, { getState }) => {
// retrieve the access_token from the Expo SecureStore
const access_token = await SecureStore.getItemAsync("access_token");
if (access_token) {
headers.set("Authorization", `Bearer ${access_token}`);
headers.set("Content-Type", "application/json");
}
return headers;
},
}),
endpoints: (builder) => ({
getData: builder.query({
// body holds the fields passed during the call
query: (body) => {
return {
url: "/data",
method: "POST",
body: body,
};
},
}),
}),
});
export const { useGetDataQuery } = api;
I understand that when the API returns isError = true and error = something 403 I need to renew the access token within the Expo SecureStore (and there's a function already in place for that). However I have no idea about how can I query the RTKQ API again, on the fly, when it returns a 403 response code, and virtually going unnoticed by the user.
Can someone please point me in the right direction?
I got the hang of it, massive thanks to #phry! I don't know how I could have missed this example from RTKQ docs but I'm a n00b for a reason after all.
This being said, here's how to refactor the RTKQ api to renew the access token on the fly, in case some other react native beginner ever has this problem. Hopefully this is a reasonable way of doing this
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
import * as SecureStore from "expo-secure-store";
import { baseUrl } from "~/env";
import { renewAccessToken } from "~/utils/auth";
// fetchBaseQuery logic is unchanged, moved out of createApi for readability
const baseQuery = fetchBaseQuery({
baseUrl: baseUrl,
prepareHeaders: async (headers, { getState }) => {
// retrieve the access_token from the Expo SecureStore
const access_token = await SecureStore.getItemAsync("access_token");
if (access_token) {
headers.set("Authorization", `Bearer ${access_token}`);
headers.set("Content-Type", "application/json");
}
return headers;
},
});
const baseQueryWithReauth = async (args, api) => {
let result = await baseQuery(args, api);
if (result.error) {
/* try to get a new token if the main query fails: renewAccessToken replaces
the access token in the SecureStore and returns a response code */
const refreshResult = await renewAccessToken();
if (refreshResult === 200) {
// then, retry the initial query on the fly
result = await baseQuery(args, api);
}
}
return result;
};
export const apiToQuery = createApi({
reducerPath: "apiToQuery",
baseQuery: baseQueryWithReauth,
endpoints: (builder) => ({
getData: builder.query({
// body holds the fields passed during the call
query: (body) => {
return {
url: "/data",
method: "POST",
body: body,
};
},
}),
}),
});
export const { useGetDataQuery } = apiToQuery;
I have following react code to make call to django rest framework API:
import Cookies from 'js-cookie';
import axios from "axios";
async downloadVideowiseCSV (fromDate, toDate) {
var url = '/stat/getStats/';
const axiosInstance = axios.create();
try {
const response = await axiosInstance.post(url,
{
data: {
'format': 'json'
},
header: {
'X-CSRFToken': Cookies.get('csrftoken')
}
}
)
//...
}
When this method gets called, the corresponding request fails with CSRF verification:
However when I check the payload of the request, I could see that X-CSRFTOken is indeed populated:
Then whats going wrong here?
The problem is in your axios request, it's not correct to send the header in the body of the HTTP request.
The following should be a valid axios request which separates the data from the options
ex:
const config = {
headers: { 'X-CSRFToken': Cookies.get('csrftoken') },
};
const data = {format: 'json'}
axios.post('http://YOUR_URL', data, config)
.then((response) => {
console.log(response.data);
});
I have the following basic fastify server:
import Fastify from 'fastify';
import expressPlugin from 'fastify-express';
import cors from 'cors';
async function build() {
const fastify = Fastify();
await fastify.register(expressPlugin);
fastify.use(cors());
fastify.post(
'/someurl',
async (request, reply) => {
console.log('PATATAPATATA');
void reply.send({message: "Hey, I'm stuck :D"})
}
);
return fastify;
}
And then I have the following test:
it.only('Should reply with a 200 status code when sending a string body', async () => {
// after this line, jest gives a timeout (5000 ms)
const response = await getResponse({
headers: {
Accept: 'application/json',
'Content-Type': 'text/plain',
'Content-Length': 1,
},
payload: 'a',
});
expect(response.statusCode).toEqual(200);
});
async function getResponse(options?: InjectOptions) {
const app = await build();
return app.inject({
method: 'POST',
url: `/someurl`,
...options,
});
}
The content-length header should not be needed, but I have added it after trying different things to fix it. If you delete it you get the timeout anyway. Any idea why this is happening? If I delete the content-type header then I get a 415 http error.
Side note: the code console.log('PATATAPATATA'); is never executed, so the problem has to be in some fastify internals or the light my request module.
Update It seems this only happens when I use payload in the inject options, like this:
async function getResponse(options?: InjectOptions) {
const app = await build();
return app.inject({
method: 'POST',
url: `/someurl`,
payload: {},
});
}
I have also discovered that:
Using payload: '' works.
Using payload: '{}' does not work. It does not give a timeout, but it does not run the post method either.
Using payload: '{}' and content-type set to application/json or text/plain, also gives the timeout.
It seems fastify-express it's not compatible with inject -> https://github.com/fastify/fastify/issues/3739
I have a Home.js component that signs the user up to the API and logs in and then gets the token received from the response authorization header and saves it in the state 'token' variable.
This token will be used in all other components to access the API when requests are made, so what is the best way of using this value for all other components?
Home.js:
const SIGNUP_URL = 'http://localhost:8080/users/signup';
const LOGIN_URL = 'http://localhost:8080/login';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated:false,
token: ''
};
}
componentDidMount() {
const payload = {
"username": "hikaru",
"password": "JohnSmith72-"
};
fetch(SIGNUP_URL, {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then((data) => {
console.log(data);
});
fetch(LOGIN_URL, {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(response =>
this.setState({token: response.headers.get("Authorization"), isAuthenticated:true})
)
}
For example the userList component which will fetch the user data from the API, but requires the API token stored in the Home component's token state variable to send the request successfully via the authorization header.
Thanks for any help
You can create a custom function called authenticated_request for example. This function could fetch your token from the CookieStorage in case of web or async storage in case of react-native or even if you have it in some state management library. Doesn't matter. Use this function instead of the fetch function and call fetch inside it. Think of it as a higher order function for your network requests.
const authenticated_request(url, config) {
fetch(url, {
...config,
headers: {
...config.headers,
Authorization: getToken()
}
});
}
You can also leverage the usage of something like axios and use request interceptors to intercept requests and responses. Injecting your token as needed.
You should be using AuthContext and localStorage to do this, save the token in the state or localStorage and make a config file which uses the same token when calling an api i have done it in axios. Axios has a concept of interceptors which allows us to attach token to our api calls, Im saving the token in the localStorage after a successfull login and then using the same token from localStorage to add to every call which needs a token, if the api doesnt need a token (some apis can be public) i can use axios directly, check out the below code:
import axios from 'axios';
let apiUrl = '';
let imageUrl = '';
if(process.env.NODE_ENV === 'production'){
apiUrl = `${process.env.REACT_APP_LIVE_URL_basePath}/web/v1/`;
}else{
apiUrl = `http://127.0.0.1:8000/web/v1/`;
}
const config = {
baseURL: apiUrl,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
"Access-Control-Allow-Origin": "https://www.*******.com",
'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE',
},
};
const authAxios = axios.create(config);
authAxios.interceptors.request.use(async function(config) {
config.headers.Authorization = localStorage.getItem('access_token') ?
`Bearer ${localStorage.getItem('access_token')}` :
``;
return config;
});
export { apiUrl, axios, authAxios };
now on making api call u can do something like below:
import { apiUrl, authAxios } from '../config/config'
export async function saveAssignment(data) {
try {
const res = await authAxios.post(apiUrl + 'assignment/save-assignment', data)
return res.data;
}
catch(e){
}
}
here pay attention im not using axios to make api call but using authAxios to make calls(which is exported from the config file) which will have token in the header.
(You can also use a third party library like Redux but the concept remains the same)
You need a centralized state that's what State Management libraries are for. You can use third-party libraries such as Redux, or simply use React's own context. You can search on google for state management in React and you'll find a lot of helpful recourses
You can place the token into a cookie if your app is SSR. To do that, you have to create the following functions:
export const eraseCookie = (name) => {
document.cookie = `${name}=; Max-Age=-99999999;`;
};
export const getCookie = (name) => {
const pairs = document.cookie.split(';');
const pair = pairs.find((cookie) => cookie.split('=')[0].trim() === name);
if (!pair) return '';
return pair.split('=')[1];
};
export const setCookie = (name, value, domain) => {
if (domain) {
document.cookie = `${name}=${value};path=/`;
} else {
document.cookie = `${name}=${value}`;
}
};
You can also place your token into the local storage:
Set into local storage via built-in function:
localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c');
Get the token via built-in function:
const token = localStorage.getItem('token');
I tried implement logout function with axios post in vue. I add the jwt to request headers, but it treated as params. In another function, i implement axios post method and it works. Anybody can help ? This is the code :
import axios from 'axios';
import store from '../store';
const API = 'http://192.168.100.184:5000/api/v1.0.0';
export class APIService{
constructor(){
}
login (userData) {
return axios.post(`${API}/auth/`, userData);
}
logout(){
const url = `${API}/auth/logout`;
const headers = {"api_token": store.state.jwt};
return axios.post(url,{headers:headers});
}
createUser(user){
const headers = {"api_token": store.state.jwt};
const url = `${API}/user/`;
return axios.post(url,user,{headers: headers});
}
}
when i see the network, in the request headers of createUsermethod there api_token field and it success. But in request headers of logout method there no api_token field, the api_token is found in the params and it looks like :
headers{…}
api_token : xxxxxxxx
the response say error with status code 400.
This:
return axios.post(url,{headers:headers});
should be this:
return axios.post(url, null, {headers: headers});
The second parameter must be the request body, the third parameter is for other options such as the headers.