How to get and pass json from model to controller? - node.js

I'm trying to get json data from database (sql) with node.js and then pass it to app.get (express.js) but without success.
I have two files urls.js which should get all urls from database and in app.js I'm trying to create api endpoint with express.js. I have managed to get json data in app.js if I write query there and run it but I do not know how to separate it into two files.
code that works in app.js
app.get('/api/urls', (request, response) => {
db.query('SELECT * FROM urls', (error, result) => {
if (error) throw error;
response.send(result);
});
});
I've tried to separate it into two files so in urls.js (model like) I could have something like
class Urls {
async getUrls() {
const sql = `select * from urls`;
return await db.query(sql);
}
}
module.exports = Urls;
and then call it in app.js (controller like):
const data = new Urls();
app.get(/api/urls, (req, res) => {
res.send(data.getUrls());
}
In both cases result should be json

Your getUrls function is async it will return promise,so do something like this
const data = new Urls();
app.get(/api/urls, (req, res) => {
data.getUrls().then(response=>{
res.send(response);
})
}

Related

Node.js - Http get will not display data from MongoDb query in Postman or browser

I'm working on a node.js backend project that uses a MongoDb database. After I query the database and received the data it will not display the data in my browser using res.send(). I also tried res.json(). However, the data does display on my console,but just will not display in postman or my browser. Is the query data from mongoDB not json or an array? I did a little reading and it states it's a cursor pointing to the data. Can this data not be converted to display in a broswer?
mycode as well as console and browser display
ProductRouter.js
router.get('/', (req, res) => {
const products = allProducts.plantProducts();
setTimeout(() => {
if (products === "400") {
res.status("400").send("Error querying database");
}else{
console.log(products);
res.status("200").send(products);
}
}, 1000);
ProductController.js
async function plantProducts(){
try {
const products = await getProducts();
return products;
} catch(err) {
let code = "400";
return code;
//res.status('400').send(err.message);
}
}
plantProducts();
//Search mongodb for all products.
function getProducts() {
return new Promise((resolve, reject) => {
const products = Product
.find()
resolve(products);
});
}
The problem is that products yields a promise which express' res.json will not automatically await and convert to a json-response. You need await the promise and then send the response. Something like:
router.get('/', async (req, res) => {
const products = await allProducts.plantProducts();
res.json(products);
});

Is it bad practice to perform http requests in main module?

I want to sync data from an external resource (file, server etc) into my DB every time the server starts.
app.js
const app = express();
app.use('/add', require('./addLayer'));
...
(async() => {
const external_resource = await axios.get('...'); // get data
await axios.post('http://localhost:3000/add', external_resource.data); // add it to db
});
addLayer.js
const DBObject = require('../../models/DBObject')
const addNewLayer = async (req, res) => {
try {
const data = validateData(req.body)
await DBObject.create(data)
} catch (err) {
res.status(400).send(err)
}
}
I do not want to re-write the code in the /add route (which includes data validations), but I find performing POST requests in here not good for some reason. Is there a better way to do so?

Express middleware to configure response

I want to create a middleware that automatically formats my output and returns it in a format that looks like
{
"successful": "true",
"message": "Successfully created",
"data": {
"name": "Joe",
"year": 1
}
}
Currently I'm just returning a json of the data itself (name, year, etc.)
I want to add the "successful", "message", etc.
Here's some snippets of my code below:
routes/student.js
var student_controller = require('../controllers/studentController');
router.get('/list', student_controller.student_list);
controllers/student.js
var Student = require('../models/student');
exports.student_list = function(req, res, next) {
Student.find()
.exec(function(err, list_students) {
if (err) {return next(err);}
res.json(list_students);
});
};
app.js
var studentRouter = require('./routes/student');
app.use('/student', studentRouter);
How do I make this middleware, and in which file(s) should I be calling it?
The response can be intercepted by overriding response.json function. By doing so, and adding our custom function, every time, response.json() is called, our intercepting function is triggered.
middleware/response.filter.js:
// Response Interceptor Middleware
export default (request, response, next) => {
try {
const oldJSON = response.json;
response.json = (data) => {
// For Async call, handle the promise and then set the data to `oldJson`
if (data && data.then != undefined) {
// Resetting json to original to avoid cyclic call.
return data.then((responseData) => {
// Custom logic/code. -----> Write your logic to add success wrapper around the response
response.json = oldJSON;
return oldJSON.call(response, responseData);
}).catch((error) => {
next(error);
});
} else {
// For non-async interceptor functions
// Resetting json to original to avoid cyclic call.
// Custom logic/code.
response.json = oldJSON;
return oldJSON.call(response, finalResponse);
}
}
} catch (error) {
next(error);
}
}
In the Server.js file, register the middleware:
// Server.js file
import externalResponseFilter from "./middleware/response.filter.js:";
// Create Express server
const app = express();
// Response interceptor - Initialization.
app.use(externalResponseFilter);
And in the controller where you are returning the response, return with response.json() function instead of response.send().
Let me know if any additional explanation is required.
I think there is no need to middleware in your case unless you need an auth check for this routing. The Middleware has the functionality to handle something before getting your response.
your code shall be:
router.get('/list',(req,res,next)=>{
Student.find().then(data=>res.json(data)).catch(err=>conole.log(err)) });

Nodejs read a file from an express web page

i have a problem, I need to read a csv file from a web page developed using express.
Basically I have
router.get('/metricas', function(req, res, next) {
var datos= lee.leedatos();
res.render('metricas', {page:'Metricas', menuId:'metricas',data:datos});
});
and the lee.leedatos() is the following
exports.leedatos= function(){
var datos;
const csv =require('fast-csv')
const stream =fs.createReadStream('mywebsite/book1.csv');
const streamCsv=csv({
headers:true,
})
.on('data',data=>{
datos=data;
//console.log(datos);
})
.on('finish',()=>{
console.log('finished')
console.log(datos)
return datos;
})
stream.pipe(streamCsv);
}
My problem is that the web page always returns before the file has been read at all :( .. and data:datos is always empty.
How can I make the call syncronous?
Thanks !
You can use either callbacks / promises to track execution of asynchronous code. This is how code would look like with promises:
router.get('/metricas', function(req, res, next) {
var datos= lee.leedatos().then(() => {
res.render('metricas', {page:'Metricas', menuId:'metricas',data:datos});
});
});
exports.leedatos = function () {
return new Promise((resolve, reject) => {
var datos;
const csv = require('fast-csv')
const stream = fs.createReadStream('mywebsite/book1.csv');
const streamCsv = csv({
headers: true,
}).on('data', data => {
datos = data;
//console.log(datos);
}).on('finish', () => {
console.log('finished')
console.log(datos)
resolve(datos);
})
stream.pipe(streamCsv);
})
}
Here is a link to blog post that might help you in understanding asynchronous functions: https://blog.risingstack.com/node-hero-async-programming-in-node-js/

Bluebird promise resolve in express route

I have an simple REST application and I want to read files in my directory and send them back to frontend. There's code I'm using:
const fs = Promise.promisifyAll(require('fs'))
const router = require('express').Router()
router.get('/list', async (req, res, next) => {
const files = await fs.readdirAsync('presentations')
res.json(files)
})
The problem is: my frontend receive 'Promise', but if I try to debug it shows me that files is an array.
I've tried not to use async/await syntax like that:
router.get('/list', (req, res, next) => {
fs.readdirAsync('presentations')
.then(files => {
res.json(files)
})
})
But result was the same: frontend still get Promise.
UPD: Problem was with frontend axios instance. It didn't resolve promise, so await for results solved a problem.
So, there are three parts. Reading, storing and sending.
Here's the reading part:
var fs = require('fs');
function readFiles(dirname, onFileContent, onError) {
fs.readdir(dirname, function(err, filenames) {
if (err) {
onError(err);
return;
}
onFileContent(filename);
});
});
});
}
Here's the storing part:
var data = {};
readFiles('dirname/', function(filename) {
data[filename] = filname;
}, function(err) {
throw err;
});
The sending part is up to you. You may want to send them one by one or after reading completion.
If you want to send files after reading completion you should either use sync versions of fs functions or use promises. Async callbacks is not a good style.

Resources