I want to make a request from the Coinmarketcap API. They don't allow to use CORS configuration because security matters, the option is execute that in the browser by routing calls through an own backend service.
People said to me use several serverless options with free tier out there, AWS Lambda, Google Cloud Functions, Azure Functions.
But I don't know how to do that, I'm wondering to use Aws Lambda, what I need to do?
That is my node.js code:
const rp = require('request-promise');
const requestOptions = {
method: 'GET',
uri: 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest',
qs: {
'start': '1',
'limit': '5000',
'convert': 'USD'
},
headers: {
'X-CMC_PRO_API_KEY': 'b54bcf4d-1bca-4e8e-9a24-22ff2c3d462c' //that isn't my real api key
},
json: true,
gzip: true
};
rp(requestOptions).then(response => {
console.log('API call response:', response);
}).catch((err) => {
console.log('API call error:', err.message);
});
The AWS Lambda function can help you to execute the api and return the response. You will also need probably AWS API Gateway to connect the response from lambda to your api. The code in lambda will look like this:
const rp = require('request-promise');
exports.handler = async (event) => {
const requestOptions = {
method: 'GET',
uri: 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest',
qs: {
'start': '1',
'limit': '5000',
'convert': 'USD'
},
headers: {
'X-CMC_PRO_API_KEY': 'b54bcf4d-1bca-4e8e-9a24-22ff2c3d462c' //that isn't my real api key
},
json: true,
gzip: true
};
const response = rp(requestOptions);
console.log(response);
return response;
}
basically pass the input in event object and then you could use like a normal object. Also see - https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html
Related
I have 2 functions in the same google cloud functions project (myfunction1 and myfunction2.
exports.myfunction1 = async (req, res) => {
await axios({
method: 'post',
url: 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2',
timeout: 15000,
headers: {
'Content-Type': 'application/json',
},
data: myjson
}).then(response => {
console.log(JSON.stringify(response.data));
}).catch(err => {
console.error("catch error");
console.error(err);
})
}
It is works fine, but only if I configure invokers permission for allUsers. If I remove this permission, e receive 403 code error. Not sounds good keep this permisson activate, because the function is exposed. I tried solve with this link and this link, but, no sucess.
Edit1:
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const targetAudience = 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2'
const url = '??????????';
async function request() {
console.info('request ${url} with target audience ${targetAudience}');
const client = await auth.getIdTokenClient(targetAudience);
const res = await client.request({url});
console.info(res.data);
}
I'm trying using this code, but, who is const url?
You must perform service to service authentication. You can find a great tutorial in the Cloud Run page (ok you use Cloud Functions but the underlying infrastructure is the same and the doc is better).
You also have to be aware about the Functions identity and how to change them (or to grant the current service account the correct permission)
let audience = 'https://SERVER-PROJECT-ID.cloudfunctions.net/myfunction2';
let token_request_url = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=' + audience;
var token_response = await axios.get(token_request_url, { headers: {'Metadata-Flavor': 'Google'} });
let token_auth = token_response.data;
axios({
method: 'post',
url: audience,
timeout: 15000,
headers: {
'Authorization': "Bearer " + token_auth
},
data: myJSON
}).catch(err => {
console.error(err);
});
I have a node.js application served over https. I would like to call an API from that application. The API is also served over https and it has been generated using the express-generator.
Unfortunately the call never works. There is no error message. The call never reaches the API.
Strangely enough if I try to call another public API (e.g. https://api.publicapis.org/entries') that is working perfectly.
Here is my call:
const requestBody = {
'querystring': searchQuery,
};
const options = {
rejectUnauthorized: false,
keepAlive: false, // switch to true if you're making a lot of calls from this client
};
return new Promise(function (resolve, reject) {
const sslConfiguredAgent = new https.Agent(options);
const requestOptions = {
method: 'POST',
body: JSON.stringify(requestBody),
agent: sslConfiguredAgent,
redirect: 'follow',
};
fetch('https://192.168.112.34:3003/search', requestOptions)
.then(response => response.text())
.then(result => resolve(result))
.catch(error => console.log('error', error));
});
};
And here is the API which I would like to call:
router.post('/', cors(), async function(req, res, next) {
req.body;
queryString = req.body.querystring;
let data = JSON.stringify({
"query": {
"match": {
"phonetic": {
"query": queryString,
"fuzziness": "AUTO",
"operator": "and"
}
}
}
});
const { body } = await client.search({
index: 'phoneticindex',
body: data
});
res.send(body.hits.hits)
});
What is wrong with my API and/or the way I am trying to communicate with it?
UPDATE: I receive the following error in the fetch catch block: 'TypeError: Failed to fetch'
When I create a request in Postman I receive the expected response.
UPDATE 2: This is most probably an SSL related issue. The webapp is expecting an API with a valid certificate. Obviously my API can only have a self signed cert which is not enough here. How can I generate a valid cert for an API which is running on the local network and not publicly available?
UPDATE 3: I managed to make it work by changing the fetch parameters like this:
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
mode: 'cors',
body: raw,
agent: httpsAgent,
redirect: 'follow',
})
and on the API side I added the following headers:
'Content-Type': 'application/json',
'Access-Control-Allow-Origin' : 'https://localhost:2200',
'Access-Control-Allow-Methods' : 'POST',
'Access-Control-Allow-Headers' : 'Content-Type, Authorization'
I also added app.use(cors()) and regenerated the self-signed certificates.
I trying to write node function to call third party API . I using the angular Fire Function for display the results in angular project. The issues is no data response;
Here is my node js code.
const request = require('request');
const UserDetail = () => {
const options ={
url: 'https://www.reddit.com/r/funny.json',
method: 'GET',
headers: {
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
'User-Agent': 'my-reddit-client'
}
}
request(options, function(err, res, body) {
let json = JSON.parse(body);
console.log(json);
});
}
UserDetail();
Here is my firebase function code:
exports.userdetails = functions.https.onRequest(require('./api/user/userdetail'));
Here is my angular service calling firebase function code:
callUserDetails(){
const details = this.functions.httpsCallable('userdetails')({ text: 'Some Request Data' })
.pipe()
.subscribe(resp => {
console.log({ resp });
}, err => {
console.error({ err });
});
}
You are mixing up Callable Cloud Functions and HTTPS Cloud Functions.
By doing
exports.userdetails = functions.https.onRequest(...)
you define an HTTPS Cloud Function,
but by doing
this.functions.httpsCallable('userdetails')({ text: 'Some Request Data' })
in your front-end, you actually call a Callable Cloud Function.
You should either change your Cloud Function to a Callable one, or call the userdetails HTTPS Cloud Function by sending an HTTP Request to the Cloud Function URL.
I would advise the first approach because Callable brings several advantages over a "simmple" HTTPS one (see the doc).
In addition you need to note that request supports callback interfaces natively but does not return a Promise. And it is necessary to use Promises in order to manage the life cycle of a Callable Cloud Function (see the official video serie).
I would use Axios along the following lines (untested):
exports.userdetails = functions.https.onCall(async (data, context) => {
try {
const options = {
url: 'https://www.reddit.com/r/funny.json',
method: 'get',
headers: {
'Accept': 'application/json',
'Accept-Charset': 'utf-8',
'User-Agent': 'my-reddit-client'
}
}
const axiosResponse = await axios(options);
// Build the resp to be sent to the frontend by
// using axiosResponse.data .... up to you, see https://github.com/axios/axios#response-schema
return { resp: .... }
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
}
});
Maybe you should call angular service like this:
// provider class
constructor(private http: HttpClient) {}
this.http.get(url, {params: {}, headers: {}}).subscribe(result => {
//// result
})
I would like to consume a REST service in Node.js using request.js, as follows:
var request = require('request');
request.get({
url: 'https://www.googleapis.com/storage/v1/b',
auth: {
'bearer': 'oauth2_token'
}
}, function(err, res) {
console.log(res.body);
});
However, I would like to specify also a set of request parameters, such as project, prefix, etc. (as specified at https://cloud.google.com/storage/docs/json_api/v1/buckets/list).
How can I pass these parameters in the request for consuming the API service?
You can pass in qs as the additional queries. See example below:
const queryObject = { project: 'project', prefix: 'prefix' };
request.get({
url: 'https://www.googleapis.com/storage/v1/b',
qs: queryObject,
auth: {
'bearer': "oauth2_token"
}
}, function(err, res) {
console.log(res.body);
});
See here for github issue.
I'm using the Serverless Framework for my Lambda functions on AWS. How can I send authorization headers? My current config:
Lambda function:
module.exports.createUser = (event, context, callback) => {
let response = {
statusCode: 302,
headers: {
"Authorization": userUtil.getToken(userId)
},
body: ""
};
callback(null, response);
return;
}
serverless.yml:
createUser:
handler: functions/user.createUser
events:
- http:
path: users
method: post
cors: true
integration: lambda
The above config sends the response object as body. The status is still 200.
I'm using Serverless v1.10
If you want to set the status code, headers and body in your code, instead of adding them in your configuration, you need to use the Lambda-proxy setting. See more here.
Lambda-proxy is the default type. So you need to remove the integration: lambda in your serverless.yml. Also, remove the cors: true setting and modify the handler.js to add CORS headers.
Modify your Lambda function to:
module.exports.createUser = (event, context, callback) => {
let response = {
statusCode: 302,
headers: {
"Authorization": userUtil.getToken(userId),
"Access-Control-Allow-Origin" : "*", // Required for CORS support to work
"Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
},
body: ""
};
callback(null, response);
return;
}
Modify your serverless.yml file to:
createUser:
handler: functions/user.createUser
events:
- http:
path: users
method: post