I am confused about http requests and how it works when requesting links.
So I know a GET requests adds parameters to urls like ?username=ABC123&password=TTT and POST just sends data to the server but thats all I get when reading tutorials out there. How would I for example send a request and download a file from a rapidgator link after authenticating? (I am a premium user).
Link to their api doc:
https://support.rapidgator.net/index.php?/Knowledgebase/Article/View/89/9/do-you-have-api
The first response gives back a response object like this which gives the session id which is where I got the session id from:
{
"response": {
"expire_date":1351526400,
"traffic_left":0,
"reset_in":11695
},
"response_status":200,
"response_details":null
}
This is my code:
const rp = require('request-promise');
const url = 'http://rapidgator.net/api/user/login';
const opts = {
uri: url,
qs: {
username: '**censored**',
password: '**censored**',
},
headers: {
'User-Agent': 'Request-Promise'
},
json: true
}
rp(opts)
.then(( resp ) => {
const sessionId = resp.session_id;
const postOpts = {
uri: 'http://rapidgator.net/api/user/info?sid=knf3pqpg3ldm05qnol0sn16326',
method: 'POST',
body: {
session_id: sessionId
},
json: true
}
rp(postOpts)
.then(res => {
console.log(cyan(JSON.stringify(res, null, 2)));
})
})
.catch(( err ) => {
throw Error(err);
});
Am I approaching this the wrong way? How do I proceed to download links from rapidgator?
Related
This question already has an answer here:
Express routes parameters
(1 answer)
Closed last month.
I'm trying to send a POST to my server, in order to edit a user's details. I've made sure it's sending to the right URL, however get a 404 response. GET requests work fine, however my POST doesn't seem to get through for whatever reason. I've been searching for solutions for a while, with no luck hence posting here!
user.js (server side)
userRoutes.route('/user/update/:id').post(function (req, response) {
let db_connect = dbo.getDb("preview");
let myquery = { _id: ObjectId(req.params.id) };
let newValues = {
$set: {
name: req.body.name,
user_name: req.body.user_name
},
};
db_connect
.collection("users")
.updateOne(myquery, newValues, function (err, res) {
if (err) throw err;
console.log('user updated');
response.json(res);
})
});
middleware
export const updateUser = async (id, userDetails) => {
const endpoint = `${serverIp}/user/update/?id=${id}`;
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userDetails)
})
const result = await response.json();
return result;
} catch (error) {
console.log(error)
}
}
and a simple function to handle submitting
function handleSave() {
const newUserDetails = {
name: accountHolder,
user_name: accountUsername
};
updateUser(userId, newUserDetails);
}
Under networking in dev tools I can see the URL is indeed correct, so I can't see why this isn't working
chrome dev tools
Any help would be greatly appreciate it!
I've tried sending a basic response (i.e. a string instead of object), changing the endpoint, and more all to no avail
It seems like you are passing the id as a query param and not as part of the path
const endpoint = `${serverIp}/user/update/?id=${id}`;
^
What I can see from first glance is that in server-side you are using request parameter for id, but in the client you're sending id as a request query
Here is the code for my server which works fine. I am trying to achieve this with netlify's serverless functions which I have pasted further below.
CODE ON STANDARD SERVER-HEROKU
const ratingController = {};
const Rating = require("../models/ratingModel");
ratingController.getAllRatings = async function (req, res) {
const rating = await Rating.find();
res.status(200).json({
status: "success",
data: rating,
});
};
ratingController.createOneRating = async function (req, res) {
console.log(req.body);
req.body.userIp = req.headers["x-forwarded-for"];
const rating = await Rating.create(req.body);
// const rating = new Rating(req.body);
// await rating.save();
res.status(200).json({
status: "success",
data: {
rating,
},
});
};
PART 1 - GET REQUEST
Here's my code for the getAllRatings and it works fine
SERVERLESS FUNCTION - NETLIFY
const { MongoClient } = require("mongodb");
require("dotenv").config();
exports.handler = async function getData(event, context) {
const client = await MongoClient.connect(process.env.DB, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
const db = client.db();
try {
const slug = event.queryStringParameters.id;
const data = await db.collection("collectionName").find({ slug }).toArray();
client.close();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "success",
data: data,
}),
};
} catch (error) {
console.log(error);
return {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "fail",
message: error.message,
}),
};
}
};
My first question for the above is
Just because it works may not mean it's the right way to do it.. I had a few concerns if calling the database each time there's a call is correct and then placing the code the way I have, if it's the way it should be. It's all based on testing and random research. There's no real method being followed so would appreciate some guidance on a more efficient method to do this.
Normally on a regular server the database connection is done just once.. and here I seem to be doing it every time and I am a bit confused if that's ok or not..
PART 2 - POST REQUEST
Here's my code for the POST request createOneRating
SERVERLESS FUNCTION - NETLIFY
const { MongoClient } = require("mongodb");
require("dotenv").config();
exports.handler = async function createRating(event, context) {
const client = await MongoClient.connect(process.env.DB, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
const db = client.db();
try {
console.log(event);
const rating = await db.collection("ratings").insertOne(event.body);
client.close();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "success",
data: rating,
}),
};
} catch (error) {
console.log(error);
return {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
status: "fail",
message: error.message,
}),
};
}
};
This one does not work as it says
{
"status": "fail",
"message": "Cannot create property '_id' on string ''"
}
And I guess that's because event.body is not where the data is.. but I am not sure how to get the POST data in a serverless deployment.
So my second question is
How do I retrieve the data sent by a POST request. As there's no request parameter I am a bit confused.
Also I'd like to add the IP of the user so other than the POST data I'd also need some help on how to do this
req.body.userIp = req.headers["x-forwarded-for"];
Based on my own research, I have answers to the questions and am placing them here for my own reference and for those who might face a similar situation in the future.
Question 1 : Is it ok to make a database connection on every call that's made to a serverless function
It seems it's ok to do this and for those, like me, who thought that maybe it was not the right way to do it, it's definitely not the wrong way. Maybe there's an efficient way to do this and I'd be open to learn more about this if possible. But for now, it's good to know that it's not wrong to connect to the database each time you make a call.
Question 2: How to make a POST request on a serverless as there's no request parameter
I was not aware that the event parameter is in fact a replacement for the request parameter and that the headers and body are properties of the event object and can be accessed in the same way ie event.body and event.headers. Here's a link that could save you some time to confirm this.
(https://docs.netlify.com/functions/build-with-javascript/#synchronous-function-format)
And if you, like me, don't know if a serverless function can be defined as GET or POST or run into an issue where the POST request gets converted into a GET when making a function call here's a link that would help.
How to define Netlify function endpoint as POST?
When making the following fetch request on my front-end I'm getting my desired type and id values.
export const getUserProfile = () => {
return (
fetch(
"https://api.spotify.com/v1/me", {
headers: {"Authorization": "Bearer " + user_id}
})
.then(response => {
return response.json()
})
.then(data => {
console.log(data.type)
console.log(data.id)
})
)
}
Knowing you can't use fetch api in Node I used the npm install request package to get the data on my node server.
request.post(authOptions, function(error, response, body) {
var access_token = body.access_token
let postInfo = {
url: 'https://api.spotify.com/v1/me',
headers: {
"Authoriztion": "Bearer " + access_token
},
json: true
}
request.post(postInfo, function(error, response, body) {
const route = body.type
const current_user_id = body.id
console.log(body)
let uri = process.env.FRONTEND_URI || `http://localhost:3000/${route}/${current_user_id}`
res.redirect(uri + '?access_token=' + access_token)
})
})
The purpose of doing this is so when the res.redirect gets called it sends the client to the user's home page. However when the client gets redirected the url is http://localhost:3000/undefined/undefined?accesss_token={some token}
when looking why the values are undefined I console.log(body) and I get
{
error: {
status: 401,
message: 'No token provided'
}
}
but I can see when logging the response that the token is included
_header: 'POST /v1/me HTTP/1.1\r\nAuthoriztion: Bearer {some token}=\r\nhost: api.spotify.com\r\naccept: application/json\r\ncontent-length: 0\r\nConnection: close\r\n\r\n'
I can see why my values are undefined but why am I getting an unauthorized status in node but not on the client using fetch api? Also I noticed that the url access_token doesn't match the server logged token.
Here are the docs I'm using:
https://www.npmjs.com/package/request
https://developer.spotify.com/documentation/web-api/reference/users-profile/get-current-users-profile/
Github file: https://github.com/ryansaam/litphum-server/blob/master/server.js
If you use node-fetch in your server code, you have a similar API as fetch.
I'm trying to retrieve, then POST a JPEG image to Foursquare's https://api.foursquare.com/v2/photos/add endpoint using Axios in Node. I've tried a few methods with Axios (and Postman) but always receive the same error response of Missing file upload:
{
"meta": {
"code": 400,
"errorType": "other",
"errorDetail": "Missing file upload",
"requestId": "NNNNNNNNNNNNNNNNNNNNN" // not the true requestId
},
"notifications": [
{
"type": "notificationTray",
"item": {
"unreadCount": 0
}
}
],
"response": {}
}
The image is created using the Google Static Map API and retrieved with an Axios GET request:
const image = await axios.get(imageURL, {
responseType: "arraybuffer"
});
which is wrapped in an async function and successfully returns a buffer. The data is read into a Buffer and converted to a string:
const imageData = new Buffer(image, "binary").toString();
Here's an example imageData string. I've also tried converting the string to base64.
This string is then POSTed to the Foursquare endpoint:
const postPhoto = await axios.post(
"https://developer.foursquare.com/docs/api/photos/add?
checkinId=1234&
oauth_token=[TOKEN]&
v=YYYYMMDD",
imageData,
{
headers: { "Content-Type": "image/jpeg" }
}
);
where the checkinId, oauth_token and v params are all valid.
I've tried different Content-Type values, base64 encoding the imageData and several other solutions found in forums and here on SO (most are several years old), but nothing works. The response errorDetail always says Missing file upload.
The issue could be in how the POST request is structured, but I could also be requesting/handling the image data incorrectly. A 2nd (or 3rd, or 4th) set of eyes to check I'm putting this together would be super helpful.
Whew, I have finally solved this.
I was eventually able to get it working thru Postman which provided some hints. Here's the Postman code snippet using request:
var fs = require("fs");
var request = require("request");
var options = { method: 'POST',
url: 'https://api.foursquare.com/v2/photos/add',
qs:
{ checkinId: [MY CHECKING ID],
public: '1',
oauth_token: [MY OAUTH TOKEN],
v: [MY VERSION] },
headers:
{ 'postman-token': '8ce14473-b457-7f1a-eae2-ba384e99b983',
'cache-control': 'no-cache',
'content-type': 'multipart/form-data; boundary=---- WebKitFormBoundary7MA4YWxkTrZu0gW' },
formData:
{ file:
{ value: 'fs.createReadStream("testimage.jpg")',
options: {
filename: 'testimage.jpg',
contentType: null
}
}
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
The key part of this was fs.createReadStream(). The part I was missing before was to pass the image as a stream to the request.
Using this I was able to figure out the Axios request:
const axios = require("axios");
const querystring = require("qs");
const FormData = require("form-data");
const getImageStream = async function(url) {
return await axios
.get(url, {
responseType: "stream"
})
.then(response => response.data);
};
let form = new FormData();
form.append("file", getImageStream([IMAGE URL]));
const requestURL = "https://api.foursquare.com/v2/photos/add";
const requestParams = {
checkinId: [MY CHECKIN ID],
public: 1,
oauth_token: [MY OAUTH TOKEN],
v: [MY VERSION]
};
const requestConfig = {
headers: form.getHeaders()
};
try {
const postPhoto = await axios.post(
requestURL + "?" + querystring.stringify(requestParams),
form,
requestConfig
);
return postPhoto;
} catch (e) {
console.error(e.response);
}
And voila, the request succeeds and the image is posted to the Foursquare checkin.
I'm creating a React app with Firebase for basic backend functionality.
I'm trying to use the Yelp API but am always getting a MISSING_PARAMETER and CORS errors.
Here is my code:
import OAuth from 'oauth-1.0a';
const oauth = OAuth({
consumer: {
public: 'public',
secret: 'secret'
},
signature_method: 'HMAC-SHA1'
});
const url = "https://api.yelp.com/v2/";
const token = {
public: 'public',
secret: 'secret'
};
class YelpWrapper {
static prepareSearch(term, category) {
const requestData = {
url: url + "search/?term=" + term + "&category_filter=" + category + encodeURIComponent("&location=Los Angeles, CA"),
method: "POST"
};
return {
requestData: requestData,
headers: oauth.toHeader(oauth.authorize(requestData, token))
};
}
}
export default YelpWrapper;
and this is called in
static addMemory(memory) {
const { id, category, text, name } = memory;
newMemoryRef(id).set(memory);
const search = YelpWrapper.prepareSearch(name, category);
const { url, method } = search.requestData;
const { headers } = search;
request({
url: url,
method: method,
headers: headers
}, (error, response, body) => {
console.log(error);
console.log(response);
console.log(body);
});
}
Here is the missing parameter error I'm getting
{"error": {"text": "One or more parameters are missing in request", "id": "MISSING_PARAMETER", "field": "oauth_consumer_key"}}
Although when I print the headers I can see all the required headers defined in there, for some reason they don't appear in the Request Headers of Chrome Dev Tools. I think that's the reason, but I can't figure out why that is.