Getting invalid JSON on API call - javascript

I'm trying to use GoToMeeting's API and making a POST request to create a meeting. At the moment, I'm just trying to hardcode the body of the meeting and send headers but I'm receiving and I'm invalid JSON error and not sure why. Here's the code for that route:
app.post('/new-meeting', (req, res) => {
const headers = {
'Content-Type': 'application/json',
Accept: 'application / json',
Authorization: 'OAuth oauth_token=' + originalToken
};
console.log('-----------------------------------------------------------')
console.log('Acess Token:');
console.log('OAuth oauth_token=' + originalToken);
console.log('-----------------------------------------------------------')
const meetingBody = {
subject: 'string',
starttime: '2018-03-20T08:15:30-05:00',
endtime: '2018-03-20T09:15:30-05:00',
passwordrequired: true,
conferencecallinfo: 'string',
timezonekey: 'string',
meetingtype: 'immediate'
};
return fetch('https://api.getgo.com/G2M/rest/meetings', {
method: 'POST',
body: meetingBody,
headers: headers
}).then(response => {
console.log('response:');
console.log(response);
response
.json()
.then(json => {
res.send(json);
console.log(req.headers);
})
.catch(err => {
console.log(err);
});
});
});
When I hit that router, I get the following error:
{
"error": {
"resource": "/rest/meetings",
"message": "invalid json"
}
}
Any advice would be appreciated!

tl;dr
You are passing fetch a value for the body represented by a JavaScript object. It is converting it to a string by (implicitly) calling its .toString() method. This doesn't give you JSON. The API you are calling then complains and tells you that it isn't JSON.
You need to convert your object to JSON using:
body: JSON.stringify(meetingBody),
Test case
This demonstrates the problem and the solution.
Server
This is designed to be a very primitive and incomplete mock of GoToMeeting's API. It just echos back the request body.
const express = require("express");
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.text({ type: "*/*" }));
app.post("/", (req, res) => {
console.log(req.body);
res.send(req.body)
});
app.listen(7070, () => console.log('Example app listening on port 7070!'))
Client
This represents your code, but with the Express server stripped out. Only the code relevant for sending the request to GoToMeeting's API is preserved.
const url = "http://localhost:7070/";
const fetch = require("node-fetch");
const headers = {
'Content-Type': 'application/json',
Accept: 'application / json',
Authorization: 'OAuth oauth_token=foobarbaz'
};
const meetingBody = {
subject: 'string',
starttime: '2018-03-20T08:15:30-05:00',
endtime: '2018-03-20T09:15:30-05:00',
passwordrequired: true,
conferencecallinfo: 'string',
timezonekey: 'string',
meetingtype: 'immediate'
};
fetch(url, {
method: 'POST',
body: meetingBody,
headers: headers
})
.then(res => res.text())
.then(body => console.log(body));
Results of running the test case
The logs of both server and client show:
[object Object]
This is what you get when you call meetingBody.toString().
If you change the code as described at the top of this answer, you get:
{"subject":"string","starttime":"2018-03-20T08:15:30-05:00","endtime":"2018-03-20T09:15:30-05:00","passwordrequired":true,"conferencecallinfo":"string","timezonekey":"string","meetingtype":"immediate"}
This is JSON, which is what the API is expecting.
Aside
MIME types do not have spaces in them. Accept: 'application / json', should be Accept: 'application/json',. This probably isn't causing you any problems though.

I believe the header is incorrect.
You need 'Accept: application/json' without space.

Related

How to send data from React to Node?

I am really new to node and wanted to know how to send some data from my frontend using react to my backend (Node JS).I want to send some string to my backend,is this the process or is it a completely different thing?
useEffect(() => {
fetch("/api")
.then((res) => res.json())
.then((data) => setData(data.message));
}, []);
index.js file
// server/index.js
const express = require("express");
const PORT = process.env.PORT || 3001;
const app = express();
app.get("/api", (req, res) => {
const tmp=req.body;
res.json({ message: tmp });
});
app.listen(PORT, () => {
console.log(`Server listening on ${PORT}`);
});
Your /api route is listening to GET requests. GET requests can't contain body, therefore you won't be receiving anything inside the body.
If you want to pass data with get request, you can either use query parameters or URL parameters. Passing query params would be something like,
fetch('/api?' + new URLSearchParams({
message: 'message',
}))
To receive this from backend and use it as a response, you can access the query parameters like below using req.query,
app.get('/api', function(req, res) {
res.json({
message: req.query.message
});
});
You can also send data using URL parameters with GET request, instead of using query parameters.
I suggest taking a deeper look at HTTP requests.
you need to use post method, here is the client side using fetch api(from mdn docs):
// Example POST method implementation:
async function postData(url = '', data = {}) {
// Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.json(); // parses JSON response into native JavaScript objects
}
postData('https://example.com/answer', { answer: 42 })
.then(data => {
console.log(data); // JSON data parsed by `data.json()` call
});
and for backend, you can handle it this way (from express docs):
const express = require("express");
const bodyParser = require("body-parser");
const router = express.Router();
const app = express();
//Here we are configuring express to use body-parser as middle-ware.
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
router.post(‘/handle’,(request,response) => {
//code to perform particular action.
//To access POST variable use req.body()methods.
const {answer} = request.body;
res.json({answer});
});
// add router in the Express app.
app.use("/", router);

SyntaxError: Unexpected token " in JSON at position 0

I have an error with request to express. I have this fetch:
fetch(`http://localhost:4200/dist/js/server.min.js`, {
method: "POST",
// mode: 'no-cors',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(`<html><body><div style="background-color: yellow;"><p>Hello World!</p></div></body></html>`),
}).then((response) => {
console.log(response)
})
And I have such a code for my express server:
const { exec } = require("child_process"),
express = require("express"),
bodyParser = require('body-parser'),
webshot = require('webshot'),
PORT = 4200,
app = express(),
cors = require('cors')
// app.use(cors())
app.use((req, res, next) => {
res.append('Access-Control-Allow-Origin', 'http://localhost:4242');
res.append('Access-Control-Allow-Methods', 'POST', 'GET', 'OPTIONS');
res.append('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With');
res.append('Access-Control-Allow-Credentials', 'true');
next();
});
// app.use(express.json());
app.use(bodyParser.json());
app.get('/dist/js/server.min.js', (req, res) => {
res.send('<h1>Hello</h1>')
})
app.post('/', function(req, res) {
htmlData = req.body
screen(htmlData) // just my function
});
app.listen(PORT, () => {
console.log(`What's my age again? ${PORT}, I guess.`)
});
And I've got this error in browser:
POST http://localhost:4200/dist/js/server.min.js 400 (Bad Request)
And this in console:
SyntaxError: Unexpected token " in JSON at position 0
at JSON.parse (<anonymous>)
at createStrictSyntaxError (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/lib/types/json.js:158:10)
at parse (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/lib/types/json.js:83:15)
at /home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/lib/read.js:121:18
at invokeCallback (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/node_modules/raw-body/index.js:224:16)
at done (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/node_modules/raw-body/index.js:213:7)
at IncomingMessage.onEnd (/home/joe/Documents/vscode-projects/html-projects/swipeskins/node_modules/body-parser/node_modules/raw-body/index.js:273:7)
at IncomingMessage.emit (events.js:198:15)
at endReadableNT (_stream_readable.js:1139:12)
at processTicksAndRejections (internal/process/task_queues.js:81:17)
I guess, server has problems with parsing json data. But why? What is wrong with code?
Thanks a lot for your time, I would be very grateful to hear something from you if you have some thoughts about my situation.
Your JSON's top level data type is a string:
JSON.stringify(`<html>...</html>`);
The current version of the JSON specification allows the top level data type in the JSON text to be any JSON data type.
The original version only allowed an object or an array.
The error message says that having " as the first character is an error, which implies that it doesn't support strings as the top level data type (and thus implements the original specification).
Either change the structure of the JSON so it starts with an object or an array:
{
"html": "<html>...</html>"
}
or change the data format you are sending:
"Content-Type: text/html"
and
body: "<html>...</html>" // without JSON.stringify
Obviously, you'll need to change the server-side code to accept the changed format.
The problem is that the express JSON body parser by default only accepts inputs that can be parsed as JSON objects. However, you can change it to accept other types of valid JSON data (including strings, like in the OP) by disabling "strict" parsing:
app.use(express.json({strict: false}));
This is the workaround the worked for me.
Reference: https://github.com/expressjs/express/issues/1725#issuecomment-22844485
On request body you are not parsing a DOM element, so you can do the following:
const parser = new DOMParser();
const raw = '<html><body><div style="background-color: yellow;"><p>Hello World!</p></div></body></html>';
const body = parser.parseFromString(raw, 'text/html');
fetch(`http://localhost:4200/dist/js/server.min.js`, {
method: "POST",
// mode: 'no-cors', // If you use 'no-cors' you will not get response body and some headers
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
}).then((response) => {
console.log(response);
})

JSON is becoming empty upon hitting server code

I am sending a fetch request with a JSON payload from my webpage like this:
let raw = JSON.stringify({"name":"James","favourite":"books"})
var requestOptions = {
method: 'POST',
body: raw
};
let send = () => {
fetch("http://mywebsite.herokuapp.com/send", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
On the server side I am getting an empty body {}. Here is the code I use to monitor that:
app.post('/send', (req, res) => {
console.log(req.body)
})
When I send the exact same code generated with Postman to server — somehow everything works fine, and I get the correct JSON. Please help me understand why that is.
On the server, req.body will be empty until you have middleware that matches the content type in the POST and can then read the body from the response stream and populate req.body with the results.
// middleware to read and parse JSON bodies
app.use(express.json());
app.post('/send', (req, res) => {
console.log(req.body);
res.send("ok");
});
And, then on the client side, you have to set the matching content-type, so the server-side middleware can match the content-type and read and parse it:
const requestOptions = {
method: 'POST',
body: raw,
headers: {
'Content-Type': 'application/json'
},
};

Proxying an endpoint to avoid CORS issues

I am using an external api that doesn't allow client side POST request. I can make a POST request using node.js and I am getting my desired response on the server. I am stuck trying to figure out how to get the response from the server into my HTML file.
const https = require("https");
const data = JSON.stringify({
key: "value"
});
const options = {
hostname: "url",
port: 443,
path: "/path",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": data.length
}
};
const req = https.request(options, res => {
console.log(
`statusCode: ${res.statusCode} statusMessage: ${res.statusMessage}`
);
res.setEncoding("utf8");
res.on("data", chunk => {
console.log(chunk);
});
});
req.on("error", error => {
console.error(error);
});
req.write(data)
req.end();
This is my server.js file, I'm not sure what the next step is to get in a file.

Response is undefined via ajax client but proper tru url

I'm setting up an ajax client with a dummy server (just to test). I've managed to resolve the cors issue it seems, but the response as seen from the ajax client is undefined. When I get at the same URL using just a browser thought, it displays the object properly.
// server-side
var express = require('express');
var router = express.Router();
var cors = require('cors');
router.use(cors());
var data = [
{"id": 1, "message": 'first object'},
{"id": 2, "message": 'second object'},
{"id": 3, "message": 'third object'}
];
router.get('/', (req, res, next) => {
console.log("building response body");
res.json(data);
});
// client-side
function fetcher() {
console.log("fetch from:" + rootUrl + arrayEndpoint);
fetch(rootUrl + arrayEndpoint, {
mode: "cors",
method: "GET",
headers: {
"Content-Type": "application/json"
}
})
.then((response) => {
console.log(response);
console.log("response: " + response.body);
})
.catch((error) => {
console.log("error: " + error);
});
}
The response printed to the console of the client:
Response { type: "cors", url: "https://angry-badger-63.localtunnel.me/getArray/", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, bodyUsed: false }
undefined
And on the browser:
[{"id":1,"message":"first object"},{"id":2,"message":"second object"},{"id":3,"message":"third object"}]
So I'm pretty sure my server-side is fine (it does send what I expect to the browser and how complicated can resp.json(object) really be?), but apparently something about the ajax client I'm not seein. What's wrong with it?
Taking into account SLak's comment, fetch doesn't have resp.body, which I sort just had assumed. Huge blind spot there.
That revealed another issue - which was that resp.json() (which would be the actual way to handle it) returns a promise, which I wasn't handling. Actually it seems the parsing of the response (whether with .json() or with .text()) returns a promise in general. I'm still not really getting the array proper, but to keep things on topic here's a corrected snippet to parse a generic json object:
//client.js
function fetcher() {
console.log("fetch from:" + rootUrl + arrayEndpoint);
fetch(rootUrl + arrayEndpoint,{
mode: "cors",
method: "GET",
headers: {"Content-Type": "application/json"}
})
.then(function(response) {
response.json()
.then(function (data) {
console.log(data);
});
})
.catch(error => console.log("error:" + error));
}
//server.js
var express = require('express');
var router = express.Router();
var cors = require('cors');
/* GET users listing. */
router.use(cors());
var data = {"1":{"id":1, "message": 'first object'},
"2":{"id":2, "message": 'second object'},
"3":{"id":3, "message": 'third object'}};
router.get('/', function(req, res, next) {
console.log("building response body")
console.log(data)
res.json(data);
});

Categories