How to call an external API from my API in Koa.js - node.js

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;
}
})

Related

Can I make a fetch request from an express request handler or middleware?

Let's say I have an express api with an /api/load-post endpoint. That is handled by the loadPostHandler: RequestHandler
index.ts
const app = express();
app.get("/api/load-post", loadPostHandler);
loadPostHandler.ts
Can I make a fetch request from that handler?
import fetch from "cross-fetch";
export const loadPostHandler: RequestHandler = async (req, res) => {
// HANDLE BLOGPOST LOAD
res.json({ blogPost: blogPostData }) // RES JSON THE BLOGPOST DATA
await fetch("/api/updateViewcount?id=POST_ID"); // MAKE A FETCH REQUEST
};
Is this something people usually do? Or is this an anti-pattern? Not sure if this would even work.
Short answer
Yes, you can make requests in the api call handler in general, and it depends on the requirements of that api.
Longer version
Judging by your example: you want to update view count, and since there is no use of response of it, you don't need to await for the response. You can just fire it without await.
And structurally it would be better practice to move it to a separate function that make an actual call, or fire an event and handle it in a different place.
Moreover, it looks like you are calling the same api server, in that case it will be better just to call a function instead of the api call.
const updatePostViewcount = postId => {
// HANDLE BLOGPOST VIEWCOUNT UPDATE
}
export const loadPostHandler: RequestHandler = async (req, res) => {
// HANDLE BLOGPOST LOAD
// no await here because we don't need the response
// it will still run asynchronously
updatePostViewcount(POST_ID);
res.json({ blogPost: blogPostData }) // RES JSON THE BLOGPOST DATA
};

Using https for REST requests from inside expressjs applicaiton

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.

How to request apikey from backend API using node js?

I was learning to build a weather app using Node (Express) + React. I successfully fetched weather data from open weather API.
However I was directly using the open weather API key in my React app like this const weatherURL = 'http://api.openweathermap.org/data/2.5/weather?q=london,uk&APPID=1234567qwerty';. Obviously this is not safe as it exposed the API key to the client. I thought about storing the API key in .env file, but according to [this answer][1], I should never store API key in .env file or .gitignore. The right way is to make a request to backend API and make an API call to backend and send the data back. I could not find out how to do it. Can anyone help?
Following is my node js code:
const express = require('express');
const cors = require('cors');
const app = express();
const SELECT_ALL_QUERY = 'SELECT * FROM `mySchema`.`myTable`;';
app.use(cors());
app.get('/', (req, res) => {
res.send('go to /myTable to see content')
});
const pool = require('./awsPool');
pool.getConnection((err, connection) => {
if (err) {
return console.log('ERROR! ', err);
}
if(!connection) {
return console.log('No connection was found');
}
app.get('/myTable', (req, res) => {
console.log(connection);
connection.query(SELECT_ALL_QUERY, (err, results) => {
if (err) {
return res.send(err)
}
else {
return res.json({
data: results
})
};
});
});
});
let port=process.env.PORT||4000;
app.listen(port, () => {
console.log(`App running on port ${port} `);
});```
[1]: https://stackoverflow.com/a/57103663/8720421
What the linked answer was suggesting is to create a route in your Node/Express backend API that will make the call to the weather API for you, instead of the front end. This way the request and your API key are not public-facing whenever your front end makes a call.
The method for doing this would essentially be the same as what you have done in React, making an HTTP request using a built-in or 3rd party library. This resource I just found has some information on how to do both.
The simplest pure http-request in node looks like this:
const http = require('http')
const url = 'http://api.openweathermap.org/data/'
http.request(url, callback).end()
function callback (weatherResponse) {
let jsonString = ''
weatherResponse.on('data', chunk => {
jsonString += chunk
})
weatherResponse.on('end', () => {
// Now you have the complete response and can do whatever you want with it
// like return it to your user `res.send(jsonString)`
console.log(jsonString)
})
}
Many people find it bulky to having to handle chunks and the whole asynchronous thing, so there are many popular npm modules, like: https://www.npmjs.com/package/axios. (And here's a list of other contenders https://github.com/request/request/issues/3143).
Also, it is normal to store API-keys in environment variables on the backend. It makes things easy if you ever try to dockerize your app, or just scale up to using two backend servers instead of one.
I found a solution based on #ippi answer, add the following part to the original code:
const request = require('request');
const url = 'http://api.openweathermap.org/data/2.5/weather?q=london,uk&APPID=1234567';
app.get('/weather', (req, res) => {
request(url, (error, response, body) => {
if (!error && response.statusCode == 200) {
var info = JSON.parse(body)
res.send(info);
}
})
})
The url can be stored in .env file and passed into the above code. The returned weather data can be viewed in JSON format at http://localhost:4000/weather. In React the weather data can be fetched via this localhost url.
EDIT: request is deprecated, so here is a solution using axios
app.get('/weather', (req, res) => {
axios.get(url)
.then(response => {res.json(response.data)})
.catch(error => {
console.log(error);
});
})
User Passport middleware for nodeJs/Express. They provide passport-headerapikey strategy using which you can create and authorize apiKeys. http://www.passportjs.org/packages/passport-headerapikey/

Using Node and Express, How to Call remote API from inside server.get(..)

Because of CORS problems, I want to call an external REST API from inside my node express server. That is, I have code like this that obviously does not work because it does not return.
How can I make this work and return the results of my external call?
const server = express();
server.put('/callme',(req,res) => {
axios
('http://weather.com/restapi', 'put', { zip: 10530 })
.then((resp: any) => {
console.log(' success' + resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
Axios returns a Promise which is resolved in the .then(). In order to get the response data back to the client you need to return it with res.send().
const server = express();
server.get('/callme', (req, res) => {
axios
.get('http://weather.com/restapi?zip=10530')
.then((resp: any) => {
res.send(resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
It would be a good idea to cache the weather API response for a period of time and serve the cached response for subsequent requests.

Making external get request with Express

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)

Resources