Posting data from EJS to Node.js - node.js

I have an index.html EJS rendered by a koa/Node.js app which contains a javascript snippet to post data about the current page to the same app, to an endpoint to save in a database.
The javascript code ( an AJAX fetch POST) reaches the Node.js endpoint but doesn't transmit any data. I don't see any typo in the code.
[CORRECTION] indeed a typo with the bodyparser
# index.js
const Koa = require("koa");
const path = require("path");
const render = require("koa-ejs");
const bodyParser = require("koa-bodyparser");
const router = require("./routes/routes.js");
const app = new Koa();
render(app, {
root: path.join(__dirname, "/views"),
layout: false,
viewExt: "html",
});
app
.use(bodyParser())
.use(router.routes())
.use(router.allowedMethods())
.use(staticCache("./images", { maxAge: 600000 }))
.listen(PORT, () => {
console.log(`Running on port ${PORT}`);
});
In the index.html, I have a button that triggers a POST request to an endpoint (/node/insert) of the koaRouter. The action is to save information about the current page (say, document.location.href) in a Postgres database.
# /views/index.html
[...]
<form id="newRow">
<input type="submit" value="New row">
</form>
[...]
<script type="module" src="fetch.js" async></script>
where:
# /views/fetch.js
const data = {
app: "Node",
url: document.location.href,
...
};
document.getElementById("newRow").addEventListener("submit", (e) => {
e.preventDefault();
fetch("/node/insert", {
method: "POST",
headers: {
"Content-Type": "application/json; charset-UTF-8",
},
body: JSON.stringify(data),
})
.then((res) => {
if (res.ok) {
return res.json();
}
return Promise.reject(res);
})
.then((res) => console.log("front", res))
.catch((err) => console.warn(err));
Among the routes, I defined an endpoint /node/insert to respond to this action:
# routes.js
const koaRouter = require("koa-router");
const router = new koaRouter();
router.post("/node/insert", async (ctx) => {
console.log("posted", ctx.request.body);
^^^ "posted" is positively printed in terminal after submit
if (ctx.request.body) {
return (ctx.response.status = 200)
} else {
return (ctx.response.status = 404); <-- check
}
})
The endpoint "/node/insert" is reached since I can console.log positively, but the body isn't passed to the endpoint: ctx.request.body = {}. I have the following error:
"SyntaxError: Unexpected token O in JSON at position 0"
detected from fetch.js (probably because the body is {}?).
I don't see what is wrong.
Note: the Node app runs in a container (pm2-runtime start index.js) and use Nginx as reverse proxy, static files server and load-balancer`

Try:
const koaJson = require(koa-json);
app.use(koaJson());

Just a typo in the bodyparser as Evert pointed and bad positioning of the middleware
curl --data "app=Node" HTTP://localhost:8000/node responds normaly.

Related

Get external api with axios and req body to url for search and ajax NodeJS express

I use node.js expess with MVC pattern and use axios for get json url. The all logic request api i wrote it in Controller , I try to create some get api with axios like this.
this my chartController.js , it's some logic for get json from url with req.body.symbol (it's just symbol of stock from user submitted input form , I want to get that symbol to my axios get url in code below in const chartChartPage )
'use strict';
const axios = require('axios');
const request = require('request');
/* For Get my Api Token Data */
const dataTokens = require('../config/apitokens');
const chartChartPage = async (req,res) => {
try {
const symbol = req.body.symbol;
const url = `https://${dataTokens.sandbox}.iexapis.com/stable/stock/${symbol}/chart/1m?token=${dataTokens.tokens}`;
const fetchData = await axios.get(url);
res.status(200).json(fetchData.data);
}
catch (error){
res.status(400).send(error.message);
}
}
module.exports = {
chartPage,
chartChartPage
}
Now, i have to added some routes in my chart.js
i think i should add router.post('/', chartChartPage) for get that symbol in input when user submitted Maybe i'm wrong.
var express = require('express');
var router = express.Router();
var {chartPage , chartChartPage} = require('../controllers/chartControllers');
router.get('/', chartPage);
router.post('/', chartChartPage);
module.exports = router;
and in my chartChartPage.js (it's just js file for my template)
and use ajax to get that data from url above (with chartChartPage) to get data for build chart stock
and try to console.log that data but that's not work in console
$(function(){
chartChartPage();
});
const chartChartPage = async () => {
await $.ajax({
url: '/chart',
type: 'POST',
dataType: 'json',
success: (response) => {
if (response != null) {
console.log(response);
}
},
error: (err) => {
console.log(err);
}
});
}
and when i submitted form i got that all json data in my page, but i want when submitted it's render to same page and work in console.log in ajax get url above. How can i fix.?
enter image description here

My socket io is working but when I add new data, it's not real time, ReactJS, Express, Mongoose

I really can't find an answer in the internet so I want to ask here. My socket io works, cause I can display data from the backend on the first load of my page, but when I add new data, I still have to refresh my page so that my frontend would be updated, it's not yet real time. I use express router/rest api to add new data to the database. And I also want to ask, why I have to add { transports: ['websocket', 'polling', 'flashsocket'] } on my frontend? I see others do it without the transports thing, but when I do CORS error occurs. Thank you!
This is my React JS code on file App.js. (I didn't include the const App = () => {....}, but the state and useEffect is inside of the const App)
import io from "socket.io-client";
const socket = io("http://localhost:3001", { transports: ['websocket', 'polling', 'flashsocket'] });
const [rooms,setRooms] = useState([]);
useEffect(() => {
socket.emit("rooms");
socket.on("rooms", rooms=> {
setRooms(rooms);
})
},[])
This is my app.js (node/express)
const http = require('http').createServer(app);
const io = require("socket.io")(http);
const viewRooms = require("./events/rooms");
const onConnection = (socket) => {
viewRooms(io,socket);
}
io.on("connection",onConnection);
This is rooms.js file on my events folder
const Rooms= require("./../models/Rooms");
module.exports = (io,socket) => {
const view = () => {
Rooms.find()
.then(rooms=> {
io.emit("rooms",rooms);
})
}
socket.on("rooms",view);
}
The submit function I use to add data to the database
const submitHandle = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('name',addForm.name);
addForm.description.forEach((val,key) => {
formData.append("article[" + key + "]" + "[" + Object.keys(val) + "]",val.paragraph);
})
addForm.images.forEach(val => {
formData.append("image",val);
})
formData.append('date',addForm.date);
let token = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwNDU2ZmNhNTI3ZTdhMGEwODY0NjVjNSIsImlhdCI6MTYxODI3NzIyNX0.w6eBHJC72xo-NRPtzJ3gKu_hIY70eCk_-K3-pkO4bAc";
fetch("http://localhost:3001/rooms/upload",{
method : "POST",
body : formData,
headers : {
"Authorization" : token
}
})
.then(data => data.json())
.then(rooms=> {
console.log(rooms);
alert(rooms.message);
})
}
And this is my code on my express route (post)
const router = require("express").Router();
const Rooms = require("./../models/Rooms ");
const auth = require("./../authorization");
const passport = require("passport");
const multer = require("multer");
require("./../passport-setup");
// IMAGE DESTINATION AND FILENAME
const storage = multer.diskStorage({
destination : (req,file,cb) => {
cb(null,"public/images")
},
filename : (req,file,cb) => {
cb(null, Date.now() + "-" + file.originalname)
}
})
// upload
const upload = multer({ storage : storage});
// upload rooms
router.post("/upload",upload.array("image",10),passport.authenticate("jwt",{session : false}),auth,(req,res,next) => {
let allDescription = req.body.description;
req.body.images = req.files.map(file => {
return(
{
roomName : req.body.name,
image : "/public/" + file.filename
}
)
})
Rooms .create(req.body)
.then(rooms=> {
res.send({
message : "Rooms uploaded!",
success : true,
rooms
})
})
.catch(next);
})
For the first part of your question, we'd have to see a lot more logging info to now exactly what is going on. It seems possible that your client-side socket.emit("news"); is perhaps happening too early before your socket.io connection is fully established and perhaps before the server is ready for the incoming request. You can fully log every single socket.io event and probably determine what's going on.
And I also want to ask, why I have to add { transports: ['websocket', 'polling', 'flashsocket'] } on my frontend?
If you don't list websocket as the first transport, then socket.io starts with a few http polling requests and those http requests are subject to CORS. The webSocket transport is not subject to CORs, so if you force it to use that first, then no CORs. FYI, the http polling that socket.io uses is only really there to detect situations where the webSocket transport is not supported or is blocked. If you aren't concerned about that, then you can really just do this in the front-end:
{ transports: ['websocket'] }
Or, you could remove the transports option completely from the client and then implement support for CORs on your server so that the CORs polling request will be allowed.

How to send data from react and use the request body in express, using Axios?

I use states to hold some form data in react, and what i want is, when i submit the form in react i send a post request using axios,and i want to use the form data in express. I tried req.body.params, req.body.data, req.body general,to access the request payload and they all returned undefined. Basically I want to access general(form data) in express. How can i do that?
note: the states are named general, and oneSuggestion, they are basic string states.
note:i searched similar questions for hours but none of them solved my question. if there was a solution, i couldn't find it, please excuse me.
edit: commented data, and put the states directly inside post.
React code:
function handleSubmit(e) {
e.preventDefault();
console.log(general, suggestionsForCases);
/*
let data = {
general: general,
oneSuggestion: oneSuggestion,
};
data = JSON.stringify(data);
console.log(data);
*/
let axiosConfig = {
headers: {
"Content-Type": "application/json;",
"Access-Control-Allow-Origin": "*",
},
};
axios
.post(
"http://localhost:8000/dict/",
{ general: general, oneSuggestion: oneSuggestion },
axiosConfig
)
.then((res) => console.log("success, dictionary sent,", res))
.catch((err) => {
console.log(err.response);
});
}
function handleInput(e) {
if (e.target.name == "general") {
setGeneral(e.target.value);
console.log(general);
}
if (e.target.name == "oneSuggestion") {
setOneSuggestion(e.target.value);
console.log(oneSuggestion);
}
}
return (
<div>
<form onSubmit={handleSubmit}>
<label>
general:
<textarea name="general" onChange={handleInput}></textarea>
</label>
<label>
suggestion:
<input name="oneSuggestion" onChange={handleInput}></input>
</label>
<button type="submit">submit</button>
</form>
</div>
);
}
export default AddDictionary;
express code:
const express = require("express");
const router = express.Router();
const Dictionary = require("../database/category/dictionary");
router.use(express.json());
router.get("/", (req, res) => {
console.log("success");
res.json({ msg: "success"});
});
router.post("/", (req, res) => {
console.log(req.body.general);
res.json({
msg: "success",
});
});
module.exports = router;
Ok, I found the problem. I deleted the axiosConfig from post, based on this source , and now this is the working code:
axios
.post(
"http://localhost:8000/dict/",
{ general: general, oneSuggestion: oneSuggestion }
)
.then((res) => console.log("success, dictionary sent,", res))
.catch((err) => {
console.log(err.response);
});
thank you guys for your help.
You'll need middleware in order to parse the request and make it accessible in req.body. I've assumed you're using a version after 4.16 which introduced express.json() as middleware for this scenario. I'll update my answer if you're using an earlier version.
Example using your code as a starter:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/', (req, resp) => {
console.log(request.body.params);
});
app.listen(3000);
To explain, anything you post, aka let's say you posted the following object:
{
fruit: 'apple',
vegetable: 'onion'
}
After using the parser you'd access the posted data in req.body.fruit and req.body.vegetable.

POST http://localhost:3000/login/aa/aa 404 (Not Found)

I have an angular app and a nodejs backend server. I want to get data from my backend but when I try to connect to it with Angular HTTPClient, it says: POST http://localhost:3000/login/aa/aa 404 (Not Found).However, when I put the link manually into the browser, it works perfectly fine. Here is some code:
service.ts
addUser(user: IUser): Observable<IUser> {
return this.httpClient.post<IUser>(`http://localhost:3000/login/${user.email}/${user.passwort}`, user, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
})
.pipe(catchError(this.handleError));
}
index.js
var mysql = require('mysql');
var express = require('express');
var app = express();
const port = process.env.PORT || 3000;
[...]
app.get('/login/:email/:pw',function(req,res) {
res.setHeader('Content-Type', 'application/json');
var passwort = new Passwort(''+req.params.pw);
passwort.comparePasswort();
con.query("SELECT u.Email, u.Hash FROM User u WHERE u.Email LIKE "+ "'" + req.params.email+ "'", function(err, result ){
if(err) throw err;
console.log(result)
res.send("test")
})
});
Thanks for every answer and for your time!
Your route in your backend is set as a get request and not a post request.
You should either convert your request to a get in your service with this.httpClient.get... or convert to a post request in your backend with app.post.
The reason it works in your browser is that the browser performs a GET request when acessing something using the address bar.
In backed you declared a get method and from frontend you are calling post. your code in service should be :-
addUser(user: IUser): Observable<IUser> {
return this.httpClient.get<IUser>(`http://localhost:3000/login/${user.email}/${user.passwort}`, {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
})
.pipe(catchError(this.handleError));
}
before using /:email you need to subscribe this particular element
const mongoose = require("mongoose");
const User = mongoose.model("User");
const userParams = (req, res, next, email) => {
User.findOne({email:email})
.then((user)=> {
if (!user) {
return res.sendStatus(404);
}
req.user = user;
return next();
})
.catch(next);
};
module.exports = userParams;
then use that in express router by typing
router.param("email", userParams);
this way your router will get to know what the params you are trying to send
In your index.js file, you are creating a handler for a GET request (which is the default request sent by your browser while accessing your webpage)
But in your service.ts file you are trying to send a post request to the server which is not handled, so the simple solution would be to replace the line
return this.httpClient.post<IUser> `http://localhost:3000/login/${user.email}/${user.passwort}`, user, {
with:
return this.httpClient.get<IUser> `http://localhost:3000/login/${user.email}/${user.passwort}`, user, {
For more info you can read this: https://angular.io/guide/http

ReactJS download file from Express Server

I'm trying to make my user able to download a file from our backend server. I've tried the solution from this question as well as the backend from this.
Sadly none of them worked. The download itself works through postman, but not in react.
Additional info: The Backend is running on the same machine but on port 3001 while the frontend is running on port 3000
I'm not sure if that helps, but the react frontend is connected to the backend via proxy in the package.json
"proxy": "http://localhost:3001",
The client side currently looks like this:
const download = require("downloadjs");
const handleDownload = async () => {
const res = await fetch("http://localhost:3001/download");
const blob = await res.blob();
download(blob, "test.pdf");
}
function App() {
return (
<div className="App">
<header className="App-header">
<button onClick={() => handleDownload().finally(() => console.log("Passed through the whole handleDownload Request"))}> </button>
</header>
</div>
);
}
while on the backend side I'm using this code as from the previous questions asked here on stackoverflow.
app.get('/getdoc', function (req, res) {
res.download(path.join(__dirname, 'files/test.pdf'), function (err) {
console.log(err);
});
});
This is the Code working through Postman, but it won't trigger a download in React.
The Error occurring in react looks like this:
App.js:8 GET http://localhost:3001/download/test.pdf net::ERR_CONNECTION_REFUSED
Uncaught (in promise) TypeError: Failed to fetch
So it seems the handling on frontend seems to be the problem, as it is not triggering the Save Dialog from the browser (Chrome).
Your request to postman will work because I assume you are hitting the correct endpoint which is '/getdoc' which will let you download the pdf via postman.
However, your fetch request does not seem to match the API endpoint that serves the pdf document. That is why your React Component will give you the error on download.
const handleDownload = async () => {
const res = await fetch("http://localhost:3001/download");
const blob = await res.blob();
download(blob, "test.pdf");
}
//the fetch call for "http://localhost:3001/download" will not hit '/getdoc'
app.get('/getdoc', function (req, res) {
res.download(path.join(__dirname, 'files/test.pdf'), function (err) {
console.log(err);
});
});
Here is how I implemented the pdf download.
//resume route... this route is hit when you make a GET request to /api/resume
const router = require('express').Router();
module.exports = router;
//when route is hit the resume is downloaded
//aka /api/resume
router.get('/', (req, res, next) => {
try {
const file = `${__dirname}/resume/resume.pdf`;
res.download(file);
console.log('here');
} catch (err) {
console.log(err);
}
});
//react component
import React from 'react';
import download from 'downloadjs';
const Resume = () => {
return (
<div>
<button
type="button"
onClick={async () => {
const res = await fetch('/api/resume');
const blob = await res.blob();
download(blob, 'test.pdf');
}}
>
Download
</button>
</div>
);
};
Try this:
const handleDownload = () => {
var reqObj = new XMLHttpRequest();
reqObj.open('GET','http://localhost:3001/download',true); // 'getpdf' is the URI to recongize your request at the server
reqObj.send();
reqObj.onreadystatechange = function() {
var resObj = this;
if(resObj.readyState == resObj.DONE) {
if (resObj.status != 200) {
console.log(200);
} else if (resObj.status == 200){
var resTxt = reqObj.responseText;
window.location.assign(resTxt); // Opens the pdf download prompt
}
}
}

Resources