Cant set headers after they are sent - node.js

I am using express backend with a react frontend everything is working fine but occasionally i get error
Cant set header after they are sent
and server gets down.i searched few ways this error might happen but in my code i could not find such cases.i tried to be simple as possible in the code.can anyone please point me what might be the issue?
Server.js file
// call the packages we need
const addItem = require('./controllers/addItem');
const addCategory = require('./controllers/addCategory');
const addSubCategory = require('./controllers/addSubCategory');
const getSubCategory = require('./controllers/getSubCategoryByCategory');
const getCategory = require('./controllers/getAllCategory');
const getAllItems = require('./controllers/getAllItems');
const cors = require('cors');
const express = require('express');
// call express
const app = express(); // define our app using express
const bodyParser = require('body-parser');
// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
const port = process.env.PORT || 8080; // set our port
// ROUTES FOR OUR API
// =============================================================================
const addItemRoute = express.Router(); // get an instance of the express Router
const getCategoryRoute = express.Router();
const addCategoryRoute = express.Router();
const addSubCategoryRoute = express.Router();
const getSubCategoryRoute = express.Router();
const getAllItemsRoute = express.Router();
getCategoryRoute.get('/get_category', (req, res) => {
getCategory(res);
});
addCategoryRoute.post('/add_category', (req, res) => {
addCategory(req.body.name, res);
});
getSubCategoryRoute.get('/get_subcategory/:catId', (req, res) => {
getSubCategory(req.params.catId, res);
});
addSubCategoryRoute.post('/add_subcategory', (req, res) => {
addSubCategory(req.body.name, req.body.cat_id, res);
});
// code, name, quantity, length, description and subcategory id should be passed as parameters
addItemRoute.post('/add_item', (req, res) => {
addItem(req.body.item, res);
});
getAllItemsRoute.get('/get_items', (req, res) => {
getAllItems(res);
});
// more routes for our API will happen here
// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', addItemRoute);
app.use('/api', getCategoryRoute);
app.use('/api', addCategoryRoute);
app.use('/api', addSubCategoryRoute);
app.use('/api', getSubCategoryRoute);
app.use('/api', getAllItemsRoute);
// START THE SERVER
// =============================================================================
app.listen(port);
console.log(`Server started on port ${port}`);
getAllCategories() function
Object.defineProperty(exports, '__esModule', {
value: true,
});
const pool = require('./connection');
module.exports = function (res) {
pool.getConnection((err, connection) => {
if (err) {
connection.release();
return res.json({ code: 100, status: 'Error in connection database' });
}
console.log(`connected as id ${connection.threadId}`);
connection.query('select * from category;', (err, rows) => {
connection.release();
if (!err) {
return res.json(rows);
}
});
connection.on('error', err => res.json({ code: 100, status: 'Error in connection database' }));
});
};

If you get an error in connection.query() you send a response with res.json(). This error is caught in connection.on('error') where you send another response. You can't send two responses to the same request. It seems that in this case, you don't really need connection.on() at all or if you have it to catch other errors, don't send a response on connection.query()'s error.

Related

Returning data to a user from an external API

i am trying to return the value of my search after using the node-spotify-api package to search for an artist.when i console.log the spotify.search ..... without the function search function wrapped around it i get the values on my terminal..what i want is when a user sends a request to the userrouter routes i want is to display the result to the user..i using postman for testing ..
This is the controller
const Spotify = require('node-spotify-api');
const spotify = new Spotify({
id: process.env.ID,
secret: process.env.SECRET,
});
const search = async (req, res) => {
const { name } = req.body;
spotify.search({ type: 'artist', query: name }).then((response) => {
res.status(200).send(response.artists);
}).catch((err) => {
res.status(400).send(err);
});
};
module.exports = {
search,
};
**This is the route**
const express = require('express');
const searchrouter = express.Router();
const { search } = require('./spotify');
searchrouter.route('/').get(search);
module.exports = searchrouter;
**This is my server.js file**
const express = require('express');
require('express-async-errors');
const app = express();
require('dotenv').config();
// built-in path module
const path = require('path');
// port to be used
const PORT = process.env.PORT || 5000;
// setup public to serve staticfiles
app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.set('port', PORT);
const searchrouter = require('./route');
app.use('/search', searchrouter);
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'index.html'));
});
app.listen(PORT, (req, res) => {
console.log(`Server is listening on port ${PORT}`);
});
[that is my project structure][1]
Well Your Code has a bug
Which is
searchrouter.route('/').get(search);
You are using a get request and still looking for a req.body
const { name } = req.body;
name is going to equal to = undefined
and when this runs
spotify.search({ type: 'artist', query: name })
it's going to return an empty object or an error
req.body is empty for a form GET request
So Your fix is
change your get request to a post
searchrouter.route('/').post(search);

How do I grab json from an external api (serp api) from my backend and then make that same data available for my front end application?

Right now I have a front end react application using axios and and a backend server using node.js and express. I cannot for the life of me get my serp api data to post so that my front end can get it through axios and display the json data. I know how to get data to the front end but I am not a backend developer so this is proving to be incredibly difficult at the moment. I'm able to get the data from the the external api, I just don't know how to post it once I get it. Also I would not like to have all these request running on server.js so I created a controller but I think that is where it is messing up. Any help is appreciated
//pictures controller
const SerpApi = require('google-search-results-nodejs');
const {json} = require("express");
const search = new SerpApi.GoogleSearch("674d023b72e91fcdf3da14c730387dcbdb611f548e094bfeab2fff5bd86493fe");
const handlePictures = async (req, res) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com"
};
const callback = function(data) {
console.log(data);
return res.send(data);
};
// Show result as JSON
search.json(params, callback);
//res.end();
}
// the above code works. how do i then post it to the server so that i can retrieve it to the backend?
module.exports = {handlePictures};
//server.js
const express = require('express');
const app = express();
const path = require('path');
const cors = require('cors');
const corsOptions = require('./config/corsOptions');
const { logger } = require('./middleware/logEvents');
const errorHandler = require('./middleware/errorHandler');
const cookieParser = require('cookie-parser');
const credentials = require('./middleware/credentials');
const PORT = process.env.PORT || 3500;
// custom middleware logger
app.use(logger);
// Handle options credentials check - before CORS!
// and fetch cookies credentials requirement
app.use(credentials);
// Cross Origin Resource Sharing
app.use(cors(corsOptions));
// built-in middleware to handle urlencoded form data
app.use(express.urlencoded({ extended: false }));
// built-in middleware for json
app.use(express.json());
//middleware for cookies
app.use(cookieParser());
//serve static files
app.use('/', express.static(path.join(__dirname, '/public')));
// routes
app.use('/', require('./routes/root'));
app.use('/pictures', require('./routes/api/pictures'));
app.all('*', (req, res) => {
res.status(404);
if (req.accepts('html')) {
res.sendFile(path.join(__dirname, 'views', '404.html'));
} else if (req.accepts('json')) {
res.json({ "error": "404 Not Found" });
} else {
res.type('txt').send("404 Not Found");
}
});
app.use(errorHandler);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
//api/pictures.js
const picturesController= require('../../controllers/picturesController');
const express = require('express')
const router = express.Router();
// for POST request use app.post
router.route('/')
.post( async (req, res) => {
// use the controller to request external API
const response = await picturesController.handlePictures()
// send the response back to client
res.json(response)
})
module.exports = router;
You just need to return the result from SerpApi in your handlePictures function. To do this make a new Promise and when search.json runs callback do what you need with the results and pass it in resolve.
Your picturesController.js with an example of returning all results.
//pictures controller
const SerpApi = require("google-search-results-nodejs");
const { json } = require("express");
const search = new SerpApi.GoogleSearch(process.env.API_KEY); //your API key from serpapi.com
const handlePictures = async (req, res) => {
return new Promise((resolve) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com",
};
const callback = function(data) {
resolve(data);
};
search.json(params, callback);
});
};
module.exports = { handlePictures };
Output:
And I advise you to change your API key to SerpApi to prevent it from being used by outsiders.
Since I don't have the full context of your App I can just assume the context. But given the fact that you already have wrapped the logic of calling the external API into a dedicated controller you can use it in the following way in an express app (used the hello world example from express):
// import your controller here
const express = require('express')
const app = express()
const port = 3000
// for POST request use app.post
app.get('/', async (req, res) => {
// use the controller to request external API
const response = await yourController.method()
// send the response back to client
res.json(response)
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Here's an example how to execute the http request from the frontend:
const response = await fetch('http://localhost:3000') // result from res.json(response)

How do we pass parameters to a mounted route in nodeJS?

I'm taking a course on NodeJS, there were a few assignments related to routing, everything works fine except this part which seems a little odd: For some reason, I cannot read the parameter ID being passed to the mounted router.
dish.js
const express = require('express');
const bodyParser = require('body-parser');
const dishRouter = express.Router();
dishRouter.use(bodyParser.json());
dishRouter.route('/')
.all((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/plain');
next();
})
.get((req,res) => {
console.info('Info: ',req);
res.end(`Sending details of the dish back to you: ${req.params.dishId}`);
})
.post((req,res) => {
res.statusCode = 403;
res.end(`Operation not supported: ${req.params.dishId}`);
})
.put((req,res) => {
res.write(`Updating the dish...: ${req.params.dishId} \n` );
res.end(`Will update this dish: ${req.body.name} with details: ${req.body.description}`);
})
.delete((req,res) => {
res.end(`Deleting this dish: ${req.params.dishId}`);
});
exports.dish = dishRouter;
dishes.js
const express = require('express');
const bodyParser = require('body-parser');
const dishesRouter = express.Router();
dishesRouter.use(bodyParser.json());
dishesRouter.route('/')
.all((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/plain');
next();
})
.get((req,res) => {
res.end('Sending all dishes back to you');
})
.post((req,res) => {
res.end(`Will add the dish: ${req.body.name} with details: ${req.body.description}`);
})
.put((req,res) => {
res.statusCode = 403;
res.end(`Operation not supported.`);
})
.delete((req,res) => {
res.end(`Deleting all dishes.....`);
});
exports.dishes = dishesRouter;
index.js
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const http = require('http');
const dishRouter = require('./routes/dish');
const dishesRouter = require('./routes/dishes');
const hostname = 'localhost';
const port = 3000;
const app = express();
app.use(morgan('dev'));
app.use(bodyParser.json());
app.use('/dishes',dishesRouter.dishes);
app.use('/dishes/:dishId',dishRouter.dish);
app.use(express.static(__dirname+'/public'));
app.use((req,res,next) => {
res.statusCode = 200;
res.setHeader('Content-Type','text/html');
res.end('<html><body><h1>This is an Express Server</h1></body></html>');
});
const server = http.createServer(app);
server.listen(port,hostname,(req,res) => {
console.info(`Server running on port: ${port}, at: ${hostname}`);
})
This GET localhost:3000/dishes/123 is calling the right route, but the parameter dishId comes back as "undefined". Again, just learning nodeJS, seems like my receiver/mounted route should receive those parameters just fine, the body can be read properly, but not the params. ... thanks.
Yeah the params don't flow between routers. You're on a new router, hence new route params object.
You can check out the code for this:
https://github.com/expressjs/express/blob/master/lib/router/index.js#L43
Check out line 43 and line 53 where route.params is set to an empty object.
Some examples:
index.js
app.use('/dishes/:dishId',(req, res) => {
console.log('now I get my dishId', req.params.dishId)
});
dish.js (version 1)
dishRouter.route('/')
.get((req, res) => {
console.log('now i get nothing', req.params)
})
dish.js (version 2)
dishRouter.route('/:anotherId')
.get((req, res) => {
console.log('now we get another parameter', req.params.anotherId)
})
// the path would be /dish/123/456
I'm not sure if there is a offical-expressjs-way to pass the params object between routers.
One solution would be to create a custom handler
index.js
app.use('/dishes/:dishId', handler)
handler.js
function handler (req, res, next) {
if (req.method === 'GET') {
console.log('now we get it', req.params)
}
}
module.exports = handler
Anoter way would be to add the dishId to the request object before calling the router:
index.js
app.use('/dishes/:dishId', (req, res, next) => {
req.dishId = req.params.dishId
router(req, res, next)
})
dish.js
const express = require('express')
const router = express.Router()
router.route('/')
.get((req, res) => {
console.log('nothing here', req.params)
console.log('dishId', req.dishId)
})
module.exports = router
Third way would be to send the params as options to a router function
index.js
app.use('/dishes/:dishId', (req, res, next) => {
router(req.params)(req, res, next)
})
dish.js
function createRouter (options) {
const router = express.Router()
router.route('/')
.get((req, res) => {
console.log('nothing here', req.params)
console.log('but alot here', options)
})
return router
}
module.exports = createRouter
If you want you could also just put the :dishId on the router as an optional parameter
index.js
app.use('/dishes', dishesRouter)
dishes.js
const express = require('express')
const router = express.Router()
router.route('/:dishId?')
.get((req, res) => {
if (req.params.dishId) {
res.end(`Sending details of the dish back to you: ${req.params.dishId}`)
} else {
res.end('Sending all dishes back to you');
}
})
module.exports = router

Can't use axios to get/post data from/to localhost server in android 7.0 device - React Native app

I use Axios in my react native app. I use Mobiistar Zumbo J2 with Expo to test but I get err: Network Error. I also set CORS for my node server but it still doesn't work. I test with Postman it work normally. Here is my code:
server.js
const express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const index = require("./routes/index");
const bookings = require("./routes/bookings");
const cors = require('cors'); // Yep, you need to install this
const app = express();
const port = process.env.PORT || 3000;
app.use(cors());
app.listen(port, () => {
console.log('Server is running on port', port);
});
app.set("views", path.join(__dirname, "views"));
app.set("view engine", 'ejs');
app.engine("html", require("ejs").renderFile);
//Body parser MW
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
//Routes
app.use("/", index);
app.use("/api", bookings);
bookings.js
const express = require("express");
const router = express.Router();
const mongojs = require("mongojs");
const db = mongojs("mongodb://<username>:<password>#ds139614.mlab.com:39614/booking-car-app", ["bookings"]);
router.get("/bookings", (req, res, next) => {
db.bookings.find((err, data) => {
if (err) {
res.send(err);
}
res.json(data);
});
});
router.post("/bookings", (req, res, next) => {
const booking = req.body;
if (!booking.userName) {
res.status(400);
res.json({err: "Bad data"});
} else {
db.bookings.save(booking, (err, savedBooking) => {
if (err) {
res.send(err);
}
res.json(savedBooking);
})
}
})
module.exports = router;
using Axios to get data from server
axios.get("http://127.0.0.1:3000/api/bookings/")
.then(res => {
console.log("Get booking info: ", res);
alert(res);
})
.catch(err => console.log(err))
Error:
Network Error
Stack trace:
node_modules\axios\lib\core\createError.js:16:24 in createError
node_modules\axios\lib\adapters\xhr.js:87:25 in handleError
node_modules\event-target-shim\lib\event-target.js:172:43 in dispatchEvent
node_modules\react-native\Libraries\Network\XMLHttpRequest.js:578:29 in setReadyState
node_modules\react-native\Libraries\Network\XMLHttpRequest.js:392:25 in __didCompleteResponse
node_modules\react-native\Libraries\vendor\emitter\EventEmitter.js:191:12 in emit
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:349:47 in __callFunction
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:106:26 in <unknown>
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:297:10 in __guard
node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:105:17 in callFunctionReturnFlushedQueue
...
Please help me.
Thank you very much
Android uses a special type of IP address 10.0.2.2
axios.get("http://10.0.2.2:3000//api/bookings/")
.then(res => {
console.log("Get booking info: ", res);
alert(res);
})
.catch(err => console.log(err))

app post is not working i am not getting the output

var express = require("express");
var app = express();
var bodyParser = require('body-parser');
var port = 3000;
const fs = require('fs');
// we are connecting to the mangodb using mangoose
var mongoose = require("mongoose");
// Now we are using bodyParser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
mongoose.connect("mongodb://localhost:27017/YourDB", { useNewUrlParser: true })
// now we are creating the schema to the database
var nameSchema = new mongoose.Schema({
firstName: String,
lastNameName: String
});
// Now we have to create a model
var User = mongoose.model("User", nameSchema);
app.use("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
// Now we are posting the data
app.post("/addname", (req, res) => {
console.log("nnnnnn")
console.log(req.body.firstName)
var myData = new User(req.body);
myData.save()
console.log(myData);
fs.writeFile(__dirname +"/data.json",myData, function(err){
if(err) {
return console.log(err);
}
console.log("The file is saved ");
})
console.log(myData)
})
// Now we are getting the data
app.listen(port, () => {
console.log("Server listening on port " + port);
});
1)I am using express app.post to post the data into database and store the data into the write file to check
2) app.post is not working it tried console.log to check but it is not going inside the function
3) I am not getting output as well as any error plese help me
there is no error handling and response handling in this code.
it will be readable if we write post method with async/await :
app.post("/addname", async (req, res) => {
console.log("nnnnnn")
console.log(req.body.firstName)
var myData = new User(req.body);
await myData.save()
console.log(myData);
fs.writeFileSync(__dirname +"/data.json", myData)
console.log(myData)
})
you will add next() to app.use
var User = mongoose.model("User", nameSchema);
app.use("/", (req, res,next) => {
res.sendFile(__dirname + "/index.html");
next()
});
// Now we are posting the data
app.post("/addname", (req, res) => {
console.log("nnnnnn")
console.log(req.body.firstName)
var myData = new User(req.body);
myData.save()
console.log(myData);
fs.writeFile(__dirname +"/data.json",myData, function(err){
if(err) {
return console.log(err);
}
console.log("The file is saved ");
})
console.log(myData)
})
// Now we are getting the data
app.listen(port, () => {
console.log("Server listening on port " + port);
});
That's because every request is going to this app.use code block. app.use("/", (req, res) => { ... });
Just Put it below the app.post("/addname", (req, res) => { ... });
app.use is used to mount middlewares into the request-response chain. So, every request that comes matches the /(which is essentially every request) goes inside that middleware. So, use your routes first then use the middleware at the end.
EDIT:
Let me give you a mcve which I tested locally:
const express = require('express');
const fakeData = function(){
return {
s: "fakeData"
}
}
const app = express();
const port = 8181
const path = require('path')
app.get("/a", (req, res) => {
return res.json({d:'yay'});
});
app.use('/',(req,res)=>{
return res.json(fakeData());
})
app.listen(port, () => {
console.log(`Server started on PORT ${port}`);
});
Because every request goes through a mounted middleware, so when you GET/POST/ANYTHING to localhost:8181/<abosulutely_any_path> it will go through the app.use because it treats that function as middleware and will return { s: "fakeData" }.
But when you make a GET call http://localhost:8181/a it will go to the app.get route BECAUSE WE DECLARED IT FIRST and return { d : "yay" }

Resources