how to export array created in a promise - node.js

I'm trying to follow a project from mozilla developer local library tutorial
. It includes learning how to use nodejs, express, mongoose and mongoDB. I am swapping out mongoose and mongoDB with firestore and using firebase functions to eventually host with firebase hosting.
The project uses a model, view, controller structure. I really want to stick to this structure to help break everything down into manageable chunks.
The first problem I’m trying to resolve is to return an array that I created in a promise to be exported as a function that is called from booksController.js.
A concise view of the structure I have is:
index.js
const booksRouter = require('./routes/books');
app.use('/books', booksRouter);
routes/books.js
const books_controller = require('../controllers/booksController');
router.get('/', books_controller.index);
controllers/booksController.js
var Books = require('..models/books');
exports.index = function(req, res){
var books = Books.AllBooks();
res.render('books',{books: books});
};
models/books.js
var booksRef = db.collection(books);
var allBooks = booksRef.get()
.then(snapshot => {
var books = [];
Snapshot.forEach(doc => {
books.push({doc.data: doc.data()});
});
});
.catch(err =>{
console.log('Error getting documents', err);
});
exports.AllBooks = function(req, res){
return books;
}
I've tried wrapping the promise in a function and returning the array to export it but I get undefined when console logging in booksController.js.
Can someone please enlighten me and complete the puzzle of how to return the array so it can be exported.

What you're trying to do, can't be done using require, since require is synchronous.
Instead what you can do is export a function that will fetch the books asynchronously, and use that function in your controller.
models/books.js
const booksRef = db.collection(books);
async function getBooks() {
// You can implement a cache and fetch the books only once.
const snapshot = await booksRef.get()
const books = [];
snapshot.forEach(doc => {
books.push({
doc.data: doc.data()
});
});
return books;
}
module.exports = getBooks;
controllers/booksController.js
const getBooks = require('./../models/books');
exports.index = async function(req, res){
const books = await getBooks(); // Handle exception
res.render('books',{books: books});
};

Related

Constructing an array from db response using async await in node

I'm trying to construct an array of database responses using the async await syntax.
I understand that since async await is just a way of wrapping promises in nicer syntax, it should be easy to convert the promise-oriented instructions given here into that format.
Here's what I have:
const sqlite3 = require('sqlite3').verbose();
const express = require('express')
const bodyParser = require("body-parser")
async function getDB(dbname) {
let db = new sqlite3.Database(dbname)
return db;
}
app.get('/', async (req,res) => {
let db = await getDB(dbname)
let r = await db.serialize(() => {
let results = []
db.each(`SELECT * FROM MyTable LIMIT 3`, (err, row) => {
if (err) {
console.error(err.message);
}
let m = row.id;
console.log(m);
results.push(m)
});
return results
});
res.send(await r)
})
app.listen(port, ipaddr);
When I call this, the response is:
{"open":false,"filename":"dbname.db","mode":65542}
but I expect:
[1,2,3]
What's happening and how do I fix it?
In the console, I can see the records are being retrieved:
[
1,
2,
3
] # this is exactly the thing I want returned! Why can't I retrieve it?
So I know the database call actually works, but I can't seem to get them /into/ any object I can get out of the await.
Try the sqlite package, rather than the sqlite3 used in the demo. It has better support for async await.

How to write a static method in Mongoose that gets all documents?

I'm new to Mongoose. I wrote a statics method and a instance method for a Mongoose schema named 'questionSchema' and exported it like so:
var questionSchema = new Schema({
...
})
questionSchema.methods.createQuestion = function(){
return this.save(function(err){
if(err){
return err
};
return 'Saved the question';
});
};
questionSchema.statics.getAllQ = function(){
return this.find({}, function(err, res){
if(err){
return err
};
return res;
});
}
module.exports = mongoose.model('Question', questionSchema)
Then in a route in my Node/Express server, I imported the it as a model, and tried calling the static method, which should return all the documents under the Question model:
const Question = require('../models/question.js');
...
router.post('/qcrud/submit', (req, res) => {
let reqBody = req.body;
var newQuestion = new Question({reqBody});
newQuestion.createQuestion();
})
router.get('/qcrud/getAll',(req, res) => {
let qArr = Question.getAllQ()
res.send(qArr);
});
However, it returns a Query object, not an array like I expected. I looked around and saw on MDN that
'If you don't specify a callback then the API will return a variable
of type Query.'
I did specify a callback, but still got the Query object. First of all, am I using my static and instance methods right? Are the documents even saving? And how do I access the array documents saved?
If you're using Node 8.x you can utilize async/await
This way your code will look more synchronous:
questionSchema.statics.getAllQ = async () => {
return await this.find({});
}
router.get('/qcrud/getAll',async (req, res) => {
let qArr = await Question.getAllQ();
res.send(qArr);
});
You can find a really nice article that is explaining how to use Mongoose with async/await here.

[Koa]404 while passing throught the routeur

I'm having some trouble with the Koa framework. I'm trying to build a pretty basic server by I'm having a problem with my router. The ctx always return 404 despite passing in my functions.
Some code :
//www.js
const Koa = require('koa');
const app = new Koa();
const version = require('./routes/version');
app.listen(config.port, () => {
console.log('Server is listenning on port ' + config.port);
});
app.use(version.routes());
app.use(ctx => {
console.log ('test')
});
//version.js
const Router = require('koa-router');
const router = new Router();
router.prefix('/version');
router.use((ctx, next) => {
ctx.vFactory = new VersionFactory(ctx.app.db);
next();
});
router.get('/', getAllVersions);
async function getAllVersions(ctx, next) {
const ret = await ctx.vFactory.getAllVersions();
ctx.body = JSON.stringify(ret.recordset);
console.log(ctx.body)
await next();
}
I've checked a few threads. Most of the time, the problem seems to come from a non Promise based function in the await part of the router function. Here it is a simple DAO using mssql which is pretty promise based.
class DaoVersion {
constructor(db) {
this.pool = db;
}
async getAllVersions() {
const me = this;
return new Promise((resolve) => {
const ret= me.pool
.query(getVersion);
resolve(ret);
});
}
}
The console output seems good. I have my ctx.body set with my db data but if I try to check the whole context, I still have a 404. More interesting, if I try to ctx.res.write (using default node response) I got the "already end" message. So it seems Koa have sent the message before passing threw my function.
Any idea why and how I could correct that ?
Koa default response.status code is 404, unlike node's res.statusCode which defaults to 200.
Koa changes the default status code to 200 - when your route set's a non empty value to ctx.body or in some cases you can manually change (like if you need to set it to 202) it by using ctx.status = xxx.
You can use this documentation for reference: https://github.com/koajs/koa/blob/master/docs/api/response.md
Also, your route should be an async function:
router.get('/', async(ctx, next) => {
ctx.body = await getAllVersions
await next()
}

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.

run mongoose methods synchronously in node.js

I'm trying to call getMarchandiseList() method that call a mongoose method " find({}) " in my marchandise router.
the problem is when I send the responce I find that I get a result=0 before the find({}) method send it's result
//the result should be
{id_marchandise : 01 , libelle_marchandise : 'fer'}.
[MarchandiseDAO.js][1]
*
var express = require("express");
var router = express.Router();
var marchandiseDAO = require ('../DAO/marchandises');
router.get('/listmarchandise', function (req,res) {
var marchandise = new marchandiseDAO();
marchandise.getMarchandiseList();
res.send("result" +marchandise.result);
});
module.exports = router;
*
[marchandiserouter.js][2]
var model = require("../Models/model");
var marchandiseDAO = function () {
console.log("get instance ");
this.result = 0 ;
}
marchandiseDAO.prototype.getMarchandiseList = function () {
console.log("begin");
model.MarchandiseModel.find({},function(err,marchandiselist){
console.log("traitement");
if(err) result = err;
else result = marchandiselist;
});
console.log("end");
}
You cannot run mongoose methods synchronously. But if you use a modern version of Node then you can make it appear as if it was run asynchronously.
Unfortunately you posted your code examples as screenshots which would require me to use Photoshop to fix your code, which is obviously not worth the hassle. So instead I will show you a general solution.
Using normal promises:
Model.find({...}).then(data => {
// you can only use data here
}).catch(err => {
// always handle errors
});
Using await:
try {
let data = await Model.find({...});
// you can only use data here
} catch (err) {
// always handle errors
}
The second example can only be used in an async function. See this for more info:
try/catch blocks with async/await
Use await outside async
Using acyns/await in Node 6 with Babel
When do async methods throw and how do you catch them?
using promises in node.js to create and compare two arrays
Keeping Promise Chains Readable
function will return null from javascript post/get

Resources