infinitered / apisauce Goto Github PK
View Code? Open in Web Editor NEWAxios + standardized errors + request/response transforms.
License: MIT License
Axios + standardized errors + request/response transforms.
License: MIT License
Good day,
I'm not getting a timeout on an IP address that does not exist. Rather the app is waiting for a NETWORK_ERROR which can take a lot longer (several minutes in some circumstances). My setup below:
const api = apisauce.create({
// base URL is read from the "constructor"
baseURL,
// here are some default headers
headers: {
'Cache-Control': 'no-cache'
},
// 10 second timeout...
timeout: 2000
})
const testURL = () => api.get('/');
const result = yield call(api.testURL);
OR
api.testURL().then(
(result) => console.warn(result),
(err) => console.error(err)
)
Regards
I have multiple places for api.create and I don't want to set headers with tokens every time, seems like no solution exist
For my app i want to implement an asyncStorage cache. Right now i temporary save your get method, replace it with my own and call the saved get method inside my own.
How about you add something like the request transformer just for pre checking a request? This is just an idea. If you don't like it just close the issue :)
this.request = create({
baseURL: url || 'http://localhost',
headers: {
'Accept' : 'application/json',
'Content-Type': 'application/json'
}
});
const originalGet = this.request.get;
this.request.get = (url, params = {}) => {
return new Promise(resolve=> {
const key = buildURL(url, params);
AsyncStorage.getItem(key, (error, value)=> {
if (!error && value) {
const data = JSON.parse(value);
if (moment(data.expires) >= moment()) {
console.log(`[CACHED_GET] ${url} ${JSON.stringify(params)}`);
return resolve({ok: true, fromCache: true, data: data.data});
}
}
return resolve(originalGet(url, params));
});
})
};
this.request.addResponseTransform((response)=> {
if (response.ok) {
const url = response.config.url.replace(response.config.baseURL, '');
var params = response.config.params;
delete params.api_key;
const key = '/' + buildURL(url, params);
const data = {
expires: moment().add(7, 'days').toDate().getTime(),
data : response.data
};
console.log(`[SAVE_GET] ${url} ${JSON.stringify(params)}`);
AsyncStorage.setItem(key, JSON.stringify(data));
}
});
Apisauce always resolves promise even if a response status is 4xx. Apisauce should reject a promise if a status is not in range 2xx as axios do.
So, I'm not sure you are gonna be able to reproduce it. But I think its worth I mention this bug.
react native v. 0.33.1
axios v. 0.8.0
In a simple POST, Im getting back a ok
with False, in a 204 (no content) response.
Now the tricky part:
It only happen on Android in production.
It didn't happen on iOS (prod or dev), neither on Android in development mode (emulator and device).
During the local tests, I was pointing to the production environment.
Any ideia what might cause that?
I tried running npm test
using Node 6/NPM 3.8.9 and Node 4/ NPM 2.15.1 with no luck. Both complained about the import
statements so I guess they were not being transpiled by babel.
When running Node 4 I also had to install babel-core
and babel-plugin-transform-runtime
because these were not part of the deps of the project.
We got some weird api which requires the body of the POST request to be a string like this
username=xyz&password=abc
And the content type has to be
I tried the following
const body = {username, password};
// and
const body = `username=xyz&password=abc`;
const options = {
headers: {
'Accept' : 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
}
};
return this.api.post('/tokenAuthentication/getToken.json', body, options);
In both cases it didn't worked. How do i send a request with just a string?
And I think the bug come from regenerator-runtime
dependency used by the package to support async/await.
I'm not sure which is the best solution:
symbol.toStringTag
thing?"apisauce": "0.6.0"
api.post('/oauth/token', data)
.then((res) => console.log(res));
the below response is logged in "then"
{
"duration":190,
"problem":"CLIENT_ERROR",
"ok":false,
"status":400,
"headers":{
"content-type":"application/json;charset=UTF-8",
"access-control-allow-origin":"",
"pragma":"no-cache, no-cache",
"x-xss-protection":"1; mode=block",
"transfer-encoding":"Identity",
"access-control-max-age":"3600",
"expires":"0",
"cache-control":"no-cache, no-store, max-age=0, must-revalidate, no-store",
"date":"Mon, 23 Jan 2017 23:37:54 GMT",
"access-control-allow-credentials":"true",
"x-content-type-options":"nosniff",
"vary":"Accept-Encoding",
"x-frame-options":"DENY"
},
"config":{
"transformRequest":{
},
"transformResponse":{
},
"headers":{
"Accept":"application/json",
"Content-Type":"application/json",
},
"timeout":0,
"xsrfCookieName":"XSRF-TOKEN",
"xsrfHeaderName":"X-XSRF-TOKEN",
"maxContentLength":-1,
"baseURL":"http://114.119.116.135:8000/fe-oauth",
"method":"post",
"url":"http://localhost:8000/oauth/token",
"data":"{\"grant_type\":\"password\"}"
},
"data":{
"error":"invalid_request",
"error_description":"Missing grant type"
}
}
I'm not terribly sure if I'm missing something basic. This could just be my ignorance.
I want to do something special when login
api is called. It should do something with response, then return the promise. But it appears to swallow response
Ergo:
api
.post('/repos/skellock/apisauce/commits')
.then((response) => response.data[0].commit.message)
.then((response) => window.alert(response.data.taco))
second promise is like "WUT? WUTs this response thing yo?"
This is critical, because I want a certain API function to update headers, but not to keep response away from the following paths the promise might be handed. I hope I made this clear... I need coffee ☕
Is it possible to implement a middleware feature like the addMonitor method? Like if i want to handle every response in a certain way. Now i have to add to every request a .then
I'm using apisauce in React Native app to connect to an external api that is not on my own.
I'm working with a dev environment that i cannot touch and i would like to ignore the cert error to test the connection.
There is a way to ignore invalid certificates?
Hi, I'm seeing the following error when I make a request to my apisauce API, which is structured as a singleton. Any idea why this is occurring? It's happening on my 4th API call.
I don't seem to find a way to upload files with apisauce.
Thanks
Kunal
Hi, what would be the license for this project? MIT?
Thanks!
So, turns out, this is not straight forward.
axios
uses XmlHttpRequest
which uses JavaScriptCore
which is different for each platform.
I tested on a few devices: iOS 8.1, 8.2, 8.3, 9.0, 9.1, 9.3 and Android 16, 22.
I tested a few scenarios (bad dns, ssh port, no port, no server, fast time, slow time).
It's all over the map.
XmlHttpRequest Responses
--- DNS ---
android 16 - Unable to resolve host "kelsdfjhklasjf": No address associated with hostname
ios 8.3 - A server with the specified hostname could not be found.
iOS 8.2 - The operation couldn’t be completed. (NSURLErrorDomain error -1003.)
--- Couldn’t connect ---
iOS 9 - Could not connect to the server.
iOS 8.2 - The operation couldn’t be completed. (NSURLErrorDomain error -1004.)
android 22 - failed to connect (SECRET DATA GOES HERE): connect failed: ECONNREFUSED (Connection refused)'
--- Time out ---
iOS 8.1 The operation couldn’t be completed. (NSURLErrorDomain error -1001.)
iOS 8.2 The operation couldn’t be completed. (NSURLErrorDomain error -1001.)
iOS 8.3 The request timed out.
android 22 - SSL handshake timed out
android 22 - timeout
android 16 - Read timed out
--- Bad Port ---
iOS 8.1 - The operation couldn’t be completed. (NSURLErrorDomain error -1005.)
iOS 8.2 - The operation couldn’t be completed. (NSURLErrorDomain error -1005.)
iOS 8.3 - The network connection was lost.
iOS 9.3 - The network connection was lost.
android 22 - Unexpected status line: SSH-2.0-OpenSSH_6.0p1 Debian-3ubuntu1.2
I'm thinking there should be a ghetto
mode detection with all of this stuff in it.
Off by default? I'm not sure.
This example for AsyncRequestTransform seems to be wrong.
Perhaps we should fix it
api.addAsyncRequestTransform(request => async () => {
await AsyncStorage.load('something')
request.
}})
I was following the infiniteRed/ignite way to making api calls by using redux / redux-saga
but I got wired "NETWORK_ERROR" when I was trying to make api call to my sever on AWS even on localhost.
but under the same network condition, I replaced the baseURL to the github demo one, which works just fine (returning some 'CLIENT_ERROR' since I was calling the non-exist endpoint)
this is what I logged response:
Object {duration: 246, problem: "NETWORK_ERROR", ok: false, status: null, headers: null…}
config
:
null
data
:
null
duration
:
246
headers
:
null
ok
:
false
problem
:
"NETWORK_ERROR"
status
:
null
does someone have any insight about this? and I noticed that '[NEW] Network errors and timeouts are now detected on React Native - @skellock' on 0.2.0 release, then I upgrade apisuace to 0.3.0, but the error happens - -
Supposedly awaiting features in 0.25, I know you were planning on doing timout in constructor.
It appears they've added an onTimeout. Would be a good add-on to assure this works with API sauce and associated docs.
Something like this:
this.api = create({
baseURL:'localhost:8999'
})
this.api.setBaseURL('somefancyapi.com');
Docs clearly say PUT/POST/PATCH gets addRequestTransform
but I could sure use it for a GET. Any reason it's not supported?
If philosophically, then let's discuss.
If just not done yet, then I'd love to add it!
Hi, I've been struggling with this for a while and can't seem to find a solution.
I am sending this request:
//Service
const postPasswordLogin = function (email, password, deviceId, deviceType) {
var data = {email, password, deviceId, deviceType};
return api.post('/sign-in/password', data);
};
//Saga
const response = yield call(api.postPasswordLogin, email, password, 'alibaba', 'android');
console.log(response);
but I keep getting a response like this:
Object
config:Object
data:"GET"
duration:1177
headers:Object
ok:true
problem:null
status:200
if I test the API request with postman using the same JSON object as I see in config and with application/json headers, server responds just fine. With apisause it seems like it's attempting to send some kind of error (because response header is text/html instead of JSON), but such error won't have a 200 status code and definitely will have some response..
any ideas?
You can set the default headers when you call create()
. Yay.
But if you want to change that after that call, you have to reach into the axiosInstance.defaults.headers
object to mutate it.
Let's provide a nicer way for this.
Title says it all.
When making a post request with the body being a FormData object, there doesn't seem to be a Content-Type set , nor can I manually set that header. It seems to be overwritten to nothing.
The QSV parsing is taking objects that are null
and transforming them into the string "null". That's not cool.
Hello
We have recently investigating React Native for cross-platform mobile development and been using this awesome library. I am in the point of investigating unit testing and trying to mock the async of apisauce in my Redux's Actions.
I followed the recommended approach in Redux Writing Test documentation here with no success. My JS is level is pretty beginner and was wondering if anyone using this library can share what you did for testing/mocking Actions in Redux.
a server i'm communicating with requires a signature to be generated from a key-pair and appended as a header for every request. i decided to try and use a request transformer to keep this signature generating code in one place, instead of passing a signature value in with every api.whatever('/where/ever')
call.
the module i'm using for generating these signatures works exclusively with promises. as a result of this constraint, i have tried these two approaches:
// approach one
api.addRequestTransform(async function(request) {
var now = crypto.createHash('sha256').update(Date.now()).digest()
request.headers['x-message'] = now.toString('hex')
request.headers['x-signature'] = await sign(Buffer.from(privatekey, 'hex'), now)
})
// approach two
api.addRequestTransform(function(request) {
var now = crypto.createHash('sha256').update(Date.now()).digest()
request.headers['x-message'] = now.toString('hex')
sign(Buffer.from(privatekey, 'hex'), now).then(function(sig) {
request.headers['x-signature'] = sig.toString('hex')
}).catch(function(err) {
// abort the request, somehow
})
})
when the request is fired, the value that i attempted to set in the transformer is not present. after reading the request transformer part of the source code, it appears to be attributed to ramda foreach not waiting for my promise to resolve.
so, is it okay to be using asynchronous functions within a transformer? (i'm presuming the answer is no 😄 ) or should i try and find a way to generate these signatures synchronously?
edit:
is it possible to support asynchronous transformers? if so, any pointers for implementing? i'd be happy to have a crack at it!
after i installed the apisauce i got error:
transformed 416/417 (100%)(node:2216) UnhandledPromiseRejectionWarning: Unhandle
d promise rejection (rejection id: 290): UnableToResolveError: Unable to resolve
module http from F:\ReactNative\test\node_modules\axios\lib\adapters\http.js: M
odule does not exist in the module map or in these directories:
F:\ReactNative\test\node_modules
i work with windows 7, npm and android
the files exists in the directory...
Currently we pass through the timeouts but we don't set them.
@skellock
How can I set the mode to no-cors?
In fetch I would do it this way:
fetch('https://github.com/', {
method: 'get',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
mode: 'no-cors',
}))
How can I achieve similar thing with axios/apisauce
Thank you in advance.
For a post request it would be a great feature to be able to see the upload progress. Attaching some kind of handler to a request seems like the right way to do it: (progressValue) => do stuff
api.post('url', data, (progressValue) => do stuff)
From the Axios docs:
//
onUploadProgress
allows handling of progress events for uploads
onUploadProgress: function (progressEvent) { // Do whatever you want with the native progress event }
When i try to fetch anything from my api i get the following error in pidcat: [TypeError: undefined is not an object (evaluating 'error.response.status')]
. In your source is only one place with error.response.status
var getProblemFromError = function getProblemFromError(error) {
// first check if the error message is Network Error (set by axios at 0.12) on platforms other than NodeJS.
if (error.message === 'Network Error') return NETWORK_ERROR;
// then check the specific error code
return R.cond([
// if we don't have an error code, we have a response status
[R.isNil, function () {
return getProblemFromStatus(error.response.status);
}], [R.contains(R.__, TIMEOUT_ERROR_CODES), R.always(TIMEOUT_ERROR)], [R.contains(R.__, NODEJS_CONNECTION_ERROR_CODES), R.always(CONNECTION_ERROR)], [R.T, R.always(UNKNOWN_ERROR)]])(error.code);
};
Do you know what my problem is and what i'am doing wrong?
I use this commit 7aa56ac and rn 0.32.0
const api = apisauce.create({
// base URL is read from the "constructor"
baseURL,
// here are some default headers
headers: {
'Cache-Control': 'no-cache'
},
// 10 second timeout...
timeout: 10000
})
api.addAsyncRequestTransform(request => async () => {
request.headers['Authorization'] = 'Bearer ' + await AsyncStorage.getItem('@nytevibetoken');
});
throwing api.addAsyncRequestTransform is not a function error. Why? I want to fethc token from AsyncStorage and add it o header. Any suggestion?
I want to set basic auth on axios requests using its auth
config property. Username and password (used for basic auth) is not know at the time of creation so it can't be set then. What is the solution so that I don't have to set the Authorization
header myself?
Not sure if I'm missing something simple or not.
When I send a POST request that has two or more parameters the request is rejected.
The request body for a successful request usually looks like this:
{ email: '[email protected]', password: 'xxxxxx' }
but instead I get this:
{ '{"email":"[email protected]","password":"xxxxxx"}': '' }
If I have only one parameter it works fine.
Using in a react native app made with the infinitered/ignite generator.
I cannot change the data anymore within a response transformer.
api.addResponseTransform((response)=> {
response.data = { ... }
});
api.get('user').then(response=>{
console.log(response.data); // the original data from my api
})
Is it possible that the transformer api has changed?
EDIT: ok my cleanup function has changed. Before i have done something like this
API.request.addResponseTransform((response)=> {
if ( response.ok ) {
HydraCleanUp(response.data);
}
});
HydraCleanUp mutated the data inside the response.data object;
Now i have this code
API.request.addResponseTransform((response)=> {
if ( response.ok ) {
const data = HydraCleanUp(response.data);
response.data = data;
}
});
And this does not work because i would assign another reference to response.data;
Hi @skellock, I'm a front-end dev from Wanderio and I'm using your package since this summer.
I find your package really useful to isolate the API calls of my application, so I wish to continue use it.
But recently I had some trouble with it, so I will try to help you!
I will open some issues about each topic so we can tackle them separately, thanks in advance!
Cannot do this in a transform:
response.data = camelizeKeys(response.data);
Hi,
I am having difficulty passing multiple variables to the post method using apisauce.
I am getting a response with data.error which indicates that it didn't receive the params
import apisauce from 'apisauce'
const create = (baseURL = 'http://prod.karma.vote/api/') => {
const api = apisauce.create({
baseURL,
headers: {
'Content-Type':'application/x-www-form-urlencoded;charset=UTF-8',
'Accept' : '*/*'
}
})
if (__DEV__ && console.tron) {
api.addMonitor(console.tron.apisauce)
}
const login = () => {
return api.post('login', {
{
email: '[email protected]', firstName: 'XX',
lastName: 'XX', profilePicUrl: 'example.png',
'device.facebookId': '1234567', 'device.token': '',
'device.platform': ''
});
}
return {
login
}
}
export default {
create
}
My current apisauce version is 0.6 and I can't upgrade it for another issue that I will open later, maybe this issue is already solved..
This is the size of my main bundle external packages
As you may notice I have a problem with ramda.js, the library is first in terms of size, and I bet because I import the whole library without tree-shaking/picking only the functions I really use in the application..
But I also see a double ramda depedency coming from apisauce!
https://github.com/babel/babel-preset-env
You can configure it to target node also!
Hi @skellock ,
I'm facing an issue when i am fetching on first request and then fetching second request. its look like the second request is waiting for the first to be done. The second request is being called on another route. Imagine if you are on products page and currently fetching each product's price and then you press one of product and will be redirected into new page (product detail page). on the new page, you create another request to get product's detail data. Any idea about this?
Fancy word for a timer.
That'd be nice to see.
The latest version just added 7 MB on to size because of the babel-runtime
dependency. That's crazy. Find a different way to do that. There's a handful of transformers out there that don't have a dependency on babel-runtime.
It doesn't need to be a monitor, but maybe just a way to turn on console.log
ging.
Maybe also have levels like: none
, simple
, grouping
.
Where grouping will use the grouping functionality in chrome debugging.
it would be nice if we can setup a transformer that can intercept a request and delay its execution until some condition is met.
in my case i'm using api-sauce in an react-native app. i want to intercept any api call IF user is not online and either cancel it. or delay its execution;
i tried returning rejected promise in requestTransformer but it doesnot work
const api = apisauce.create({
baseURL,
headers: header,
timeout: 20000,
});
api.addRequestTransform((request) => {
if(!Config.isOnline()) {
return new Promise((ok, not)=>not({ok: false, data: {message: 'Your device is not connected to internet'}, status: 412}));
} else {
return request;
}
});
is there another method to reject request before sending it if some condition is unmet ?
it would be very nice also if we can keep the request pending until device is back on the resolve promise :).
thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.