0

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.

rich_web
  • 81
  • 1
  • 9
  • 1
    Exports can't be "exported later". They are exported on the module's initial execution, and thus in an async operation that produces an array, the array cannot be exported. The best you can do is export a function that performs the asynchronous request. – Andrew Li Jul 08 '18 at 15:47
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Estus Flask Jul 08 '18 at 15:47
  • While not an exact duplicate, estus' link is a very good read into why your code doesn't work (aside from the fact `var` is not in scope where you try to export it). – Andrew Li Jul 08 '18 at 15:52
  • 1
    It looks like you don't intend to query the value of the database on each access of `AllBooks`. If you want to load just once and export the same array each time, you could also export a Promise that resolves to the final value, and all callers just have to wait on that. – Doug Stevenson Jul 08 '18 at 15:55
  • @DougStevenson exporting the promise directly may not be the perfect solution, since if the promise rejects, he will have a rejected promise in the require cache. So exporting a function with a cache implemented would be better imo. Unless he makes sure that that promise will never reject. – Marcos Casagrande Jul 08 '18 at 16:03
  • WOW! Thanks. A response from Firebase Royalty :) Thanks for all the videos you've put up on the Firebase youtube channel. I’ll try the answer I got from Marcos Casagrande first. Then I’ll try and digest your answer. – rich_web Jul 08 '18 at 16:08
  • @MarcosCasagrande Exporting a promise that's already in flight has the advantage of pre-fetching the results so that they may be available quickly on first access. It could also be backed by a listener (instead of get()) that's persistent in waiting for the results, and won't reject unless the query can't complete because of security rules. But if we're going this far, we've got an architecture that's bigger in scope than just splitting code up into modules! – Doug Stevenson Jul 08 '18 at 16:13

1 Answers1

0

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});
};
Marcos Casagrande
  • 37,983
  • 8
  • 84
  • 98
  • Thank you very much for the quick response. I'll give it a try then mark your answer as the solution. Thanks :) – rich_web Jul 08 '18 at 15:58
  • You're welcome, let me know if you have any trouble. Have in mind that `async/await` is available since node v7.6. – Marcos Casagrande Jul 08 '18 at 15:59
  • It works :) just have to learn how it works now :) thank you very much for helping move on from that problem. I need to watch a few more videos on async/await so I can understand how it works. – rich_web Jul 08 '18 at 17:40