How to export the data present in await - node.js

I am struggling with promises, async/await. Here i want to export the gridfs object to another file but when I import it using require and execute console.log(gridfs) it gives an empty object. Can anyone help how i could export gridfs
const mongoose = require('mongoose');
async ()=> {
await mongoose.connection.on('connected', ()=>{
const gridfs = require('mongoose-gridfs')({
collection: 'sharedfiles',
model: 'SharedFiles',
mongooseconnection: mongoose.connection
});
global.sharedfile = gridfs;
});
module.exports = sharedfile;
}
file where i need gridfs:
const sharedfile = require('path to above file');
//under another promise
rslt.data.on('end', ()=>{
console.log(sharedfile);
}
the result i am getting is {} because console.log runs before the script loads can anyone suggest how to fix it. (I am new with promises and async/await).

Two things.
First, you never actually call your anonymous async function.
Second, your export is in the scope of that function so it never gets set. Therefore your require returns the empty contents '{}' of module.export.
Try something like this.
const mongoose = require('mongoose');
module.exports = async ()=> {
await mongoose.connection.on('connected', ()=>{
const gridfs = require('mongoose-gridfs')({
collection: 'sharedfiles',
model: 'SharedFiles',
mongooseconnection: mongoose.connection
});
global.sharedfile = gridfs;
return gridfs;
});
}
Then, your export provide the async function (a Promise) to the requiring code. You invoke it with () and use .next to handle its returned result.
require('path to above file') ()
.next(
function (gridfs) {
console.log (gridfs)
} )
.catch (...)
Or you can require it and then invoke it later.
const getGridFs = require('path to above file')
...
getGridFs ( )
.next (gridfs => {console.log(gridfs)})
.catch (error => {console.error(error)})
Be patient: you'll figure it out.

Related

How to create a database object using MongoDB with Next.js?

I followed this tutorial to set up MongoDB in my Next.js application: https://www.mongodb.com/developer/languages/javascript/nextjs-with-mongodb.
In a file called mongodb-config.js, I have
import { MongoClient } from 'mongodb'
const uri = process.env.MONGODB_URI
const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
}
let client;
let dbPromise;
if (!process.env.MONGODB_URI) {
throw new Error('Please add your Mongo URI to .env.local')
}
// In production mode, it's best to not use a global variable.
client = new MongoClient(uri, options)
dbPromise = client.connect()
// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default dbPromise
The above is how I configure my database.
When I need to use the database for my API, I do:
import dbPromise from "database/mongodb-config";
let db;
dbPromise.then((value) => {
const client = value;
db = client.db("database_name");
})
.catch((error)=>{
console.error(error);
});
db will be the variable that links to my database.
Now, I want to simplify this process and put everything in one file.
In the portion where I set up dbPromise in mongodb-config.js, I initialize the DB variable and export it:
client = new MongoClient(uri, options)
dbPromise = client.connect()
let db;
dbPromise.then((value)=>{
const client = value;
db = client.db("datatbase_name");
})
.catch((error)=>{
console.error(error);
});
export default db
I totally expect this to work. However, when I import db to another file and use it, the db is null. However, I waited until the dbPromise is resolved to pass db a value.
Use await like this.
dbPromise = client.connect()
let db;
await dbPromise.then((value)=>{
const client = value;
db = client.db("datatbase_name");
})
.catch(error){
console.error(error);
}
export default db
because mongoose is asynchronous so we have to use await before dbPromise otherwise export will be executed first before dbPromise.
Try this, instead of returning db, return an async function, which you can all with await, in other parts to obtain, the connection to the database, the function will not try to make repeated connections, if they are already present:
let db, connection;
async function dbSetup() {
try {
if(!connection) {
connection = await client.connect();
}
if(!db) {
db = await connection.db("datatbase_name");
}
return db;
} catch(error){
console.error(error);
}
}
export default dbSetup;
Another way is if you have node version 14.8 or greater, you can use top-level await functionality, which is basically using await, not inside an async function, like this:
client = new MongoClient(uri, options);
dbPromise = await client.connect();
let db = await dbPromise.db("datatbase_name");
export default db;

Using async/await with Mongoose recent version(6.2.7)

I have the following code to connect to Mongoose DB using Node.js. But I get some kind of warning or refactor notification from VS code to remove await from the the part when I try to connect.
Its say that: await has no effect in this kind of expression
From the documentation of Mongoose inside index.d.ts file;
export function connect(uri: string, options?: ConnectOptions): Promise<Mongoose>; returns promise
So I try to do like this:
// Provide connection to a new in-memory database server.
const connect = async () => {
// NOTE: before establishing a new connection close previous
await mongoose.disconnect()
mongoServer = await MongoMemoryServer.create()
try {
const mongoUri = await mongoServer.getUri()
await mongoose.connect(mongoUri, opts) // No need to use await??
} catch (error) {
console.log(error)
}

Unhandled promise rejection. Can't connect to server

I was wondering what I'm doing wrong here.I couldn't connect to the server and gives me a message that a promise was rejected and was not handled by catch(). Am i missing an async function somewhere? Thanks in advance.
const mongoose = require("mongoose");
const Dishes = require("./models/dishes");
const url = "mongodb://localhost:27017/conFusion";
const connect = mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
connect.then(db => {
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
newDish
.save()
.then(dish => {
console.log(dish);
return Dishes.find({});
})
.then(dishes => {
console.log(dishes);
return Dishes.remove({});
})
.then(() => {
return mongoose.connection.close();
})
.catch(err => {
console.log(err);
});
});
You have no rejection handler on the promise from mongoose.connect itself (your connect constant). you only use then, not catch, and you don't supply the second argument to then.
So the minimum change is:
connect.then(db => {
// ...
})
.catch(error => { // ***
// ...handle/report error connecting... // ***
}); // ***
Am i missing an async function somewhere?
No, but using one might make the code easier to follow (this is subjective). The purpose of async/await is to make it possible to write code with our usual flow-control structures while using promises.
For instance, if you can't use top-level await or you don't want to, you could wrap all your code in an immediately-invoked async function like this:
const mongoose = require("mongoose");
const Dishes = require("./models/dishes");
const url = "mongodb://localhost:27017/conFusion";
// If you can't use top level `await`, you can use an `async` IIFE
(async () => {
const connect = await mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
const dish = await newDish.save();
console.log(dish);
const dishes = await Dishes.find({});
console.log(dishes);
await Dishes.remove({});
await mongoose.connection.close();
})().catch(error => {
// ...handle/report error...
});
If you can use top-level await and you want to (it does mean you have to switch to using JavaScript's own module syntax instead of CJS modules, but IMHO that's a good thing anyway), you can do this:
import mongoose from "mongoose";
import Dishes from "./models/dishes.js";
const url = "mongodb://localhost:27017/conFusion";
try {
const connect = await mongoose.connect(url);
mongoose.set("useUnifiedTopology", true);
mongoose.set("useNewUrlParser", true);
console.log("Connected correctly to server");
var newDish = Dishes({
name: "Uthappizza",
description: "test"
});
const dish = await newDish.save();
console.log(dish);
const dishes = await Dishes.find({});
console.log(dishes);
await Dishes.remove({});
await mongoose.connection.close();
} catch (error) {
// ...handle/report error...
}
Note, though, that you'd probably only want to do that in your top-level module (which it looks like this is), since it holds up resolution of the module tree. That's fine for the entry point module, or a module that can't create its exports until a promise settles, but for the logic in the code you've shown it would probably only be appropriate in the entry point module.

Why does it return undefined?

So I have two files, server.js and db.js
Now Here is the code that I am having issue with :
server.js :
var DB = require('./db')
app.get("/test", (req, res) => {
console.log(DB.getPostAll())
})
db.js :
MongoClient.connect(uri, { useNewUrlParser: true })
.then(function (db) {
console.log("Connected")
var dbo = db.db('test')
module.exports.getPostAll = function getPostAll() {
return (
dbo.collection('posts').find({}).toArray(function (err, res) {
if (err) throw err;
else return res
})
)
}
})
.catch(function (err) {
})
Sorry for the silly question. But can anyone say what am I doing wrong here ?
I am trying to use two returns. Before this I tried to use a variable in place of the returns in db.js's getPostAll. But it also returns undefined.
There are plenty of bad practices in the code, I'll try to cover some.
Your db.js file triggers an asynchronous action the moment it is required.
You are not returning the Promise from the db.js file, hence your server.js file starts executing code without knowing if the connection to the DB was fulfilled/pending or rejected
You are exporting a method after a function is executed, this leads to many odd and unexpected side effects. It is best to define all your exports at the top level of the file.
The simplest way to solve your issue is:
server.js :
const connectDB = require('./db')
connectDB().then((db) => {
app.get("/test", (req, res) => {
console.log(db.getPostAll())
})
app.listen(...); // lift the server ONLY when the db is connected
});
db.js :
module.exports = function connectDB() {
return MongoClient.connect(uri, { useNewUrlParser: true })
.then(function (db) {
console.log("Connected")
var dbo = db.db('test')
return {
getPostAll() {
return dbo.collection('posts').find({}).toArray()
}
}
})
}
In the db.js file, I am exporting a function that returns a promise, this way I can tell when the connection is complete (by having the promise resolved), it returns an object with all the db methods you need (getPostAll).
In the server.js file, I am waiting for the async connection to be established before I lift the app, this way I know i have my application in a ready state when it is served, and I have the db methods readily available for my app.
You are getting undefined as your promise has not resolved. Try using an async function and await your db operation to finish. See working with async funtions . you can also try mongoose object modeling as your mongodb client.

how to export array created in a promise

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});
};

Resources