I built a GraphQL server as follows,
import express from 'express';
import graphqlHTTP from 'express-graphql';
import { schema } from './data/schema';
const app = express();
app.use(express.static(__dirname + '/public'));
app.get('/', function(req, res) {
res.sendFile('index.html');
});
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true
}));
app.listen(8081, () => {
console.log('Running server on port localhost:8081/graphql');
});
And I can make a POST call from Postman like below,
However, when I try to call the API with fetch in the app.js file which is loaded in the index.html as follows,
function fetchQuery(query) {
return fetch('/graphql', {
method: 'POST',
header: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query })
}).then(response => {
return response.json();
});
}
const query = `{
friend {
firstName
}
}`;
fetchQuery(query).then((data) => {
console.log(data);
});
It says the following errors,
app.js:2 POST http://localhost:8081/graphql 400 (Bad Request)
and response error message: "Must provide query string."
Headers should be passed in by providing a headers property in the options object, not header.
headers: {
'Content-Type': 'application/json',
},
The content type for the request is necessary for body-parser to know how to correctly parse the body. Without the header, body ends up an empty object and therefore req.body.query is undefined, which is why you see that error.
Related
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);
I create an app in react. I am trying to use fetch with a post to a different port of localhost. I received the req on the server, but my body is empty. Why my body is empty? I don't understand.
Code in React function:
export default function Sending() {
async function handleSubmit(e) {
e.preventDefault()
try{
let result = await fetch('http://localhost:5000',{
method: 'post',
mode: 'no-cors',
headers: {
'Accept': 'application/json',
'Content-type': 'application/json',
},
body: JSON.stringify({ email: 'example#gmail.com' })
})
console.log(result)
} catch (error){
console.log(error)
}
}
return (
<>
Have a Form here
</>
)
}
the console log of browser:
Response {type: "opaque", url: "", redirected: false, status: 0, ok: false, …}
body: null
bodyUsed: false
headers:
Headers {}
ok: false
redirected: false
status: 0
statusText: ""
type: "opaque"
url: ""
__proto__: Response
my simple server hold on node.js:
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const port = process.env.PORT || 5000
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.post('/', (req, res) => {
console.log(req.body)
res.send("Hello")
})
app.get('/hello', (req, res) => {
res.send("Hello, Benny")
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
You are trying to post JSON
To do that you need to set a 'Content-type': 'application/json' request header
This is only allowed on a cross-origin request when you have permission from CORS (from a preflight request).
You said mode: 'no-cors', a declaration that you do not have or want permission to do anything from CORS, so the browser will silently ignore anything that needs CORS permission
The request arrives at the server without 'Content-type': 'application/json' so the body-parser doesn't know that it needs to decode the JSON
You need to:
Remove mode: 'no-cors'
Configure the server to support CORS including for preflight requests
This is most easily done with the cors module
const cors = require('cors')
const app = express()
const corsOptions = {
origin: 'http://example.com',
}
const configuredCors = cors(corsOptions);
app.options('*', configuredCors)
app.post('/', configuredCors, (req, res) => {
console.log(req.body)
res.send("Hello")
})
Consider installing cors and add it to your code such as shown bellow
const cors = require('cors')
app.use(cors())
Then with the help of morgan
const morgan = require('morgan');
Consider reading a previous similar case as shown here
Express JS is receiving an empty req.body from ReactJS
From the first glacne; you are sending over a string with the stringify. Node body-parser trys to parse the json. Try removing the stringify and just sending the object over.
Please help me with the below POST request that I'm trying to make. Below is the code snippet.
const express = require("express");
const bodyParser = require("body-parser");
//const request = require("request");
const https = require("https");
const request = require('request-promise');
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(express.static("public"));
app.get("/", function(req, res){
res.sendFile(__dirname + "/test.html");
});
const data = {
"some_header":{
"name":"xxxxx"
}
};
const jsonData = JSON.stringify(data);
console.log(jsonData);
app.post("/post", function(req, res){
const url = "xxxxxxxxxxxx";
const options = {
method: "POST",
body: data,
json: true,
headers: {
ContentType: "application/json",
Authorization: "nhgjgjhgjghjghj"
}
}
const request = https.request(url, options, function(response) {
if (response.statusCode === 200) {
// res.send("success");
console.log("success");
} else {
//res.send("Failed");
console.log("failure");
}
response.on("data", function(data) {
console.log(JSON.parse(data));
})
})
request.write(jsonData);
request.end();
});
app.listen(process.env.PORT || 3000, function() {
console.log("The app is up and running on Port 3000");
});
I'm getting 200OK response from the external server, but unable to post the data. When I logged the response data from the server, I received this success
{ require_login: true }
"Success" is the console log message. require_login: true is the response I'm getting from the server. where am I going wrong?
Try to add Basic before you api key. Also, if you are using base64, then check that original string is right one and should be something like: login:password
headers: {
ContentType: "application/json",
Authorization: "Basic BAsE64Format or api:key or login:password"
}
headers: { "Content-type": "application/json", Authorization: 'Basic ' + Buffer.from('CtB2HZwaRdGggr1g4K').toString('base64') }
enclosing the content-type with quotes and converting the API key to base 64 did the trick
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.
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?