Append a query param to a GET request? - javascript

I'm trying to make a simple API that calls another API that will return some information. The thing is, in order to connect to the second API, I need to attach query parameters to it.
So what I've tried to do so far is to use an axios.get in order to fetch the API. If I didn't need to add queries on top of that, then this would be really simple but I'm having a really hard time trying to figure out how to attach queries on top of my request.
I've created an object that pulled the original query from my end and then I used JSON.stringify in order to turn the object I made into a JSON. Then, from my understanding of Axios, you can attach params my separating the URL with a comma.
On line 6, I wasn't sure if variables would carry over but I definitely can't have the tag var turned into the string "tag", so that's why I left it with the curly brackets and the back ticks. If that's wrong, then please correct me as to how to do it properly.
the var tag is the name of the query that I extracted from my end. That tag is what needs to be transferred over to the Axios GET request.
app.get('/api/posts', async (req, res) => {
try {
const url = 'https://myurl.com/blah/blah';
let tag = req.query.tag;
objParam = {
tag: `${tag}`
};
jsonParam = JSON.stringify(objParam);
let response = await axios.get(url, jsonParam);
res.json(response);
} catch (err) {
res.send(err);
}
});
response is SUPPOSED to equal a JSON file that I'm making the request to.
What I'm actually getting is a Error 400, which makes me think that somehow, the URL that Axios is getting along with the params aren't lining up. (Is there a way to check where the Axios request is going to? If I could see what the actual url that axios is firing off too, then it could help me fix my problem)
Ideally, this is the flow that I want to achieve. Something is wrong with it but I'm not quite sure where the error is.
-> I make a request to MY api, using the query "science" for example
-> Through my API, Axios makes a GET request to:
https://myurl.com/blah/blah?tag=science
-> I get a response with the JSON from the GET request
-> my API displays the JSON file

After looking at Axios' README, it looks like the second argument needs the key params. You can try:
app.get('/api/posts', async (req, res, next) => {
try {
const url = 'https://myurl.com/blah/blah';
const options = {
params: { tag: req.query.tag }
};
const response = await axios.get(url, options);
res.json(response.data);
} catch (err) {
// Be sure to call next() if you aren't handling the error.
next(err);
}
});
If the above method does not work, you can look into query-string.
const querystring = require('query-string');
app.get('/api/posts', async (req, res, next) => {
try {
const url = 'https://myurl.com/blah/blah?' +
querystring.stringify({ tag: req.params.tag });
const response = await axios.get(url);
res.json(response.data);
} catch (err) {
next(err);
}
});
Responding to your comment, yes, you can combine multiple Axios responses. For example, if I am expecting an object literal to be my response.data, I can do:
const response1 = await axios.get(url1)
const response2 = await axios.get(url2)
const response3 = await axios.get(url3)
const combined = [
{ ...response1.data },
{ ...response2.data },
{ ...response3.data }
]

Related

How you manually parse a raw request withtout using express.raw()

i'm implementing Stripe Webhooks and everything works fine but I'm having problems validating the payload when there are special characters(example, áéíóú). The code:
const endpointSecret = "whsec_xxx";
// stripe signature
const sig = headers['Stripe-Signature'][0]
const stripe = require('stripe')(
'sk_test_yyy'
);
//const buf = new Buffer(body, 'base64');
// let text = buff.toString('ascii');
try {
event = stripe.webhooks.constructEvent(body.text(), sig, endpointSecret);
} catch (err) {
response.setStatusCode(400);
throw err;
}
The thing is that i'm using Realm MongoDB HTTP EndPoints that don't support adding a body parser in the function. So What I have is just this:
exports = async function({ query, headers, body }, response) {
I can read the raw body using body.text() and this works fine in most cases, but not all.
What I Need is to emulate the JSON parsing when I declare the function like this:
app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => {
This way always works. But I don't know how to manually parse the body in the same way that bodyparse does it.
Is there any way to achieve this?

Is it good practice to access req.query in a PUT request?

I'm building a website with an API using NEXTjs. For a single element, I use the dynamic api route provided by NEXTjs and I'm currently using that route both for getting an element and updating element.
In both the GET and PUT request, I use the req.query.fetchId to get or update the element.
However, I see req.query mostly used for GET requests and in POST/PUT request it's usually req.body being used.
It seems to work, but I'm wondering if I should?
This is the URL for the request: api/items/[fetchId]
And this is my code for the PUT request so far:
if (req.method==="PUT") {
try {
const { db } = await connectToDatabase();
const videoGamesCollection = db.collection("videogames");
const result = await videoGamesCollection
.updateOne({ _id: ObjectId(req.query.fetchId) }, {$inc: {}})
res.status(200).json({ message: "success", result: result });
} catch (error) {
res.status(error.code ?? 502).send({
message: error.message ?? "Something went wrong.",
});
}
}

Get body of GET request (NodeJS + axios)

Here's my request:
axios.get(BASE_URI + '/birds/random', {Stuff: "STUFF"})
.then(randBird=>{
const birdData = randBird.data
const bird = {
age: birdData.age,
bio: birdData.profile.bio,
displayname: birdData.profile.displayname,
species: birdData.profile.species,
_id: birdData._id
}
this.setState({currentBird:bird})
})
Here's what happens on my router (on '/birds'):
birdRouter.route('/random').get((req, res)=>{
console.log('req.body = ', req.body)
User.count().exec((err, num)=>{
if(err){
console.log(err)
return res.send({error: err})
}
const random = Math.floor(Math.random() * num)
User.findOne().skip(random).exec((err, bird)=>{
if(err){
console.log(err)
return res.send({error: err})
}
console.log(bird)
res.send(bird)
})
})
Really, the only lines that are worth paying attention to in both snippets are the first and first two (for the first and second snippet, respectively).
The request goes through, but my console.log shows this:
req.body = {}
What did I do wrong here?
Some browsers and libraries don't support HTTP get method with a body. You can switch to POST/PUT and see if it works as expected.
Usually in GET method we are not passing body data. instead of body data you can pass in query string. and also if you are using express server than you need to install a package body-parser to get data in body. please take a reference of issue posted in axios

Long running Node REST API takes much time to respond

I have a Rest API in node.js that takes much time to respond because it sends a request to suppliers vendor and once the response is fully prepared then it returns the result what I want is as the result is being prepared it should be able to display it on the front react side. Thanks in advance for any help and your time
here is my controller
module.exports.search = async (req, res) => {
try {
let params = req.query;
params = _.extend(params, req.body);
const result = await modules.Hotel.Search.search(req.supplierAuth, params);
res.json(result);
} catch (e) {
global.cli.log('controller:hotels:search: ', e);
res.status(500).json({ message: e.message });
}
};
here is my front side service
export const getHotels = (filters, options = {}) => {
const queryString = new URLSearchParams(options).toString();
return post(`/api/hotels/search?${queryString}`, filters);
};
The best solution is to use streams and pipe() the results as they come into express's res object, similar to this guy's approach.
You'll have to modify the modules.Hotel.Search.search(....) and make that use streams.

Request inside a nested request loop to another server

I need to request X products from another server and I need to wait for that execution to finish before proceeding and saving the order in the database.
Let's say I receive via post an array of product Ids that I need to add to the order, e.g
JSON FILE:
{
"order_products":[1,2,3,4]
}
Here's a code sample:
//Express module
var router = require('express').Router();
//HTTP Request module
var client = require('request');
//Util that saves the URLs of the other databases
var productURL = require('../utils/product/productURL');
//Builds a product object given a JSON
var productBuilder = require('../utils/product/productBuilder');
router.post('/', req, res) {
//Instantiate a new order
var orderInstance = new order({
date: Date.now
});
//Query the products in the other server and add them to the order
req.body.order_products.forEach(id => {
client.get(productURL.HTTPS + id, { json: true }, (err, res, JSONProduct) => {
var product = productBuilder.build(JSONProduct);
orderInstance.order_products.push(product);
});
};
//Save the order in the database
orderInstance.save(....);
//Send response
res.status(201).json(orderInstance);
}
The problem here is that while the loop is still executing, the response is sent (201) and the orderInstance is saved without any product. If I console.log the products they only appear after the orderInstance is saved.
I've tried implementing callbacks to fix this issue, but with no success. I'd appreciate if anyone could lend me a hand here! Thanks in advance :smiley:(edited)
forEach runs synchronously - when the forEach ends, the client.get requests may have all been sent out, but the responses surely haven't come back yet. You need to convert each request into a Promise, and then call Promise.all on an array of those Promises. The Promise.all will resolve once all responses have come back. For example:
const allPromises = req.body.order_products.map(id => new Promise((resolve, reject) => {
client.get('productURL.HTTPS' + id, { json: true }, (err, res, JSONProduct) => {
if (err) reject (err);
else resolve(productBuilder.build(JSONProduct));
});
}));
Promise.all(allPromises)
.then((newProducts) => {
orderInstance.order_products.push(...newProducts);
res.status(201).json(orderInstance);
})
.catch((err) => {
// handle errors
});

Categories

Resources