Node.js + Sending API Request to Third Party Service - javascript

I have a Node console app. In my app, I am trying to send some data to a third-party web service. I can successfully add data via the third-party web service via POSTMAN. In my Postman, I have a request setup like this:
Headers
api-key
{my key}
Content-Type
application/json
Body
{
"value": [
{
"#operation": "send",
"id":"1",
"name":"Hello",
"position": { "type": "Point", "coordinates": [90.000000, 0.000000] },
"tags":["january", "friends"]
}
]
}
I am now trying to replicate sending this data via Node. In an attempt to do this, I've written the following:
var ops = {
host: 'example.com',
path: '/api/upload?version=2',
method: 'POST',
headers: {
'api-key':'[my key]',
'Content-Type': 'application/json'
}
};
var r = https.request(ops, (res) => {
res.on('data', (d) => {
console.log(res.statusCode);
console.log(res.statusMessage);
});
});
var data = {
"value": [
req.body.record // req is passed in via express
]
};
console.log(data);
r.write(JSON.stringify(data));
r.end();
When the line console.log(data); is executed, I see the following in the console window:
{ value:
[ { '#operation': 'send',
id: '1',
name: 'Hello',
position: [Object],
tags:[Object]
} ] }
I'm not sure if this is a printing problem or an actual problem. Either way, my real issue is that when I send my request, I see the following also printed in the console window:
400
Bad Request
That data looks correct. I don't understand why I'm getting a 400 when I try to send from Node. Yet, it works just fine from Postman. Does it have to do with the quotation marks? I thought JSON.stringify handled that.
What am I doing wrong?

Honestly, unless you have a really good reason not to, just use something like request. It's way easier/cleaner than trying to build it yourself.
I can't answer any more specifically without knowing what the API you're talking to is expecting.
Also, in your res.on('data'), just console.log(req); and see what else is hiding in there; it might help you solve this with your existing code.

I would try
console.log(JSON.stringify(data));
Instead of
console.log(data);
and then decide what is wrong. This will give me full data in way it is sent to server.
Update:
I have made quick experiment and received following output:
{"value":[{"#operation":"send","id":"1","name":"Hello","position":{"type":"Point","coordinates":[90,0]},"tags":["january","friends"]}]}
The difference with the original JSON is in way how the coordinates are sent to the web service (as integers), which can be your problem.

Related

Post request to JSON server through FETCH api refreshes the page

I am trying to send POST requests through fetch API to JSON-server. Function is called on a simple button click (type 'button', not 'submit'). When I replace POST request with GET request everything works like it supposed to, but with POST I have a problem. Request passes, on the JSON-server entity gets created but keeps refreshing the page after each request. Also, I don't have a response from JSON-server, google chrome says 'Failed to load response data'.
Where I'm making a mistake?
const comment = {
text: "test comment",
article_id: 3
};
console.log(JSON.stringify(comment));
const options = {
method: 'post',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(comment)
}
fetch(`${URL_COMMENTS}`, options)
.then(response => { return response.json() })
.then(data => {
console.log(data)
});
If you use Live Server extension, try disabling that and try again.
Check out for Json sever port number running on your machine
attach the html form code
So we can try it on oru local machine to reproduce the issue.... Which help us to resolve the issue easy

How to read parameters of Azure function HTTP request and respond with a file from blob storage?

I'm building an Azure Function app and have had some issues trying to proxy requests (for data capture purposes) and still respond with the requested files. Essentially what I'm trying to accomplish is:
Client requests file via GET parameter on an Azure Function endpoint (not its blob storage location)
Function logs some metadata to table storage (e.g. IP address, timestamp, file name, etc.)
Function locates the desired file in blob storage and forwards it to the client as if Step 2 didn't occur
I've tried an approach using Q (outlined here) with no luck, and I haven't been able to narrow the issue down (beyond standard 500 errors).
The tutorial above basically goes as follows:
const rawFile = await q.nfcall(fs.readFile, blobUrl);
const fileBuffer = Buffer.from(rawFile, ‘base64’);
context.res = {
status: 202,
body: fileBuffer,
headers: {
"Content-Disposition": "attachment; examplefile.mp3;"
}
};
context.done();
I've hit a bit of a wall and I'm struggling to find a solution to what I thought would be a common problem (i.e. simply logging download metadata to a table). I'm new to Azure and so far I'm finding things a bit tedious... Is there a simple way of doing this?
Edit: Based on the response I get from context.bindings.myFile.length I think I've been able to retrieve a file from blob storage, but I haven't yet been able to send it back in the response. I've tried the following:
context.res = {
status: 202,
body: context.bindings.myFile,
headers: {
'Content-Type': 'audio/mpeg',
'Content-Disposition': 'attachment;filename=' + fileName,
'Content-Length': context.bindings.myFile.length
}
};
Edit 2: I think this is pretty much solved - I overlooked the methods and route in the HTTP input part of the answer below. Looks like I'm able to dynamically retrieve blobs based on the GET request and use them as inputs, and I've been able to get them sent back to the client as well. My HTTP response now looks like this:
context.res = {
status: 200,
headers: {
'Content-Length': context.bindings.myFile.length,
'Content-Type': 'audio/mpeg'
},
body: context.bindings.myFile,
isRaw: true
};
Let me paraphrase what you asked:
You want just to retrieve blob storage object upon http request.
I think it is worth looking into bindings. They simplify the integration with other Azure services - Storage Accounts, Service Bus, Twilio - and Function Apps.
In your case it should be an input binding for blob storage. As one way to achieve it you need to customize your route in the http trigger part of function.json as follows: file/{fileName}. Then you use fileName in input binding definition in the same function.json.
I think function.json should like this:
{
"bindings": [
{
"type": "httpTrigger",
"name": "req",
"direction": "in",
"methods": [ "get" ],
"route": "file/{fileName}"
},
{
"name": "myFile",
"type": "blob",
"path": "your-container-name/{fileName}",
"connection": "MyStorageConnectionAppSetting",
"direction": "in"
},
{
"type": "http",
"name": "res",
"direction": "out"
}
]
}
With your index.js as follows:
module.exports = function(context, req) {
context.log('Node.js Queue trigger function processed', context.bindings.myFile);
const fileToReturn = context.bindings.myFile;
// you return it here with context.res = ...
context.done();
};
You also superficially mentioned the logging that is not part of your question but is mentioned. I recommend looking into Azure Application Insights. It might serve the purpose.

Uncaught (in promise) SyntaxError: Unexpected end of JSON input

I am trying to send a new push subscription to my server but am encountering an error "Uncaught (in promise) SyntaxError: Unexpected end of JSON input" and the console says it's in my index page at line 1, which obviously is not the case.
The function where I suspect the problem occurring (because error is not thrown when I comment it out) is sendSubscriptionToBackEnd(subscription) which is called in the following:
function updateSubscriptionOnServer(subscription) {
const subscriptionJson = document.querySelector('.js-subscription-json');
const subscriptionDetails = document.querySelector('.js-subscription-details');
if (subscription) {
subscriptionJson.textContent = JSON.stringify(subscription);
sendSubscriptionToBackEnd(subscription);
subscriptionDetails.classList.remove('is-invisible');
} else {
subscriptionDetails.classList.add('is-invisible');
}
}
The function itself (which precedes the above function):
function sendSubscriptionToBackEnd(subscription) {
return fetch('/path/to/app/savesub.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
})
.then(function(response) {
if (!response.ok) {
throw new Error('Bad status code from server.');
}
return response.json();
})
.then(function(responseData) {
if (!(responseData.data && responseData.data.success)) {
throw new Error('Bad response from server.');
}
});
}
I have tried replacing single quotes with double quotes in the fetch call but that yields the same results.
I know that the JSON should be populated because it prints to the screen in the updateSubscriptionOnServer() function with subscriptionJson.textContent = JSON.stringify(subscription);, and I used that output in the google codelab's example server to receive a push successfully.
EDIT: Here is the JSON as a string, but I don't see a mistake in syntax:
{"endpoint":"https://fcm.googleapis.com/fcm/send/dLmthm1wZuc:APA91bGULRezL7SzZKywF2wiS50hXNaLqjJxJ869y8wiWLA3Y_1pHqTI458VIhJZkyOsRMO2xBS77erpmKUp-Tg0sMkYHkuUJCI8wEid1jMESeO2ExjNhNC9OS1DQT2j05BaRgckFbCN","keys":{"p256dh":"BBz2c7S5uiKR-SE2fYJrjPaxuAiFiLogxsJbl8S1A_fQrOEH4_LQjp8qocIxOFEicpcf4PHZksAtA8zKJG9pMzs=","auth":"VOHh5P-1ZTupRXTMs4VhlQ=="}}
Any ideas??
This might be a problem with the endpoint not passing the appropriate parameters in the response's header.
In Chrome's console, inside the Network tab, check the headers sent by the endpoint and it should contain this:
Example of proper response to allow requests from localhost and cross domains requests
Ask the API developer to include this in the headers:
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true
This happened to me also when I was running a server with Express.js and using Brave browser. In my case it was the CORs problem. I did the following and it solved the problem in my case:
(since this is an Express framework, I am using app.get)
-on the server side:
res.set({
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
});
-on client side I used Fetch to get data but disabled the CORS option
// mode: "no-cors" //disabled this in Fetch
That took care of my issues with fetching data with Express
This can be because you're not sending any JSON from the server
OR
This can be because you're sending invalid JSON.
Your code might look like
res.end();
One of the pitfalls is that returned data that is not a JSON but just a plain text payload regardless of headers set. I.e. sending out in Express via something like
res.send({a: "b"});
rather than
res.json({a: "b"});
would return this confusing error. Not easy to detect in network activity as it looks quite legit.
For someone looking here later. I received this error not because of my headers but because I was not recursively appending the response body to a string to JSON.parse later.
As per the MDN example (I've taken out some parts of their example not immediately relevant):
reader.read().then(function processText({ done, value }) {
if (done) {
console.log("Stream complete");
return;
}
result += chunk;
return reader.read().then(processText);
});
For my issue I had to
Use a named function (not an anonymous ()=>{}) inside the .then
Append the result together recursively.
Once done is true execute something else on the total appended result
Just in case this is helpful for you in the future and your issue is not header related, but related to the done value not being true with the initial JSON stream response.
I know this question has already been answered but just thought I add my thoughts.
This will happen when your response body is empty and response.json() is expecting a JSON string. Make sure that your API is returning a response body in JSON format if must be.

paypal express checkout - 400 bad request

I'm using this instruction https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/advanced-integration/ to set up paypal. The only different is that I need to send data to CREATE_PAYMENT_URL endpoint, so I transfer it as second parameter
paypal.request.post(
CREATE_PAYMENT_URL,
JSON.stringify([{"name": "test", "price": 10}]),
{ headers: { 'Content-Type': 'application/json' } }
)
after that I'm getting 400 error
{"success":false,"errors":{"statuscode":400,"message":"Invalid json message received"}}
Backend doesn't matter, cause it's never reached. Also I'm almost sure it worked fine few days ago
Any ideas?
Fiddler screen
Try setting the second param to null, and send an options object as the third param with an object named json in the third with a plain JS object inside.
paypal.request.post(PAYMENT_URL, null, {
json : {
name:'test',
price:10
}
})
It's not in the documentation, I was having similar issues but dug around in the source code to get it to work.

Express.js: API URL and res.json, using request package from npm

I'm at wits end trying to figure out how to get my API to return a valid JSON object.
I'm currently using the following code to send a response:
res.json({ foo: 'bar', secret: 'code' });
I have also tried:
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({ foo: 'bar', secret: 'code' }));
Both of the above methods work when I visit the API URL in browser. I get the expected:
{
"foo": "bar",
"secret": "code"
}
On the other hand, I'm having trouble parsing the JSON response from URL (the example below uses the request package from npm):
request({
url: API-URL,
json: true
}, function (error, response, body) {
if (!error) {
console.log(body);
} else {
console.log(error);
}
})
Using the above code, I get the following error response:
{
"code": "ENOTFOUND",
"errno": "ENOTFOUND",
"syscall": "getaddrinfo",
"hostname": <<Redacted>>,
"host": <<Redacted>>,
"port": 443
}
I've tried plugging in third-party API URLs and they work just fine (ie: Google Maps, and OpenWeatherAPI) so I'm pretty sure that I'm using request properly. I'm guessing there is a step I missed in setting up the API (or I'm doing something wrong)?
Testing my API URL in https://jsonformatter.curiousconcept.com/, the formatted JSON object shows up but I also get the following regardless of how I set the response header and encoding:
Error: Invalid encoding, expecting UTF-8, UTF-16 or UTF-32.[Code 29, Structure 0]
Any suggestions would be greatly appreciated!
Edit:
The above is in a live web app and Express works properly otherwise (everything else in the application works fine, the JSON response just isn't playing nicely with request or jQuery.

Categories