Trying to send proper header with Express, Tedious, while accessing Azure SQL - node.js

My backend gets a request to get records from an Azure SQL db. To manage this requests I'm using Express in Nodejs, and Tedious (to connect to DB). When the request to the appropriate route comes in, Tedious opens the connection with db, queries it, and it should send the response back to frontend.
However, the code responds before I have an answer with from the db, and thus when I go to send the real (the actually desired) response, Express tells me it already sent headers back (the dreaded: 'Cannot set headers after they are sent to the client').
After debugging quite a bit (using several console.log(JSON.stringify(resp.headersSent)); ) to see when was the response actually sent, I noticed that it's sent the moment I connect with Azure (see below).
I'm not sure if I'm missing something (though I already checked the documentation for all those programs quite a bit), but how can I control when the response is sent? Or, is there another way of doing this.
I omitted several of the other routes for brevity. Other routes work fine and thus I know code connects well to Azure db, and frontend does query backend correctly. Help is appreciated. Thank you.
const express = require('express');
const cors = require('cors');
const Connection = require('tedious').Connection;
const Request = require('tedious').Request;
const config = {
authentication: {
options: {
userName: "xxxx",
password: "xxxx"
},
type: 'default'
},
server: "xxxx",
options: {
database: "xxxx",
encrypt: true
}
};
const app = express();
app.use(express.json({type: '*/*'}));
app.use(cors({ origin: '*' }));
app.get("/allproj/", function (req, resp) {
const q = `select Title, Report_Date, Project_Number, Phase_Code, Items_No, PId from projec order by PId desc`;
let ansf = [];
const connection = new Connection(config);
connection.on('connect', (err, connection) => {
if (err) {
console.log(err);
} else { //this is the moment the headers are sent,
//seemingly with positive response from connection
queryItems(q);
}
});
queryItems = (q) => {
request = new Request(q, function (err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows pulled');
connection.close();
}
});
request.on('row', function(columns) {
let ans = [];
columns.forEach(function(column) {
ans.push(column.value);
if (ans.length === 6) { // I know each row is 6 cols long
ansf.push(ans);
ans = [];
}
});
console.log('ansf length: ' + ansf.length);
resp.send({ ansf }); // This is the response I would like to return
});
request.on('done', function(rowCount) {
console.log(rowCount + ' rows returned');
connection.close();
});
connection.execSql(request);
};
resp.redirect("/");
});
app.listen(3000, process.env.IP, function() {
console.log("Started OK...");
});

Remove resp.redirect("/");
As it is already transferring your request to "/" and when control come at resp.send({ansf}), It gives you error.

Related

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

Nodejs not able to respond to more than one request for mongoose connection test

I'm fairly new to mongoose and node. I am trying to write a simple API using Nodejs and Express, wherein I send a MongoDB URI, and if it is valid, i.e. the username/password combo works and a connection to that URI is successfully established, a success message should be returned.
I initially tried to create a separate function to try to connect to the given URI:
function testURI(uri) {
mongoose.connect(uri, {useNewUrlParser: true} );
mongoose.connection.on("connected", function() { return true } );
}
module.exports.test = function(req, res) {
var uri = req.body.uri;
if(testURI(uri)) res.status(200).json({'success': true});
else res.status(400).json({'success': false});
};
But this failed, since mongoose connects asyncronously, and the "connected" event callback is not able to return true for the main function
So I dropped the idea for a separate function, and instead tried to achieve it within the module.exports.test function:
module.exports.test = function(req, res) {
var uri = req.body.uri;
mongoose.connect(uri, { useNewUrlParser: true, connectTimeoutMS: 2500 });
mongoose.connection.on("connected", function() {
mongoose.connection.close();
res.status(200).json({'success': true});
});
mongoose.connection.on("error", function(err) {
result = {
'error': true,
'errorMsg': 'Error: Could not connect to the given host.'
}
mongoose.connection.close();
res.status(400).json(result);
});
};
This works fine, except that the server dies after responding to one request. Once I try with an invalid URI, it returns HTTP 400 as expected, but then when I send another request with a different URI, the app just crashes with the error
Error: Can't set headers after they are sent.
And then I have to restart the app before I can send another request. Apparently the two separate res.status(200).json and res.status(400).json in the same controller are creating the problem and the app is treating subsequent requests also as the same request.
Fixed the problem by creating a custom middleware. Instead of returning the result via res.status().json() within the controller function, I used this function as a middleware and returned the result from the next function
module.exports.test = function(req, res, next) {
var uri = req.body.uri;
mongoose.connect(uri, { useNewUrlParser: true, connectTimeoutMS: 2500 });
mongoose.connection.on("connected", function() {
mongoose.connection.close();
req.status = 200;
req.result = {'success': true};
next();
});
mongoose.connection.on("error", function(err) {
result = {
'error': true,
'errorMsg': 'Error: Could not connect to the given host.'
}
mongoose.connection.close();
req.status = 400;
req.result = result;
next();
});
};
module.exports.returnDb = function(req, res) {
res.status(req.status).json(req.result);
};
Edited route declaration:
router.route('/test')
.post(client.test, client.returnDb);

HTTP2 push for Express

I'm trying to set up HTTP2 for an Express app I've built. As I understand, Express does not support the NPM http2 module, so I'm using SPDY. Here's how I'm thinking to go about it-I'd appreciate advice from people who've implemented something similar.
1) Server setup-I want to wrap my existing app with SPDY, to keep existing routes. Options are just an object with a key and a cert for SSL.
const app = express();
...all existing Express stuff, followed by:
spdy
.createServer(options, app)
.listen(CONFIG.port, (error) => {
if (error) {
console.error(error);
return process.exit(1)
} else {
console.log('Listening on port: ' + port + '.')
}
});
2) At this point, I want to enhance some of my existing routes with a conditional PUSH response. I want to check to see if there are any updates for the client making a request to the route (the client is called an endpoint, and the updates are an array of JSON objects called endpoint changes,) and if so, push to the client.
My idea is that I will write a function which takes res as one of its parameters, save the endpoint changes as a file (I haven't found a way to push non-file data,) and then add them to a push stream, then delete the file. Is this the right approach? I also notice that there is a second parameter that the stream takes, which is a req/res object-am I formatting it properly here?
const checkUpdates = async (obj, res) => {
if(res.push){
const endpointChanges = await updateEndpoint(obj).endpointChanges;
if (endpointChanges) {
const changePath = `../../cache/endpoint-updates${new Date().toISOString()}.json`;
const savedChanges = await jsonfile(changePath, endpointChanges);
if (savedChanges) {
let stream = res.push(changePath, {req: {'accept': '**/*'}, res: {'content-type': 'application/json'}});
stream.on('error', function (err) {
console.log(err);
});
stream.end();
res.end();
fs.unlinkSync(changePath);
}
}
}
};
3) Then, within my routes, I want to call the checkUpdates method with the relevant parameters, like this:
router.get('/somePath', async (req, res) => {
await checkUpdates({someInfo}, res);
ReS(res, {
message: 'keepalive succeeded'
}, 200);
}
);
Is this the right way to implement HTTP2?

Express keeping connection open?

I'm using node js, express and postgresql as backend.
This is the approach I used to make a rest API:
exports.schema = function (inputs, res) {
var query = knex('schema')
.orderBy('sch_title', 'asc')
.select();
query.exec(function (err, schemas) {
if(err){
var response = {
message: 'Something went wrong when trying to fetch schemas',
thrownErr: err
};
console.error(response);
res.send(500, response);
}
if(schemas.length === 0){
var message = 'No schemas was found';
console.error(message);
res.send(400, message);
return;
}
res.send(200, schemas);
});
};
It works but after a while postgres logs an error and it's no longer working:
sorry, too man clients already
Do I need a close each request somehow? Could not find any about this in the express docs. What can be wrong?
This error only occurs on production server. Not on developing machine.
Update
The app only brakes in one 'module'. The rest of the app works fine. So it's only some queries that gives the error.
Just keep one connection open for your whole app. The docs shows an example how to do this.
This code goes in your app.js...
var Knex = require('knex');
Knex.knex = Knex.initialize({
client: 'pg',
connection: {
// your connection config
}
});
And when you want to query in your controllers/middlewares...
var knex = require('knex').knex;
exports.schema = function (req, res) {
var query = knex('schema')
.orderBy('sch_title', 'asc')
.select();
// more code...
};
If you place Knex.initialize inside an app.use or app.VERB, it gets called repeatedly for each request thus you'll end up connecting to PG multiple times.
For most cases, you don't need to do an open+query+close for every HTTP request.

Using angular.js $resource with express framework

I am new to Angular JS and node.js/express framework. I am working on a small application which uses angular and express frameworks. I have express app running with couple of end points. One for POST action and one for GET action. I am using node-mysql module to store and fetch from mysql database.
This application is running on my laptop.
angular.js client:
controller
function ItemController($scope, storageService) {
$scope.savedItems = storageService.savedItems();
alert($scope.savedItems);
}
service
myApp.service('storageService', function($resource) {
var Item = $resource('http://localhost\\:3000/item/:id',
{
id:'#id',
},
{
query: {
method: 'GET',
isArray: true
}
);
this.savedItems = function() {
Item.query(function(data){
//alert(data);
return data;
});
}
Express server with mysql database:
...
app.get('/item', item.list);
...
items.js
---------
exports.list = function(req, res) {
var sql = 'select * from item';
connect: function() {
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'admin',
database : 'test'
});
return connection;
},
query: function(sql) {
var connection = this.connect();
return connection.query(sql, function(err, results) {
if (err) throw err;
return results;
});
},
res.send(results);
};
When I send static array of items (json) from server, $scope.savedItems() is getting populated.
but when I access items in database, even though server is returning items, $scope.savedItems in client is empty. Using $http directly did not help either.
I read async nature of $resource and $http from angular.js documentation and I am still missing something or doing something wrong.
Thanks in advance and appreciate your help.
This has to do with the async nature of angular $resource.
$scope.savedItems = storageService.savedItems();
Returns immediately an empty array, which is populated after the data returns. Your alert($scope.savedItems); will therefore show only an empty array. If you look at your $scope.savedItems a little bit later you would see that it has been populated. If you would like to use the data just after it has been returned you can use a callback:
$scope.savedItems = storageService.savedItems(function(result) {alert(result); });
Just as a quick note. You could also watch the savedItems.
function ItemController($scope, storageService) {
$scope.savedItems = storageService.savedItems();
$scope.$watch(function() {
return $scope.savedItems;
}, function(newValue, oldValue) {
if (typeof newValue !== 'undefined') {
// Do something cool
}
},
true);
}
i suspect, node is not returning mysql results. The fact that it works for static files and not for mysql rules out issues with angular. Can you add firebug logs for the http call or chrome developer logs. This can shed more light on the matter

Resources