dev404
dev404

Reputation: 1098

How do you save the results of a mongoose query to a variable, so that its value can be used *later* in the code?

I know this question has been asked a few times, but none seem to answer the particular part of using the query results for later.

I also know the problem resides on the fact that queries are asynchronous, and perhaps this is the reason I cannot seem to find a satisfactory answer.

Here's what I'm trying to do:

I have a node project with several sections, each section with different content. These sections have individual properties, which I decided to store in a Model for later use.

So far (and for simplicity sake) I have the following schema:

const SectionSchema = new Schema({
    name: String,
    description: String
})

const Section = mongoose.model('Sections',SectionSchema)

I'd like to retrieve this data to be used in one of my layouts (a navigation header), so I tried something like this:

const express = require('express')
const app = express()

Section.find().then(function(docs){
    app.locals.sections = docs
})

console.log(app.locals.sections) // undefined

This obviously doesn't quite work due to find() being asynchronous, or rather, it does work but the values are populated at a different time. I know that if I do the console.log check inside the function I'd get a result, but that's not the concern, I want to store the data in app.locals so that I could later use it in one of my layouts.

Ideally I'd like to load this data once, before the server begins to listen to requests.

Feel free to correct me if I've made any wrong assumptions, I'm very new to node, so I don't quite know how to approach things quite yet.

Thanks in advance.

EDIT: I should've mentioned I'm using express.

Upvotes: 0

Views: 398

Answers (1)

Robert Moskal
Robert Moskal

Reputation: 22553

Your node app will likely be comprised of route handlers for http requests. app.locals.section will be undefined if you call it outside of the callback, but it will exist in the route handler.

Let's say you were using something like express or restify:

const app = restify.createServer()

app.get('/', (req, res) => {
    return res.json(app.locals.sections)
})

Section.find().then(function(docs){
    app.locals.sections = docs
})

console.log(app.locals.section) // is undefined

app.listen(8080-, ()=>{
  console.log('Server started 🌎  ',8080)
})

Actually, it might be undefined if the database call took a long time and or a user hit the app super soon after startup. Starting the server in the callback would ensure app.locals.section existed under every scenario:

Section.find().then(function(docs){
    app.locals.sections = docs
    app.listen(8080-, ()=>{
      console.log('Server started 🌎  ',8080)
    })
})

You can use async/await within a function to make it seem like you aren't using promises. But you can't use it at the top level of your module. See here: How can I use async/await at the top level?

It really would be fairly idiomatic to do all your app startup in a promise chain. It's a style of coding you are going to see a lot of.

Section.find().then((docs)=>{app.locals.sections = docs})
    .then (()=>{/*dosomething with app.locals.sections */})
    .then(startServer)


function startServer() {app.listen(8080-, ()=>{
  console.log('Server started 🌎  ',8080)
})}

Upvotes: 1

Related Questions