I've been trying to practice with some HTTP requests, and specifically I want to send a POST request, with data taken from an field, and sent via fetch() to a url on my localhost thats set up with express. From there i want to somehow get the response back and have that be displayed on my HTML doc.
However, I've ran into a real head scratcher when it comes to getting response.json() to be anything other than undefined.
here's my frontend script:
const url = "/result";
const inputField = document.querySelector("#write");
const submitButton = document.querySelector("#submit");
const responseField = document.querySelector("#text-goes-here");
const postText = async () => {
const text = inputField.value;
const data = JSON.stringify({ destination: text });
try {
const response = await fetch(url, {
method: "POST",
body: data,
headers: {
"Content-type": "application/json",
},
});
if (response.ok === true) {
const jsonResponse = await response.json();
responseField.innerHTML = jsonResponse;
}
} catch (error) {
console.log(error);
}
};
const displayText = (event) => {
event.preventDefault();
while (responseField.firstChild) {
responseField.removeChild(responseField.firstChild);
}
postText();
};
submitButton.addEventListener("click", displayText);
and my server script:
const express = require("express");
const bodyParser = require("body-parser");
const read = require('fs');
const router = express.Router();
const app = express();
const port = 3000;
app.use(express.static(__dirname + "/public"));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.get("/", (req, res) => {
res.sendFile("public/index.html");
})
router.post("/result", (req, res) => {
console.log(req.body);
res.send();
});
app.use("/", router);
app.listen(port, () => {
console.log(`Server running at port: ${port}`)
});
I did some digging in the dev console and found that (response.ok) is in fact "true", yet it errors out into the catch statement saying "SyntaxError: Unexpected end of JSON input
at postText (script.js:23)"
which is this line exactly:
const jsonResponse = await response.json();
can anyone shed any light on what i'm doing wrong here? I'm at a loss at this point
This error means that you're trying to parse something that is not a valid JSON object.
"SyntaxError: Unexpected end of JSON input at postText (script.js:23)"
Which is true, because the response you're sending back to the frontend is not a JSON.
router.post("/result", (req, res) => {
console.log(req.body);
// The response is not a valid JSON object
res.send();
});
You can change res.send() to res.json() and give it a valid object.
res.json({ name:"John", age:30, car:null })
Related
I am using express to get data from a public API and use the data in my front end.
This is my characters route which works fine on certain Public API URLs, but most that I try ends up in an unexpected end of input error.
I am also getting an
Unexpected token , in JSON at position 48. How can this happen when it appears to be valid JSON?
const express = require('express'); // Web Framework
const https = require('https');
const router = express.Router();
const api = 'https://www.cheapshark.com/api/1.0/games?title=batman&steamAppID=35140&limit=60&exact=0';
router.get("/", function(req, res) {
https.get(api, (response) => {
console.log(response.statusCode);
response.on('data', (d) => {
try{
const data = JSON.parse(d);
console.log(data);
res.send(data);
} catch (err) {
console.log(err);
}
})
// res.send("Running")
})
})
module.exports = router;
This is my index.js which uses the character route
const express = require('express'); // Web Framework
const app = express();
const PORT = 3000;
const charactersRoute = require('./routes/characters');
//Characters Route
app.use('/characters', charactersRoute)
app.listen(PORT, function(err) {
if(err) console.log(err);
console.log(`Server is listening on port ${PORT}`)
})
This is the JSON I am trying to parse. I validated this is valid JSON on those JSON validation sites.
[
{
"gameID": "146",
"steamAppID": "35140",
"cheapest": "14.95",
"cheapestDealID": "LNCZ5EicmEMiwyfYVw%2FNdGPos9V7MzoPId2UuwaBqvA%3D",
"external": "Batman: Arkham Asylum Game of the Year Edition",
"internalName": "BATMANARKHAMASYLUMGAMEOFTHEYEAREDITION",
"thumb": "https://cdn.cloudflare.steamstatic.com/steam/apps/35140/capsule_sm_120.jpg?t=1634156906"
}
]
https response is a stream that returns chunks of data in the data event, so you need to concatenate data there (store buffer raw data into an array), and parse it in the .end event, when the response is finished, and then you can use res.json to send it to the consumer:
try this:
const express = require('express'); // Web Framework
const https = require('https');
const router = express.Router();
const api = 'https://www.cheapshark.com/api/1.0/games?title=batman&steamAppID=35140&limit=60&exact=0';
router.get("/", function(req, res) {
https.get(api, (response) => {
console.log(response.statusCode);
const resData = [];
response.on('data', (chunk) => {
resData.push(chunk);
});
response.on('end', function() {
try {
const data = JSON.parse(Buffer.concat(resData).toString());
console.log(data);
res.json(data);
} catch (err) {
console.log(err);
}
});
})
});
module.exports = router;
I have a simple backend that I'd like to return a string of data whenever a POST request is made. The string is converted to JSON, but when I see that the response was successfully received by the client (the browser), the string is missing from the response data in the browser's console.
server.js (the request handler itself is simple, there's just a few dependencies listed)
const path = require('path');
const express = require('express');
const bp = require("body-parser");
const request = require("request");
const {createCanvas, loadImage} = require("canvas");
const cors = require('cors');
const requestIp = require('request-ip');
const { json } = require('body-parser');
const bodyParser = require('body-parser');
const wgServer = express();
const port = 3000;
const fs = require("fs");
const { createContext } = require("vm");
wgServer.use(cors())
wgServer.use(bp.json())
wgServer.use(bp.urlencoded({ extended: true }))
wgServer.use("/public", express.static(path.join(__dirname, 'public')));
wgServer.use(bodyParser.json({limit: '50mb'}))
wgServer.listen(port, function() {
console.log(`Weatherglyph server succesfully listening on port ${port}.`)
});
wgServer.post('/', async function (req, res) {
res.set('Content-Type', 'application/json');
... Lots of functions etc irrelevant to the question
let picpath = __dirname + "\\image.png";
let picbuff = fs.readFileSync(picpath);
let picbase64 = picbuff.toString('base64');
console.log(picbase64);
res.status(201).json({picbase64});
res.send();
})})
script.js
async function submitCity(){
let x = document.getElementById("wg_input").value;
console.log("Successfully captured city name:", x);
let toWeather = JSON.stringify({city: x});
console.log("Input data successfully converted to JSON string:", toWeather);
const options = {
method: 'POST',
mode: 'cors',
headers: {'Content-Type': 'application/json'},
body: toWeather
}
fetch('http://localhost:3000', options)
.then(res => console.log(res))
.catch(error => console.log(error))
}
Is the data (a .png converted to base64 and sent to the frontend) actually reaching the client and I just can't see it in Chrome's console, or am I sending it incorrectly?
I have this problem when I try to get the data from the database
TypeError: Cannot read property 'findAll' of undefined
I made the web using react and node.js + postgresql. Postgres is in our server so I'm not using a localhost. I tried some other post but anything works.
Server.js
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
var corsOptions = {
origin: "http://localhost:3000",
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(bodyParser.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// simple route
app.get("/", (req, res) => {
res.json({ message: "Welcome to bezkoder application." });
});
// set port, listen for requests
require("./routes/noticias.routes")(app);
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
controller.js
const db = require("../config/db.config");
const noticias = db.noticias;
// Retrieve all noticias from the database.
const getNews = (req, res) => {
noticias
.findAll({})
.then((data) => {
res.send(data);
})
.catch((err) => {
res.status(500).send({
message: err.message || "Some error occurred.",
});
});
};
module.exports = {
getNews,
};
Router
module.exports = (app) => {
const noticias = require("../controllers/noticias.controller.js");
var router = require("express").Router();
router.get("/", noticias.getNews);
app.use("/noticias", router);
};
Thanks for the help ^^
I don't know much about about knex. As far as I know from docs there is a select function not findAll in knex.
Docs: https://knexjs.org/#Builder-select
Please try following method and see what happens
// below line is not required;
// const noticias = datab.noticias;
// Retrieve all noticias from the database.
const getNews = (req, res) => {
datab
.select("column_name_1", "column_name_2", "...")
.from("noticias")
.then((data) => {
res.send(data);
})
.catch((err) => {
res.status(500).send({
message: err.message || "Some error occurred.",
});
});
};
PS: the findAll function is available in sequelize not knex.
Use db.select('*').from('users') to findAll
I am creating a project in DialogFlow and NodeJS where I want to call my fulfillments with a webhook.
In my NodeJS server, I have multiple routes for different functions/intents. For example, /getWeather calls a weather API to return a response about the weather in a specific city. Or /getMovie calls an API to return information about a movie.
DialogFlow only allows for one webhook API, so my question is, how can I call a generic API "/" where it can handle all the different routes and call the correct route when it needs to?
I can use the inline editor on DialogFlow to call each API with the correct route; however, I want to use a single webhook rather than using the firebase functions to call the correct intents.
I can't seem to find example of this online where multiple routes are handled with a generic route.
Image of my Code Stack
index.js:
const http = require('http');
const app = require('./app');
const port = process.env.PORT || 3000;
const server = http.createServer(app);
server.listen(port);
server.post
app.js
const express = require('express');
const app = express();
const morgan = require('morgan');
const bodyParser = require('body-parser');
const mongoose= require('mongoose');
const issuesRoutes = require('./API/Routes/issues');
const movieRoute = require('./API/Routes/getmovie');
const resolvedtaskroute = require('./API/Routes/resolvedtask');
const newtaskRoute = require('./API/Routes/newtask');
mongoose.connect('link', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB connected...'))
.catch(err => console.log(err));
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use((req, res, next) => {
res.header('Acces-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', '*');
if (req.method === 'OPTIONS'){
res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
return res.status(200).json({});
}
next();
});
//routes to handle requests
app.use('/issues', issuesRoutes);
app.use('/newtask', newtaskRoute);
app.use('/resolvedtask', resolvedtaskroute);
app.use('/getmovie', movieRoute);
//error handling
app.use((req, res, next) => {
const error = new Error('Not Found');
error.status = 404;
next(error);
})
app.use((error, req, res, next) => {
res.status(error.status || 500);
res.json({
error: {
message: error.message
}
})
})
module.exports = app;
Example of one of my routes: getMovie.js
const express = require('express');
const router = express.Router();
const http = require('http');
router.post('/', (req, res, next) => {
const movieToSearch = req.body.queryResult.parameters.movie;
const API_KEY = 'XXXXX';
const reqUrl = `http://www.omdbapi.com/?t=${movieToSearch}&apikey=${API_KEY}`
http.get(
reqUrl,
responseFromAPI => {
let completeResponse = ''
responseFromAPI.on('data', chunk => {
completeResponse += chunk
})
responseFromAPI.on('end', () => {
const movie = JSON.parse(completeResponse)
let dataToSend = movieToSearch
dataToSend = `${movie.Title} was released in the year ${movie.Year}. It is directed by ${
movie.Director
} and stars ${movie.Actors}.
}`
return res.json({
fulfillmentText: dataToSend,
source: 'getmovie'
})
})
},
error => {
return res.json({
fulfillmentText: 'Could not get results at this time',
source: 'getmovie'
})
}
)
})
module.exports = router;
It is very clear that Dialogflow allows one webhook POST url where every call for intents are made. IF you want to use different API services inside then You should define a webhook and inside the webhook just call the functions which are related to intents using intentMAP. On each function call the external API and return the response back to dialogflow. I will describe a bit more about it using dialogflow-fulfillment.
first thing you need is a webhook POST route for handling dialogflow requests and responses and inside it you need to map intents to its specific function as like:
const { WebhookClient } = require("dialogflow-fulfillment");
const movieService= require("your function for movie API");
router.post("/", async (req, res, next) => {
const agent = new WebhookClient({ request: req, response: res });
const movie = new movieService(agent);
let intentMap = new Map();
intentMap.set("Movie Intent", () => {
//make an api call inside this function
return movie.getinfo();
});
if (agent.intent) {
agent.handleRequest(intentMap);
}
});
Now create another file for external API calls which will be like
async getMovie(){
// get all required paramters from dialogflow here and call APIS and return back response using
agent.add("The info about movie is");
}
I am little bit confused and need some help.
I write an HTTP server using Node.js, and make an HTTP request from Vue.js to the HTTP server. Somehow it always return error like this:
Error: Request failed with status code 404
at FtD3.t.exports (createError.js:16)
at t.exports (settle.js:18)
at XMLHttpRequest.f.(:3010/anonymous function) (http://localhost:3010/static/js/vendor.1dc24385e2ad03071ff8.js:1312:88758)
It seems like url address don't correct cause error is 404 in browser. I check url address several times but did't notice something wrong. What I miss?
P.S. The main task to load file from remote sftp server from website. I use to that task ssh2-sftp-client library as backend side.
When user click the button, application run getFile function where we send post request to HTTP server.
Code inside Vue.js component:
getFile (fileName) {
axios.post('http://localhost:3010/csv', {file_name: fileName}, {headers: {'Authorization': this.token}}).then(response => {
console.log(response)
this.showAlert('You download file successfully.', 'is-success', 'is-top')
}).catch((error) => {
console.log(error)
this.showAlert(error, 'is-danger', 'is-bottom')
})
}
app.js:
const express = require('express');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const cors = require('cors');
const path = require('path');
const bodyParser = require('body-parser');
const csvRouter = require('./server/routes/csv')
const app = express();
app.use(cors());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'dist')));
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use('/csv', csvRouter);
module.exports = app;
routers/csv.js:
const express = require('express')
const router = express.Router()
const csvControllers = require('../controllers/csv')
router.get('/', csvControllers.getFile)
module.exports = router
controllers/csv.js:
const request = require('request')
const queryString = require('query-string')
let Client = require('ssh2-sftp-client')
let sftp = new Client()
const config = require('../config')
exports.getFile = (req, res) => {
console.log(req) // In console I don't notice nothing.
let data = {
file_name: req.query.file_name
}
let options = {
method: 'port',
json: true,
header: {'Authorization': req.header.token},
url: `http://localhost:3010/csv?` + queryString.stringify(data)
}
request(options, (error, response) => {
console.log('Message') // In console I don't notice nothing.
if (response) {
sftp.connect(config.sftpServer).then(() => {
return sftp.get('/reports/' + data.file_name)
}).then((chunk) => {
console.log(chunk)
}).catch((err) => {
console.log(err)
})
} else {
response.status(500).send(error)
}
})
}
It seems that app.listen(port) is missing in your app.js file:
app.listen(3000)
https://expressjs.com/en/starter/hello-world.html
In controllers/csv.js you never send a response. You should have a res.send or res.render or res.json somewhere.