I'm creating a local webserver (on my windows PC) for my arduino to send POST requests to. The data from these requests would then be written to Cloud Firestore via the local webserver since the Arduino cannot support HTTPS which is required for Firebase. I have the server set up using express:
const express = require('express');
const bodyParser = require('body-parser')
const app = express();
const HOST_PORT = 6500;
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(bodyParser.raw());
app.get('/',(req,res,next)=>{
return res.status(200).send()
})
app.post('/',(req,res,next)=>{
return res.status(200).send()
})
app.post('/machine',(req,res,next)=>{
console.log('Got body:', req.body);
//Send to firebase here use async/await or just send the request?
return res.status(200).send()
})
app.listen(HOST_PORT, () => console.log(`Started server at http://localhost:${HOST_PORT}!`));
If three arduinos make a request to /machine would three instances of that functions be created? I'm wondering because I would like to know how to handle simultaneous requests on the webserver. Do I just make the call to firebase and not await the result?
This is the nice thing about Node.js asynchronous runtime: while your /machine route awaits for a first request, it can handle other requests, no problem!
app.post('/machine', async (req,res,next)=>{
console.log('Got body:', req.body);
await firestore.doc("collection/docId").set({foo: "bar"})
return res.status(200).send()
})
More info can be found here
Related
Im currently working on my new MERN stack project.
Im trying to get information about time spent on every location and everything seemed ok until i' ve discovered a bug. After changing location and sending post request to server about 10 times whole localhost server is frozing. Any calls to the server stop working. This is very frustrating and i cant figure it out. Do you guys have any ideas?
UseEffect calling action:
useEffect(() => {
dispatch(savePageStats())
}, [location])
Redux action call:
export const savePageStats = () => async (dispatch) => {
try{
const arr = []
await api.savePageSession(arr)
}catch(err){
console.log(err)
}
Axios api:
export const savePageSession = (arr) => API.post('/stats/savepagesession', arr)
Express router:
const app = express()
app.use(bodyParser.json({ limit: '30mb', extended: true}))
app.use(bodyParser.urlencoded({ limit: '30mb', extended: true}))
app.use(cors())
app.use('/users', usersRoutes)
app.use('/messages', messagesRoutes)
app.use('/stats', statsRouter)
dotenv.config()
mongoose.connect(process.env.CONNECTION_URL)
.then(() => app.listen(process.env.PORT, () => console.log(`server running on port ${process.env.PORT}`) ))
.catch((err) => console.log(err))
Express controler
export const savePageSession = async (req, res) => {
try{
console.log('im here')
}catch(err){
res.status(500).json({message: 'something went wrong'})
}
}
The savePageSession route handler doesn't send any response back to the client. That means the browser will still be waiting for a response and will eventually stop sending requests to your server until the previous requests finish or eventually timeout.
Add at least something simple like res.send("ok"); to the route. All http request handlers on your server MUST send some kind of response back to the client or it will mess up the client.
I'm having trouble with the error message in the title when trying to retrieve all users in my express .get('/users') method. I am using Node.js, Express, and node-postgres. I have my
getUsers(); function defined in my queries.js file, and I call the function in my app.get() function in my index.js file.
queries.js
const client = require('./object models/db_client_pool')
const Pool = require('pg').Pool
const pool = new Pool(client.client)
async function getUsers(request, response) {
await pool.connect()
pool.query('select * from discord_users', (error, results) => {
if (error) {
throw error
}
response.sendStatus(200).json(results.rows)
pool.release();
})
}
module.exports = {
getUsers
}
index.js
const express = require('express');
require('dotenv').config();
//const bodyParser = require('body-parser'); deprecated
const app = express();
const port = 3000;
const db = require('./queries');
app.use(express.json())
app.use(express.urlencoded({
extended: true
}))
app.get('/', (request, response) => {
response.json({ info: 'Node.js, Express, and Postgres API' })
})
app.get('/users', (req, res) => {
db.getUsers(req, res)
})
app.listen(port, () => {
console.log(`App is listening on port ${port}`);
});
As I said, I keep getting the "cannot set headers after they are sent to the client" error and I'm at a loss of what to do. Thanks in advance for your help!
Change from this:
response.sendStatus(200).json(results.rows)
to this:
response.status(200).json(results.rows);
or even just to this:
response.json(result.rows); // 200 is the default status already
The last one is fine because 200 is already the default status so you don't need to set that yourself.
The problem is that response.sendStatus(200) sends a complete response with an empty body and then you try to call response.json(result.rows) which tries to send ANOTHER response to the same request. Trying to send that second response to the same request is what triggers the error message you are getting.
response.status(200) just sets the status to 200 as a property on the waiting response object and waits for some other method to actually send the response itself which you can then do with .json(...).
So my guess is, you're running express 4.x and that doesn't support response.sendStatus(200) anymore. You have to use response.status(200) instead.
Now, another issue I see in your code is, I don't recognize pool.release() method from pg library. You can release a client back to a pool but you can't release a pool of clients. Maybe you meant pool.end()?
I know this kind of problem has been solved before, but I can't figure out exactly why it is not working for my case.
I am working on a website locally and I want to test it out on various platforms and devices so I decided to use ngrok for this.
My front-end is running on port 3000 and my express server on port 5000.
So I opened ngrok and entered ngrok http 3000
On my local PC, where the server is running, the https://example.ngrok.io is working as intended without any problems.
But on my laptop (or another device), the front-end displays correctly but when it is actually going to get data from the back-end, it is showing the error: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/weather/51.87575912475586,0.9436600208282471. (Reason: CORS request did not succeed).
On my express server, I made sure to use the cors package and app.use(cors()); and I also tried adding the headers manually :
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});
Source: Why doesn't adding CORS headers to an OPTIONS route allow browsers to access my API?
Here is also my code where I am fetching and getting data in case I am doing something wrong there:
index.js (front-end)
const response = await fetch(`http://localhost:5000/weather/${lat},${lng}`); //sending request to server-side
const json = await response.json();
console.log(json); //getting the weather data from server-side
server.js (back-end)
const express = require("express");
const mongoose = require("mongoose");
const fetch = require("node-fetch");
const cors = require('cors');
const nodemailer = require('nodemailer');
require('dotenv').config();
const users = require('./routes/api/users');
const app = express();
//Json Middleware
app.use(express.json());
app.use(cors());
//Getting URI from keys file
const db = require('./config/keys').mongoURI;
//Connect to the Database
mongoose.set('useUnifiedTopology', true);
mongoose.set('useCreateIndex', true);
mongoose.connect(db, {useNewUrlParser: true})
.then(()=> console.log("Database Connected"))
.catch(err=> console.log(err));
//Route for user routes
app.use('/api/users',users);
const dbport = process.env.PORT || 5000;
app.listen(dbport, () => console.log(`Server started on port ${dbport}`));
app.get('/weather/:latlon', async (req,res) =>{ //awating request from client-side
const latlon = req.params.latlon.split(',');
console.log(req.params);
const lat = latlon[0];
const lon = latlon[1];
console.log(lat,lon);
const api_key = process.env.API_KEY;
const weather_url = `https://api.darksky.net/forecast/${api_key}/${lat},${lon}?units=auto`; //getting data from weather API
const fetch_res = await fetch(weather_url);
const json = await fetch_res.json();
res.json(json); //sending weather data back to client-side
});
Is this possible to work or not due to the nature of localhost?
Both firefox and chrome had the same problem.
Thanks for the help!
After some days of head scratching, I finally found a solution and I'm posting it below for others that may have the same problem.
Step 1:
Instead of having 2 ports active (3000 for client and 5000 for server), I closed my client port and served my client folder/assets directly from my server using express:
const dbport = process.env.PORT || 5000;
app.listen(dbport, () => console.log(`Server started on port ${dbport}`));
app.use(express.static('client')); //serving client side from express
//Json Middleware
app.use(express.json());
Step 2:
Now that we have one port (port 5000) for both the client and the server, I went into my client side where I did my fetch requests (see above at index.js) and modified the actual requests to be relative:
const response = await fetch(`/weather/${lat},${lng}`); //sending request to server-side
const json = await response.json();
console.log(json); //getting the weather data from server-side
Step 3:
Finally, I opened ngrok and typed:
ngrok http 5000
It should now work.
If you are using ngrok with nodejs/express.js .
Remove he cors import and use this code:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD"); // update to match
the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-
Type, Accept");
next();
});
replace "YOUR-DOMAIN.TLD" with "*" to give access to all urls OR your specific website url.
Refer to https://enable-cors.org/server_expressjs.html for more details
Thank You.
I'm using http-proxy-middleware (and open to suggestions, but would like to stick to the modules which are proxying requests instead of creating new ones like request or http) to proxy requests to remote host.
Two problems I'm not seeing solutions to currenty:
1) I have a CSRF-protected form (via csurf). I would like it that the middleware checks the CSRF-token first, and in case it is valid only then proxies the request to another host, obtains the response and sends it to user. How to achieve such a setup?
2) http-proxy-middleware (and some other proxying modules) utilizes app.use to set one forwarding rule (append the route to the host), however I would like to have a more fine-grained control over routes - each of my routes must have its own endpoint on the remote host.
The code:
const express = require('express')
const csrf = require('csurf')
const cookieParser = require('cookie-parser')
const proxy = require('http-proxy-middleware')
var app = express()
var csrfProtection = csrf({ cookie: true })
app.use(cookieParser())
// not quite what I need, since different
// routes would utilize different endpoints
app.use('/api', proxy('http://example.com'))
app.get('/forms', (req, res) => {
res.send(
res.render('csrf-protected-forms.html', { csrfToken: req.csrfToken() })
)
})
app.post('/api/one', csrfProtection, (req, res) => {
response = // proxies to 'http://example.com/something/one'
// and obtains response
res.send(response)
})
app.post('/api/two', csrfProtection, (req, res) => {
response = // proxies to 'http://example.com/somethingelse/and/here/two'
// and obtains response
res.send(response)
})
app.listen(3000)
In your code csrf protection runs after proxied middleware. In case if you want protect only this two routes '/api/one','/api/two':
app.use(['/api/one','/api/two'], csrfProtection, proxy('http://example.com'))
app.use('/api', proxy('http://example.com'))
Or if you want protect all POST requests to API, you need somthing this:
app.use('/api', csrfProtection, proxy('http://example.com'))
I'am trying to handle http post message from Mailgun bounce webhook. When sending it to Mailgun's Postbin service all data is found of course. But I'm now sending that POST to my localhost server for development purposes and all I get is empty json array. I use Test Webhook.
Intent is to keep this simple as possible besides our main service. That for I started using nodejs/expressjs to create stand alone webservice to work as relay to receive POST messages of email bounces from Mailgun and inform admins about bounced email addresses.
Now I can't figure why I don't get the same data as is visible in Postbin.
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mailgun = require('mailgun-js')({apiKey: 'key-...', domain: 'mymailgundomain.com'});
app.use(bodyParser.urlencoded({
extended: true
}));
function router(app) {
app.post('/webhooks/*', function (req, res, next) {
var body = req.body;
if (!mailgun.validateWebhook(body.timestamp, body.token, body.signature)) {
console.error('Request came, but not from Mailgun');
res.send({ error: { message: 'Invalid signature. Are you even Mailgun?' } });
return;
}
next();
});
app.post('/webhooks/mailgun/', function (req, res) {
// actually handle request here
console.log("got post message");
res.send("ok 200");
});
}
app.listen(5000, function(){
router(app);
console.log("listening post in port 5000");
});
I'm running this from Mailgun's Test Webhook using url like http://mylocalhostwithpublicip.com:5000/webhooks/mailgun
Code structure is copied from https://github.com/1lobby/mailgun-js. Probably I'm missing something fundamental here as I can't figure it out myself.
The reason you're not seeing req.body populated is because the body-parser module does not support multipart/form-data requests. For those kinds of requests you need a different module such as multer, busboy/connect-busboy, multiparty, or formidable.
If your content-type (shown by logging console.dir(req.headers['content-type'])) is 'application/x-www-form-urlencoded', and you're using body-parser, try adding the following line:
bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
to make it work with multer, you can use .any() (version 1.1.0)
for me it worked like this: (assuming multer is included and declared as "multer")
post('/track', multer.any(),function(req, res){
//if body is a string, parse the json
var data=(typeof req.body=='string')?JSON.parse(req.body):req.body;
//if data is an object but you can't verify if a field exists with hasOwnProperty, force conversion with JSON
if(typeof data=='object' && typeof data.hasOwnProperty=='undefined')
data=JSON.parse(JSON.stringify(data));
//data is your object
});
var multer = require('multer');
var msg = multer();
post('/track', msg.any(), function(req, res){
console.log(req.body);
}
I make a custom parser for get data in req.body when the Content-type = 'multipart/alternative'
https://github.com/josemadev/Multiparser/