NodeJS: #esri/arcgis-rest-request - How to adjust Fetch options? - javascript

I am looking to adjust the Fetch request option when making a request with #esri/arcgis-rest-request, but unfortunately I cannot find any documentation related to this.
import fetch from "node-fetch";
import FormData from "isomorphic-form-data";
import arcgisRestRequest from "#esri/arcgis-rest-request";
arcgisRestRequest.setDefaultRequestOptions({ fetch, FormData });
arcgisRestRequest.request("https://www.arcgis.com/sharing/rest/info")
.then(response => console.log(response));
When using the request method I am getting errors regarding the certificate of the NodeJS server:
FetchError: request to https://xxx/server/rest/self?token=xxx=json failed, reason: unable to get local issuer certificate
I would like to pass something like:
const fetchOptions = {
...
agent:new https.Agent({rejectUnauthorized: false}),
...
};
to avoid the certificate error.
How can I accomplish this?

Looking at their code it looks like you should be able to just do
arcgisRestRequest.request(
"https://www.arcgis.com/sharing/rest/info",
{
agent:new https.Agent({rejectUnauthorized: false})
}
)
.then(response => console.log(response));

Related

Express catch-all error handling with custom message or property

I'm using Express inside Netlify Functions to create a custom API for my front-end Vue app. In that, I'm connecting to a few other third-party API endpoints.
While I've got a custom error-handling middleware working correctly, it still doesn't give me any ability to write a DRY code. Not sure if that's the correct way or if I'm missing something.
For example, I have something like (someRoute.ts):
import type {NextFunction, Response} from 'express'
import type {Request} from 'express-serve-static-core'
import axios from 'axios'
export default function (request : Request, response : Response, next : NextFunction) {
axios({
url: '/api1/'
}).then(() => {
axios({
url: '/api2/'
}).then(() => {
response.status(200).json({
message: 'sequential API call completed'
})
}).catch(next)
}).catch(next)
}
Now, if my /api1/ call fails, I wish to send a different error message than when it fails at /api2/. There are 2 reasons I wish to do this:
If a user reports an error - I wish to be able to locate, where exactly my code failed. I might have 5 or 6 API calls to complete in a single route, so it would be difficult to locate at what stage the API failed. A custom message per error or some kind of a custom property would be helpful.
I'd also like to be able to present a meaningful error message to users, if possible.
To achieve 1, I thought of attaching a custom property to the request:
import type {NextFunction, Response} from 'express'
import type {Request} from 'express-serve-static-core'
import axios from 'axios'
export default function (request : Request, response : Response, next : NextFunction) {
request.stage = 101
axios({
url: '/api1/'
}).then(() => {
request.stage = 102
axios({
url: '/api2/'
}).then(() => {
response.status(200).json({
message: 'sequential API call completed'
})
}).catch(next)
}).catch(next)
}
To achieve 2, the only way I know is using:
import type {Response} from 'express'
import type {Request} from 'express-serve-static-core'
import axios from 'axios'
export default function (request : Request, response : Response) {
axios({
url: '/api1/'
}).then(() => {
axios({
url: '/api2/'
}).then(() => {
response.status(200).json({
message: 'sequential API call completed'
})
}).catch(() => {
throw new Error('Error from API 2')
// I might as well call response.send(500) here
// no point having a custom error handling middleware
// if I have to manually send errors
})
}).catch(() => {
throw new Error('Error from API 1')
})
}
I can possibly use my approach for 1 and in my front-end present error messages based on error codes - but it might soon get messy to handle too many error codes.
If this is not the correct way and if others have a better way of handling errors, please let me know.

Accessing endpoint via SSH tunnel

I'm running a dev project on my local machine (localhost) which is trying to load data from an endpoint on a server via an SSH tunnel (a React JS SPA).
The SSH tunnel is established fine and the endpoint returns the correct data packet when run in Postman using a simple GET.
But when I try to access the data from my app (fetch and Axios tried) a 200 is returned but unfortunately always with an empty array - rather than a 1k object array.
Below are the 2 simple requests, where 'localhost:8912' is the exposed & mapped port - which works in Postman.
Fetch:
fetch("http://localhost:8912/ENDPOINT/")
.then(res => console.log(res))
.catch(err => {
console.log(err);
return null;
});
Axios:
axios
.get("http://localhost:8912/ENDPOINT/")
.then(data => console.log(data.data))
.catch(err => {
console.log(err);
return null;
});
The result is returned almost immediately within the browser but take a few seconds within Postman due to server-side calculations.
Any suggestions welcomed.
If you don't have a proxy you can combine axios with another npm library — tunnel to establish a HTTPS-over-HTTP tunnel:
import axios, { AxiosInstance } from 'axios';
import * as tunnel from 'tunnel';
const agent = tunnel.httpsOverHttp({
proxy: {
host: 'proxy.mycorp.com',
port: 8000,
},
});
const axiosClient: AxiosInstance = axios.create({
baseURL: 'https://some.api.com:443', // here I specify port 443
httpsAgent: agent,
});
Ref: https://janmolak.com/node-js-axios-behind-corporate-proxies-8b17a6f31f9d
Try replacing localhost with 127.0.0.1
This could be related to the address resolution in the browser vs. Postman.
fetch("http://localhost:8912/ENDPOINT/")
.then(res => return res.json())
.then(res => console.log(res))
.catch(err => {
console.log(err);
return null;
});
fetch returns an object as Promise that contains various information like headers, HTTP status etc. etc.
You have res.json() and various other possibilities. .json() will just return the body as promise with json content.
For more info: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
You can return the data as following:
.arrayBuffer()
.blob()
.json()
.text()
.formData()

Aftership Api with React fetch issue

I added aftership in my react app the following way:
const Aftership = require('aftership')('put-my-API-KEY', {
endpoint: 'https://api.aftership.com/v4'
});
let query = {
page:10,
limit:5
};
Aftership.call('GET', '/trackings', {
query: query
}, (err, result) => {
console.log(err);
console.log(result);
});
export default Aftership;
When I am using node src/aftership.js in terminal, then fetching data works well (after comment out last line export default Aftership).
But when I fetch from src/containers/Tracking.js file the following way:
import Aftership from '../../aftership';
...
componentDidMount (){
let query = {
page:10,
limit:5
};
Aftership.call('GET', '/trackings', {
query: query
}, (err, result) => {
console.log(err);
console.log(result);
});
}
it's showing me an error in console:
Failed to load https://api.aftership.com/v4/trackings?page=10&limit=5:
Request header field x-aftership-agent is not allowed by
Access-Control-Allow-Headers in preflight response. Tracking.js:28
TypeError: Failed to fetch Tracking.js:28 Undefined
Can anyone help me what is my issue and what should I do know?
This looks like a Cross-Origin Resource Sharing (CORS) problem. To simply put this is happening you are using your local development host with third party instead. To avoid such errors, you can use Chrome extensions like https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi. This extension will edit response header to Access-Control-Allow-Headers: '*', which allows all headers on request body.
This is not recommended for daily use, so I recommend to add wildcard for only APIs you would like to use. In this case add https://api.aftership.com/* to extension settings

How to log the AWS Amplify API.get request to view queryStringParameters on url

I'm not sure how to log / see the actual request that is being made.
I can look at this code below and assume that it's http://myendpoint.com?my/path?param=value, however with more complex code and variables in other places, how can I tell what exactly is getting called via API.get?
The main reason I ask is because I don't think my query parameters are being appended to my request, and I'm hoping to confirm.
const apiName = 'http://myendpoint.com'
const path = '/my/path'
const myInit = {
queryStringParameters: {
param: 'value'
}
}
API.get(apiName, path, myInit)
.then((response) => {
console.log('> > > PLEASE TELL ME HOW TO LOG THE REQUEST < < <')
resolve(response)
},
(err) => {
console.log('err resp', err)
resolve(err)
})
Edit: FYI this is in a REACT NATIVE project, so things like the Chrome Network tab are of no use unfortunately.
Okay, I actually think I figured this out, and it boiled down to two different things:
1. ADDING THE AMPLIFY LOGGER:
I found there is an Amplify logger via:
https://github.com/aws/aws-amplify/blob/master/media/logger_guide.md
So I added:
Amplify.Logger.LOG_LEVEL = 'DEBUG'
and now when I am debugging in VS Code I'm seeing the request URL being logged.
2. REALIZING 'queryStringParameters' ISN'T ACTUALLY SUPPORTED: .
I was looking through the Amplify GitHub repo issues and found out that queryStringParameters isn't actually supported yet, which is fun.
URL to issue: https://github.com/aws/aws-amplify/issues/127 .
So instead I appended all my query parameters onto the path, and that works:
const apiName = 'http://myendpoint.com'
const path = `/my/path?param=${value}`
API.get(apiName, path)
.then((response) => {
resolve(response)
},
(err) => {
console.log('err resp', err)
resolve(err)
})
I am now seeing the request URL logged, and seeing the parameters as a part of the request.

Firebase OAuth login & data access with axios

I am pretty new to Axios and very new to OAuth and Firebase, so I'm sure I'm missing something dumb...
I am trying to create a sign in using firebase's auth provider functions & then create a user profile in my database using Axios. (I have to make a ton of other API calls based on the data I receive and it would be very convenient to just use Axios for everything.)
Here is what I have so far.
authenticate() {
var provider = new firebase.auth.GithubAuthProvider();
console.log(provider);
firebase.auth().signInWithPopup(provider)
.then((res) => {
console.log(res);
if (res.credential) {
var token = res.credential.accessToken;
}
const user = axios.create({
baseURL: fbaseUrl,
withCredentials: true, // newly added
headers: {
Authorization: `Bearer ${token}`, // cf firebase docs https://firebase.google.com/docs/reference/rest/database/user-auth
}
});
this.setState({uid: res.user.uid, useraxios: user, token: token});
}).catch((err) => {
console.log(err.message);
});
}
testPost() {
this.state.useraxios.post(`/users.json`, { id: this.state.uid, joinedOn: moment() })
.then((res) => console.log(res))
.catch((err) => console.log(err.message)); /// this errors out
}
The error I'm currently getting is that there is no 'Access-Control-Allow-Credentials' header and therefore localhost is not allowed access, which I assume is something in the Firebase rules that I have to sort through. Before I added the withCredentials: true line, I was just getting the "not allowed access" response.
I have also tried
const user = axios.create({
baseURL: `${fbaseUrl}/users/${res.user.uid}.json?auth=${token}`
});
and
firebase.auth().currentUser.getToken(true).then((token) => {
const user = axios.create({
baseURL: `${fbaseUrl}/users/${res.user.uid}.json?auth=${token}`
});
and
firebase.auth().currentUser.getToken(true).then((token) => {
const user = axios.create({
baseURL: `${fbaseUrl}`,
headers: {Authorization: token}
});
as per this stackoverflow question which returns the 401 Unauthorized error.
Posting to the database is totally fine when I have both read & write set to true, so it's not a problem with how I'm formatting the URL or something.
I am assuming there are a couple of problems, one with my axios.create config and another with my Firebase rules, but I have gone through the documentation for both and am still very much at a loss. This is a react app but I'm 98% sure the react stuff isn't the problem.
Am I at least on the right track? (Am I a fool to try to use axios for something that would be better suited to firebase's built-in methods...?) Any help would be deeply appreciated.
It is related to your functions configuration. You need to add this in your firebase functions / index.js and configure your function with cors.
const cors = require('cors')({origin: true});
For more details please refer to this url: Enabling CORS in Cloud Functions for Firebase

Categories

Resources