Im very new with Axios, and im trying to do a POST request using Axios and my DB is not getting the data, it creates one entity and all the values are null except the primary key that is 0. Then, i get an error that i'm duplicating the primary key. I don't know if its not getting my JSON or if im doing something else wrong.
api.js
import axios from "axios";
const endpoints = {
development: 'http://localhost:8080/',
};
export const api = axios.create({
baseURL: endpoints['development'],
timeout: 20000,
headers: {"Content-type":"application/json" }
});
pacienteService.js
import { api } from './helpers/api.js';
const basePath = 'api';
let config = {
headers: {
'Content-Type': 'application/json',
}
}
function getAll() { return api.get(`${basePath}/pacientes`); }
function show(pacienteId) { return api.get(`${basePath}/?id=${pacienteId}`); }
function create(data) { return api.post(`${basePath}/pacientes`, data,config); }
const pacientesService = { getAll, show, create };
export default pacientesService;
Function that do the post when Submitting the form
handleSubmit(event){
const paciente = {rut:this.state.rut,
nombre:this.state.nombre ,
nacionalidad:this.state.nombre ,
sexo:this.state.nombre ,
fecha_na:this.state.nombre ,
domicilio:this.state.nombre ,
diagnostico:this.state.nombre ,
telefono:this.state.nombre ,
gravedad:this.state.recuperacion
}
pacientesService.create({paciente}).then(res => {
console.log(res);
console.log(res.data);
})
}
Pd: I have printed all the states to see if they're correct, and they are.
Fixed it passing data directly to pacientesService.create().
From pacientesService.create({paciente}), to
pacientesService.create({rut:this.state.rut,nombre:this.state.nombre,... })
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 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 want to declare 2 axios instance,so 1 is for API call using access_token,another 1 is using refresh_token.
So I have a code like this:
config.js
import axios from 'axios';
const axiosAccessClient =function () {
const defaultOptions = {
baseURL: 'my_url',
headers: {
'Content-Type': 'application/json',
}
};
let instance = axios.create(defaultOptions);
instance.interceptors.request.use(function (config) {
const token = localStorage.getItem('access_token');
config.headers.Authorization = token ? `Bearer ${token}` : '';
return config;
});
return instance;
};
const axiosRefreshClient =function () {
const defaultOptions = {
baseURL: 'my_url',
headers: {
'Content-Type': 'application/json',
}
};
let instance = axios.create(defaultOptions);
instance.interceptors.request.use(function (config) {
const token = localStorage.getItem('refresh_token');
config.headers.Authorization = token ? `Bearer ${token}` : '';
return config;
});
return instance;
};
export {
axiosAccessClient,
axiosRefreshClient
}
So in my Request.js I do something like this:
import {axiosAccessClient,axiosRefreshClient} from "./config";
static async access(url,body) {
return await axiosAccessClient.post(url, body)
.then(function (response) {
return response;
}).catch(function (error) {
throw error;
})
}
static async refresh(url,body){
return await axiosRefreshClient.post(url, body)
.then(function (response) {
return response;
}).catch(function (error) {
throw error;
})
}
but when I run the app,it crash at the point of access() in Request.js show this error:
_AxiosConfig__WEBPACK_IMPORTED_MODULE_2__.axiosAccessClient.post is not a function
But if I do the following:
export default axiosAccessClient() in config.js,
import axiosAccessClient from "./config" in Request.js
then the code work(which is weird),but by this I cant access axiosRefreshClient from refresh() in Request.js
Question:
Can somebody tell me why is this happen? I read all this example,but still cant figure it out:
this question
this question and so on
How can solve this?? Export multiple function from a single file
Your config.js exports functions; you need to rewrite it so it exports the result of the function calls instead, i.e. the instances.
// change name of functions
function generateAccessClient() {
..
}
// create instances
const axiosAccessClient = generateAccessClient();
const axiosRefreshClient = generateRefreshClient();
// export them
export {
axiosAccessClient,
axiosRefreshClient
}
This way you can import both. Having a default export is unrelated to your problem, you just accidentally solved it by adding the () at the end.
Another way is to do this:
const axiosAccessClient = (function () {
...
})();
Same for axiosRefreshClient.
Now your original code will work.
Your two functions (axiosAccessClient and the other) return an axios instance, but the function itself is not an axios instance (that's why you can't call .post() on it). You should create the two clients in the same module and save them as const variables, and then export the instances (instead of functions that create instances). It makes no sense to re-create the same instance every time you wish to make a request. The parameters of the instance do not change, so saving the instance is better.
I'm working on a React application that makes use of an imported object with a get request to an api and a post request to a related API.
When creating a new instance of my service in the frontend in React, I am able to successfully use the '.then' & '.catch' functions to access the returned data ONLY from the get request.
When using the post request from the same object, when trying to access the response object, I get a (paraphrased) '.then' is not a function on undefined.
Only when I explicitly write out the post request in my form submit function (without consuming a service) and handling the object there am I able to check the response and subsequently set the state.
What is the appropriate/best practice way for using axios in React and why am I not able to access the response object when I create a new instance of a service?? Much appreciated!
Service:
import axios from 'axios';
class ProductServices {
getAllProducts(){
return axios.get('https://somecustomAPIURL')
}
postProduct(somePathConfig){
axios.request({
url: 'https://somecustomAPIURL' + somePathConfig,
method: 'post',
headers: {'some-custom-header': process.env.REACT_APP_API_POST_KEY}
})
}
}
export default ProductServices;
React Code instantiating and consuming the service (note, that getAllProducts works just fine, but trying to consume a response object in postProduct returns an '.then' is undefined)
constructor(){
super();
this.state = {
products: [],
productID: null,
showModal: false
}
this.ProductServices = new ProductServices();
}
getAllProducts = () => {
this.ProductServices.getAllProducts()
.then((response) => {
let items = response.data.data.items;
this.setState({
products: items,
productID: items[0].id
});
return response;
})
.catch((error) => {
console.log('Error!', error);
return error;
})
}
handleFormSubmit = (e) => {
e.preventDefault();
let productID = this.state.productID;
this.ProductServices.postProduct(productID)
.then((response) => {
this.setState({showModal: true}, () => console.log('Success!'));
return response;
})
.catch((err) => {
console.log('Error!', err);
})
}
You missed return before axios.request.
import axios from 'axios';
class ProductServices {
...
postProduct(somePathConfig){
return axios.request({
url: 'https://somecustomAPIURL' + somePathConfig,
method: 'post',
headers: {'some-custom-header': process.env.REACT_APP_API_POST_KEY}
})
}
...
Also, instead of axios.request, you can use axios.post like axios.get
return axios.post(url, body, { headers });
return axios.get(url, { headers });
return axios.put(url, body, { headers });
return axios.delete(url, { headers });
return axios.request(axiosConfigOptions);
I am trying to alter some data inside of my database, however I am getting the error once my api request is called:
Uncaught (in promise) SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
Along with the corresponding network error of 404. I am not quite sure why it isn't recognnizing my api call, here is the initial fetch call:
import fetch from '../../../../core/fetch/fetch.server';
import history from '../../../../core/history';
export default function checkIn(orderId) {
debugger;
return async (dispatch, getState) => {
// dispatch({ type: BOXOFFICE_CHECKING_IN });
const response = await fetch(`/api/orders/${orderId}/checkIn`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
checkedIn: true,
}),
}
);
if (response.status === 200) {
// dispatch({ type: BOOKING_CHECKED_IN });
} else {
const errorResponse = await response.json();
if (errorResponse.code === 'card_error') {
// dispatch({ type: BOXOFFICE_CHECKED_IN_ERROR });
}
}
} catch (err) {
throw err;
}
};
}
And my api file (removed everything that isn't relevant):
import { Router } from 'express';
import checkIn from '../handlers/api/orders/checkInCustomer';
export default (resources) => {
const router = new Router();
router.post('/orders/:orderId/checkIn', checkIn(resources));
return router;
};
Which ultimately is meant to call my js file that changes the data databse entry:
import { defaultTo } from 'lodash';
import guuid from '../../../../../../core/guuid';
import authenticateAdmin from '../authenticateAdmin';
import order from '../../../../client/reducers/ui/modals/order';
export default ({ knex }) =>
authenticateAdmin(knex)(async (req, res) => {
try {
console.log('checkinCustomer');
const { orderId } = req.params;
const { isCheckedIn } = req.body;
console.log(orderId);
console.log(isCheckedIn);
await knex('orders').where('is_checked_in', '=', orderId).update({ is_checked_in: isCheckedIn }).where({ id: orderId });
res.status(201).end();
} catch (err) {
console.error(err.stack || err);
}
});
Can anyone spot something that is fundamentally wrong in my code, I can assure you the file paths are all correct and all functions are visible to each other, maybe it's the way I have parsed my data?
EDIT
I thought it maybe of use to include that I am also getting a CORS error:
Access to XMLHttpRequest at 'http://localhost:3000/api/orders/c7216fc0-1197-4cb6-99d4-15760f00b6e7/checkIn' from origin 'my site name' has been blocked by CORS policy:
FURTHER EDIT
I have managed to remove the original JSON error, however I am still getting the 404 network error as well as the original CORS error... In addition to this, if I change the last section of the fetch from checkIn to say cancel which is a fully working api call, the same errors persist.
You should not use JSON.stringify() in passing data to your body.
You should pass json format in your body as you are using application/json.
I have a solution! Turns out my import fetch from '../../../../core/fetch/fetch.server';
in the initial file was wrong and should have been import fetch from '../../../../core/fetch/fetch';!