so I have the following Scenario; I have a private API key that Angular will show in XHR request. To combat this, I decided to use Express as a proxy and make server side requests. However, I cannot seem to find documentation on how to make my own get requests.
Architecture:
Angular makes request to /api/external-api --> Express handles the route and makes request to externalURL with params in req.body.params and attaches API key from config.apiKey. The following is pseudocode to imitate what I'm trying to accomplish:
router.get('/external-api', (req, res) => {
externalRestGetRequest(externalURL, req.body.params, config.apiKey)
res.send({ /* get response here */})
}
You are half way there! You need something to make that request for you. Such as the npm library request.
In your route something like
var request = require('request');
router.get('/external-api', function(req, res){
request('http://www.google.com', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred and handle it
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
res.send(body)
});
})
This allows you to make any type of request using whatever URL or API keys you need. However it's important to note you also need to handle any errors or bad response codes.
The accepted answer is good, but in case anyone comes across this question later, let's keep in mind that as of February, 2020, request is now deprecated.
So what can we do? We can use another library. I would suggest Axios.
Install it and do something like:
const axios = require('axios')
const url = "https://example.com"
const getData = async (url) => {
try {
const response = await axios.get(url)
const data = response.data
console.log(data)
} catch (error) {
console.log(error)
}
}
getData(url)
Related
I have an issue where I want to call an external API to verify a certain token coming from my frontend.
i.e
router.post('/api/verifyToken', async (ctx, next) => {
router.post('https://external-site.com/verify').then((response) => {
if (response.success) { console.log('Do some stuff') };
})
})
How would I go about doing this with koa?
You misunderstood with the router itself.
In your router, you define a route where your clients can send HTTP requests and according to your business logic, you return the answers to them.
You can simply imagine router.post('/api/verifyToken' as an event listener. When a request comes in you run whatever is inside of it. It is not an HTTP client though.
If you want to send an external request you have to use an HTTP client for it.
There are a bunch of options:
Built-in http/https package
Axios
Node-fetch
GOT
Request
And many others
One simple example how to do with Axios would be
import axios from 'axios';
router.post('/api/verifyToken', async (ctx, next) => {
try{
const response = await axios.post('https://external-site.com/verify');
// Do your stuff here
console.log(response.data);
ctx.body = response.data;
}catch(e){
ctx.status = 422;
ctx.body = e.message;
}
})
From inside my expressJS application I have to verify that a cookie token is valid with a back-end server. So the relevant code involved in this is as follows:
app.get('*', (req, res, next) => {
console.log('GET: ' + req.path);
// ...
const payload = JSON.stringify({ authnToken: token });
const opts = { ... authServerOptions };
opts.headers['Content-Length'] = payload.length;
// build request
const restReq = https.request(authServerOptions, result => {
console.log('back-end response' + result.statusCode);
result.on('data', data => {
next(); // token is good now proceed.
});
result.on('error', error => {
res.redirect('somewhere'); // token is bad or timeout
});
});
restReq.write(token);
restReq.end();
}
So the main get function sets the REST request in motion and then just returns without calling next() or anything.
Questions:
Is this the right code for doing this? What happens if the callbacks are never called?
Is the application blocked from processing other requests until the back-end server returns or times out?
If so is there some way of freeing up the thread to process more requests?
Thanks in advance for any help. I haven't found many examples for this code pattern so if there is one a link would be appreciated.
Yes, I think the general idea of your implementation is correct.
I would also suggest, as done in the comments, to use a client such as axios to handle the request in a less verbose and more comprehensive manner, which would leave your code looking something like this:
const axios = require('axios');
app.get('*', (req, res, next) => {
const payload = JSON.stringify({ authnToken: token });
const opts = { ... authServerOptions };
opts.headers['Content-Length'] = payload.length;
axios.post(url, payload, opts)
.then(response => next())
.catch(error => {
console.error(error);
res.redirect('somewhere');
});
});
A bit more to the point, but functionally almost equivalent to your implementation. The one thing you are missing is the onerror callback for your request object, which currently may fail and never return a response as you correctly suspected. You should add:
restReq.on('error', error => {
console.error(error);
res.redirect('somewhere');
});
On the same vein, it would probably be more fitting to call next on result end, instead of doing so while reading response data:
result.on('end', () => {
next();
});
Then you'd be covered to guarantee that a callback would be invoked.
Neither implementation blocks the processing of future requests, as the call to the token validation service is done asynchronously in both cases.
Trying to keep my code organized. I have a controller directory and router directory. The goal is to make an api call and retrieve data.
CONTROLLER
function searchName(req, res) {
res.setHeader("user-key", process.env.APIKEY)
res.redirect(`https://api-endpoint.igdb.com/games/?search=${req.params.game}&fields=*`)
}
ROUTER
router.get('/search/:game', Controller.searchName)
I export router, and require it in my server.js file. In POSTMAN, this works fine; I do have my API Key hard coded in Postman. I've tried many different methods but can't seem to pass the header with my ApiKey when making the initial request in the code. Additionally, being new to nodejs, I'm not sure if the request redirect is sufficient in this scenario. Ultimately, setHeader is not working
It sounds like you're trying to make an async call to another service, pull back some data, and send that back to the client that called your service. In a nodejs application it's quite trivial to make a new HTTP request. Nodejs has a built in HTTP.Agent, but I'd suggest trying out the axios library as it makes it even easily. This will result in something like this:
const axios = require('axios');
function searchGame(req, res) {
const requestOptions = {
method: 'GET',
uri: `https://api-endpoint.igdb.com/games/?search=${req.params.game}&fields=*`,
headers: {
"user-key": process.env.API_KEY
}
}
axios.get(requestOptions)
.then(function (response) {
// This is the data the remote service gave back
res.send(response.data);
})
.catch(function (error) {
// The remote gave an error, lets just forward that for now
res.send({error: error});
});
}
Here we build an axios request from the req object, we make the request using axios.get, we then wait for the "promise" (this is an async javascript concept if you've not come across it before), then we take the response from that and forward it back as the res. Lets pretend for argument sake that this is a JSON response, but it could be anything.
I am trying to make a slack app and to complete Oauth2, I have to send the URI below and get a JSON response back in the body.
The problem is, every time I am trying to use the function request() in my app.get() function, ejs is always trying to go and get my views. Now I tried rendering my specific view for app.get() but then when I use request() again, ejs is again trying to get a view.
How can I redirect to another url from my app.get and receive the JSON. I can use req.redirect() but I don't know how to get the response back.
Please please help! Thanks
app.get('/', (req, res) =>{
var options = {
uri: 'https://slack.com/api/oauth.access code='+req.query.code+'&client_id='+client_id+'&client_secret='+client_secret,
method: 'GET'
}
request(options, (error, response, body) => {
var JSONresponse = JSON.parse(body)
if (!JSONresponse.ok){
console.log(JSONresponse)
res.send("Error encountered: \n"+JSON.stringify(JSONresponse)).status(200).end()
}else{
console.log(JSONresponse)
res.send("Success!")
}
})
})
This is probably a simple question but I'm new to cloud functions/Node programming and haven't found the right documentation yet.
How do I write a Google cloud function that will receive a HTTP request but then send a HTTP request to a different endpoint? For example, I can send the HTTP trigger to my cloud function (https://us-central1-plugin-check-xxxx.cloudfunctions.net/HelloWorldTest). Later in the project I'll figure out how to implement a delay. But then I want to respond with a new HTTP request to a different endpoint (https://maker.ifttt.com/trigger/arrive/with/key/xxxx). How do I do that?
exports.helloWorld = function helloWorld(req, res) {
// Example input: {"message": "Hello!"}
if (req.body.message === undefined) {
// This is an error case, as "message" is required.
res.status(400).send('No message defined!');
} else {
// Everything is okay.
console.log(req.body.message);
res.status(200).send('Success: ' + req.body.message);
// ??? send a HTTP request to IFTTT endpoint here
}
};
Here is the code that I managed to get working with help from Chetan Kanjani. When I send a text message to my Google Cloud function endpoint, it replys with a text message to IFTTT (a different endpoint).
const request = require('request');
exports.helloWorld = function helloWorld(req, res) {
// Example input: {"message": "Hello!"}
if (req.body.message === undefined) {
// This is an error case, as "message" is required.
res.status(400).send('No message defined!');
} else {
// Everything is okay.
console.log(req.body.message);
request.get('https://maker.ifttt.com/trigger/arrival/with/key/xxxx', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); //Prints the response of the request.
});
res.status(200).send("Success");
}
};
I also had to change the package.json file to include the request package. It already had the sample-http package, I added the dependencies:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"request": "^2.81.0"
}
}
I'm still not sure where the console.log function prints out the information. That might be helpful for future debugging.
The Request module uses callbacks. If you want to use JavaScript promises instead, the Axios module provides equivalent functionality.
Old, but I came across this while searching myself:
request module with promise support is (request-promise)
The code below worked. Not sure if Axios is the ideal module for simple requests like these, but the Google Cloud Function documentation uses Axios so it seemed sensible to also use Axios. Other answers use the request module, but it was deprecated in February 2020.
Note: GCF doesn't support ES6 natively at this time. ES6 support is coming with Node 13.
Package.json
{
"name": "YOUR_NAME",
"version": "0.0.1",
"dependencies": {
"axios": "^0.19.2"
}
}
Index.js
/**
* Required Modules
*/
const axios = require("axios");
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.run = async(req, res) => {
// Set API end point.
let apiURL = YOUR_URL;
// Wrap API parameters in convenient object.
let apiData = {
PARAM_1: PARAM_DATA,
PARAM_2: PARAM_DATA
};
// Invoke API.
axios.post(apiURL,
JSON.stringify(apiData)
)
.then((response) => {
res.status(200).send(response.data);
console.log(response);
}, (error) => {
res.status(500).send(response.data);
console.log(error);
});
};
Use https://www.npmjs.com/package/request module.
var request = require('request');
request.get('https://maker.ifttt.com/trigger/arrive/with/key/xxxx', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); //Prints the response of the request.
});