External API Calls With Express, Node.JS and Require Module - javascript

I have a route as followed:
var express = require('express');
var router = express.Router();
var request = require('request');
router.get('/', function(req, res, next) {
request({
uri: 'http://www.giantbomb.com/api/search',
qs: {
api_key: '123456',
query: 'World of Warcraft: Legion'
},
function(error, response, body) {
if (!error && response.statusCode === 200) {
console.log(body)
}
}
});
});
module.exports = router;
I'm trying to make an API call to the Giant Bomb API to bring back whatever data it has about World of Warcraft.
The problem is, the route just loads; it doesn't do anything or it doesn't time out, it's just continuous loading.
I don't know what I'm doing wrong, but that being said... I don't know what's right either. I'm trying to learn as I go along.
Any help would be great.
Thanks

You need to take the data you get from request() and send it back as the response to the original web server request. It was just continuously loading because you never sent any sort of response to the original request, thus the browser was just sitting there waiting for a response to come back and eventually, it will time out.
Since request() supports streams, you can send back the data as the response very simply using .pipe() like this.
var express = require('express');
var router = express.Router();
var request = require('request');
router.get('/', function(req, res, next) {
request({
uri: 'http://www.giantbomb.com/api/search',
qs: {
api_key: '123456',
query: 'World of Warcraft: Legion'
}
}).pipe(res);
});
module.exports = router;
This will .pipe() the request() result into the res object and it will become the response to the original http request.
Related answer here: How to proxy request back as response
Edit in 2021. The request() library has now been deprecated and is no longer recommended for new code. There are many alternatives to choose from. My favorite is the got() library. The above could be accomplished using it like this. This also upgrades to use the pipeline() function which is a better version of .pipe() with more complete error handling.
const router = require('express').Router();
const got = require('got');
const { pipeline } = require('stream');
router.get('/', function(req, res) {
const dataStream = got.stream({
uri: 'http://www.giantbomb.com/api/search',
qs: {
api_key: '123456',
query: 'World of Warcraft: Legion'
}
});
pipeline(dataStream, res, (err) => {
if (err) {
console.log(err);
res.sendStatus(500);
}
});
});
module.exports = router;

For Laravel users,
First of all install npm i axios package if not.
var axios = require('axios');
var config = {
/* Your settings here like Accept / Headers etc. */
}
axios.get('http://local.dev/api/v1/users', config)
.then(function(response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
Hopefully it will help someone!

Per every route in Express, it is necessary to send a response (partial or complete) or call next, or do both. Your route handler does neither. Try
var express = require('express');
var router = express.Router();
var request = require('request');
router.get('/', function(req, res, next) {
request({
uri: 'http://www.giantbomb.com/api/search',
qs: {
api_key: '123456',
query: 'World of Warcraft: Legion'
},
function(error, response, body) {
if (!error && response.statusCode === 200) {
console.log(body);
res.json(body);
} else {
res.json(error);
}
}
});
});
module.exports = router;
and see what data this route handler responds with.

In 2022
In node
const fetch = (...args) => import('node-fetch')
.then(({default: fetch}) => fetch(...args));
app.get('/checkDobleAPI', async (req, res) => {
try {
const apiResponse = await fetch(
'https://jsonplaceholder.typicode.com/posts')
const apiResponseJson = await apiResponse.json()
console.log(apiResponseJson)
res.send('Running 🏃')
} catch (err) {
console.log(err)
res.status(500).send('Something went wrong')
}
})

Related

Node router returning response by itself

This is probably extremely simple but I am really confused by it.
When I hit my node /verify endpoint either via postman or the frontend, a 200 response from the backend is being sent back automatically when non 'response' values/functions are in the /verify endpoint.
For example, if I make the function extremely simple:
This will send a 200 response back, even though I don't appear to be setting 'response' anywhere.
router.post('/verify', (request, response, next) => {
code = code.toUpperCase();
});
This won't send a 200 response back (and I don't think it should, as I'm not setting response to anything)
router.post('/verify', (request, response, next) => {
const { code } = request.body;
console.log(code);
});
Can anyone explain to me what is going on? I expect to need to reference response like the examples below to push a response back
response.status(401).send("Lorem ipsum");
or
response.json(token);
Thanks
Whole page (excluding other API calls which shouldn't affect this)
const poolArray = require('../../db');
const { Router } = require('express');
const router = Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
router.post('/verify', (request, response, next) => {
// let { code } = request.body; //if commented out will send 200 back automatically, if not commented out won't pass 200 back automatically
code = code.toUpperCase();
})
module.exports = router;
My index.js:
const { Pool } = require('pg');
const oracledb = require('oracledb');
oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
const { langleyUser, langleyHost, langleyDatabase, langleyPassword, langleyPort, onboardingUser, onboardingHost, onboardingDatabase, onboardingPassword, onboardingPort } = require('../secrets/db_configuration');
const langley = new Pool({ user: langleyUser, host: langleyHost, database: langleyDatabase, password: langleyPassword, port: langleyPort });
const onboarding = new Pool({ user: onboardingUser, host: onboardingHost, database: onboardingDatabase, password: onboardingPassword, port: onboardingPort });
const poolArray = {
langley,
onboarding
}
module.exports = poolArray;
Middleware
const jwt = require('jsonwebtoken');
module.exports = (request, response, next) => {
try {
console.log(' in check auth request');
const decoded = jwt.verify(request.headers.authorization, 'bFm3Vp4Ga#cG6W');
request.userData = decoded;
next();
} catch (error) {
return response.status(404).json({
message: 'Authentication failed'
})
}
};
I believe Express will return 200 OK (?) when the response is not specified. If you want to continue to another middleware layer then call next().

Forward any Nodejs request to another servers

Imagine I want to implement API Gateway for microservices. I have this type of route in gateway:
app.all('/api/users-service/*', (req, res, next) => {
});
What I want to do is forward given req to service without knowing it's GET, POST or something else. I also may need to build two different request, forward them to two different services and return merged response in gateway. something like that:
app.all('/api/users-service/*', (req, res, next) => {
const user = await request(req, 'http://first-service/api/user/' + req.body.userId);
const products = await request(req, 'http://second-service/api/products');
res.status(200).json({
user: user,
products: products
});
});
I know it's bad example but hope you will understand what I am trying to do.
You can definitely forward requests to other services, in some cases a proxy will be exactly what you need but in others, you may want to do something more sophisticated like process requests and responses.
In this case you could try something like below:
const rp = require("request-promise-native");
const bodyParser = require("body-parser");
const userServiceRootUrl = "http://first-service/api/user/";
const productServiceRootUrl = "http://second-service/api/products/";
app.all("/api/users-service/*", bodyParser.json(), async (req, res) => {
console.log("/api/users-service/ path:", req.params[0]);
const user = await rp({ baseUrl: userServiceRootUrl, url: req.params[0], body: req.body, json: true, method: req.method });
const products = await rp({ url: productServiceRootUrl });
res.status(200).json({
user: user,
products: products
});
});
In this example we're using request-promise-native as it gives us a promise based api, you could also use node-fetch or some other http client.
What you can also do in this case is create mock endpoints in your express server to test response handling, for example:
app.all("/api/user/*", bodyParser.json(), async (req, res) => {
console.log("/api/user/", req.path);
res.json( { name: "joe smith" })
});
app.all("/api/products/", bodyParser.json(), async (req, res) => {
console.log("/api/products/", req.path);
res.json( [{ id: 1, name: "some product" }]);
});
Then simply change your userServiceRootUrl and productServiceRootUrl as appropriate, e.g.
http://localhost:<port>/api/user/
http://localhost:<port>/api/products/

Calling Express Route internally from inside NodeJS

I have an ExpressJS routing for my API and I want to call it from within NodeJS
var api = require('./routes/api')
app.use('/api', api);
and inside my ./routes/api.js file
var express = require('express');
var router = express.Router();
router.use('/update', require('./update'));
module.exports = router;
so if I want to call /api/update/something/:withParam from my front end its all find, but I need to call this from within another aspect of my NodeJS script without having to redefine the whole function again in 2nd location
I have tried using the HTTP module from inside but I just get a "ECONNREFUSED" error
http.get('/api/update/something/:withParam', function(res) {
console.log("Got response: " + res.statusCode);
res.resume();
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
I understand the idea behind Express is to create routes, but how do I internally call them
The 'usual' or 'correct' way to handle this would be to have the function you want to call broken out by itself, detached from any route definitions. Perhaps in its own module, but not necessarily. Then just call it wherever you need it. Like so:
function updateSomething(thing) {
return myDb.save(thing);
}
// elsewhere:
router.put('/api/update/something/:withParam', function(req, res) {
updateSomething(req.params.withParam)
.then(function() { res.send(200, 'ok'); });
});
// another place:
function someOtherFunction() {
// other code...
updateSomething(...);
// ..
}
This is an easy way to do an internal redirect in Express 4:
The function that magic can do is: app._router.handle()
Testing: We make a request to home "/" and redirect it to otherPath "/other/path"
var app = express()
function otherPath(req, res, next) {
return res.send('ok')
}
function home(req, res, next) {
req.url = '/other/path'
/* Uncomment the next line if you want to change the method */
// req.method = 'POST'
return app._router.handle(req, res, next)
}
app.get('/other/path', otherPath)
app.get('/', home)
I've made a dedicated middleware for this : uest.
Available within req it allows you to req.uest another route (from a given route).
It forwards original cookies to subsequent requests, and keeps req.session in sync across requests, for ex:
app.post('/login', async (req, res, next) => {
const {username, password} = req.body
const {body: session} = await req.uest({
method: 'POST',
url: '/api/sessions',
body: {username, password}
}).catch(next)
console.log(`Welcome back ${session.user.firstname}!`
res.redirect('/profile')
})
It supports Promise, await and error-first callback.
See the README for more details
Separate your app and server files with the app being imported into the server file.
In the place you want to call your app internally, you can import you app as well as 'request' from 'supertest'. Then you can write
request(app).post('/someroute').send({
id: 'ecf8d501-5abe-46a9-984e-e081ac925def',
etc....
});`
This is another way.
const app = require('express')()
const axios = require('axios')
const log = console.log
const PORT = 3000
const URL = 'http://localhost:' + PORT
const apiPath = (path) => URL + path
app.get('/a', (req, res) => {
res.json('yoy')
})
app.get('/b', async (req, res) => {
let a = await axios.get(apiPath('/a'))
res.json(a.data)
})
app.listen(PORT)

Redirect image request on NodeJS

Inside my application code, for a specific set of APIs, I'm making a NodeJS request like following, which should return a image as the body. This same request works fine on Postman (and I can see the image).
module.exports = {
getThumbnail: function (thumbnailUrn, env, token, onsuccess){
request({
url: config.baseURL(env) + config.thumbail(thumbnailUrn),
method: "GET",
headers: {
'Authorization': 'Bearer ' + token,
}
}, function (error, response, body) {
// error check removed for simplicity...
onsuccess(body);
});
}
}
The above code run under my own security checks and adds the token header. It works fine (request calls return 200/OK).
Now on my app router I want to respond this as an image, but the output is not being interpreted as an image. Here is what I have:
var dm = require(/*the above code*/);
// express router
var express = require('express');
var router = express.Router();
router.get('/getThumbnail', function (req, res) {
var urn = req.query.urn;
dm.getThumbnail(urn, req.session.env, req.session.oauthcode, function (thumb) {
res.writeHead(200,
{
'Content-Type': 'image/png'
}
);
// at this point, the 'thumb' variable is filled
// but I believe is not properly encoded...
// or maybe the res.end output is missing something...
res.end(thumb, 'binary');
});
});
module.exports = router;
EDIT: as commented by Nodari Lipartiya, this is kind of proxy behaviour ( server(responds with image) -> proxy (node.js/resends to client) -> end user)
I'm not sure what is coming back in thumb, but the following snippet seemed to work for me (bypassing Express for simplicity):
var http = require("http")
var fs = require("fs")
var server = http.createServer(listener)
server.listen(() => {
console.log(server.address().port)
})
var binary = fs.readFileSync("path to local image")
function listener(req, resp) {
resp.writeHead(200,
{
'Content-Type': 'image/png'
}
);
resp.end(new Buffer(binary), "binary")
}
What happens if you wrap it in a Buffer?
If I've understood everything correctly:
I did this
server.js
var fs = require('fs');
var express = require('express');
var app = express();
app.get('/img', function(req, res, next) {
var stream = fs.createReadStream('img.jpeg');
var filename = "img.jpeg";
filename = encodeURIComponent(filename);
res.setHeader('Content-disposition', 'inline; filename="' + filename + '"');
res.setHeader('Content-type', 'image/jpeg');
stream.pipe(res);
});
app.listen(9999, function () {
console.log('Example app listening on port 9999!');
});
proxy.js
var request = require('request');
var express = require('express');
var app = express();
app.get('/img', function(req, res, next) {
console.log('proxy/img');
request({
url: 'http://localhost:9999/img',
method: "GET",
}, function (error, response, body) {
res.end(body, 'binary');
});
});
app.listen(9998, function () {
console.log('Example app listening on port 9998!');
});
req.js
var request = require('request');
request({
url: 'http://localhost:9998/img',
method: "GET",
}, function (error, response, body) {
console.log('body', body);
});
works for me. Please, let me know if you'll need help.

Getting 500 error from API request for my node.js express app

I have a custom module that sends a API request and receives JSON.
data.js
var data = function(callback) {
var request = require('request')
request.post('https://getpocket.com/v3/get', {
headers: {'content-type':'application/json'},
body: JSON.stringify({
consumer_key:'...',
access_token:'...',
tag: 'nodejs'
})
}, function (err, res, body) {
callback(body);
})
}
module.exports = data;
And the route that will render the data.
index.js
var express = require('express');
var router = express.Router();
var data = require('../lib/data.js');
router.get('/', function(req, res, next) {
data( function(data) {
console.log(data)
res.render('index', {
title: 'Express',
body: data
});
});
});
module.exports = router;
With this structure I always get GET / 500 1129.898 ms - 1725
If I place the API request implementation in app.js like the following, I'm getting the data without 500 error.
app.js
var request = require('request')
request.post('https://getpocket.com/v3/get', {
headers: {'content-type':'application/json'},
body: JSON.stringify({
consumer_key:'...',
access_token:'...',
tag: 'nodejs'
})
}, function (err, res, body) {
//callback body
})
But having this in app.js, I'm not sure how to pass the data to the route index.js and make the callback work and also not sure if app.js is the right place for this implementation so I'm hoping to make my custom module data.js work but what can possibly cause 500 error?

Categories