I want to add data to my MongoDB collection. I'm getting this data via a local Flask API. I'm GETting the data on my React Frontend and it's displaying fine. I'm not sure why I can't do the same thing on my express nodejs backend. I want to get that same data and use it to build the entity that I'm going to store.
This is how I'm attempting to get the data
app.get('/', async (req, res) => {
let initialData = {};
axios.get('http://localhost:3000/details').then((res) => {
initialData = res.data;
});
const recruit = new RecruitModel({ email:initialData.email,
mobile_number:initialData.mobile_number,
name:initialData.name});
try {
await recruit.save()
res.send("inserted data")
} catch (error) {
console.log(error)
}
})
I'm pretty sure something wrong there and nowhere else. Because if I pass static information instead it's correctly stored, no issues.
You are saving to the database's Recruit Collection before the promise is resolved. Since data to save in the Recruit Collection is dependent upon the result from the API which will initially return the promise, therefore, use promise resolving functions to wait for its result.
Solution#1 (using .then function):
app.get('/', async (req, res) => {
let initialData = {};
try {
axios.get('http://localhost:3000/details').then((response) => {
initialData = response.data;
const recruit = new RecruitModel({
email: initialData.email,
mobile_number: initialData.mobile_number,
name: initialData.name,
});
recruit.save().then((response) => res.send('inserted data'));
});
} catch (error) {
console.log(error);
}
});
Solution#2 (using async await keywords):
app.get('/', async (req, res) => {
try {
const response = await axios.get('http://localhost:3000/details');
const recruit = new RecruitModel({
email: response.data.email,
mobile_number: response.data.mobile_number,
name: response.data.name,
});
await recruit.save();
res.send('inserted data');
} catch (error) {
console.log(error);
}
});
Either solution will work in your case.
I am following a tutorial to make a blog, and for the MongoDB connection in the server.js file, the instructor made a boiler connection function withDB. Operations and res are props of withDB function. In line 6, is operations a function passed a prop of the withDB functions?
Below is the withDB function.
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('my-blog');
await operations(db); // is operations a function that takes db as its props?
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error });
}
}
Using withDB in a function
app.get('/api/articles/:name', async (req, res) => {
withDB(async (db) => {
const articleName = req.params.name;
const articleInfo = await db.collection('articles').findOne({ name: articleName })
res.status(200).json(articleInfo);
}, res);
})
yes actually operations is your callback function, you call it with db as param once you initialize your database connection.
Maybe you're not comfortable with ES6 arrow function syntax. you can find in Mdn doc a simple example with old regular function, and in your case it could be :
function findArticlesByName(articleName) {
return function(db) {
return db.collection('articles').findOne({ name:
articleName });
}
}
async function withDB(callback) {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('my-blog');
return callback(db);
} catch (error) {
throw new Error({ message: 'Error connecting to db', error });
} finally {
client?.close();
}
}
app.get('/api/articles/:name', async (req, res) => {
try {
const articleInfo = await withDB(findArticlesByName(req.params.name));
res.status(200).json(articleInfo);
} catch(error) {
res.status(500).json(error);
}
})
Conclusion, you could easily inline your callback function like in your example, but maybe it's more understandable that way.
Moreover, you should avoid to use a wrapper in order to create and close your db connection after each request, because it could occur some weird errors. Databases connections or any resource-intensive tasks should be shared as much as possible.
So a better solution is to create a specific class with the default implementation of your singleton, construct a single instance of your connection at the top of your app, pass the single instance into each module that needs it then close your connection just before exiting your app.
Hope it'll help.
I am building tests for my node/express controller methods and using #shelf/jest-mongodb. I am creating a document first, and then when I try to find that I have to run find twice from model in order to get the results. It should get the results in the first find instead.
test.js
const { Subscription } = require('../src/models/subscription.schemaModel'); // model
const {
createSubscription,
} = require('../src/controllers/subscription.controller');
const subData = {...};
beforeAll(async () => {
await mongoose.connect(
process.env.MONGO_URL,
{ useNewUrlParser: true, useUnifiedTopology: true },
(err) => {
if (err) {
console.error(err);
process.exit(1);
}
}
);
});
afterAll(async () => {
await mongoose.connection.close();
});
describe('creates a subscription ', () => {
it('can be created correctly', async () => {
const sub = await createSubscription(subData);
await Subscription.find(); // if I comment out this line, I would get 0 results.
const subs = await Subscription.find();
expect(subs[0].items[0].sku).toBe(233234);
});
});
subscription.controller.js
const Mongoose = require('mongoose');
const { Subscription } = require('../models/subscription.schemaModel');
const isTestEnv = process.env.NODE_ENV === 'test';
module.exports.createSubscription = async (data) => {
try {
let error = null;
const doc = new Subscription(data);
doc.accountId = Mongoose.Types.ObjectId(doc.accountId);
await doc.save(function (err) {
if (err) {
logger.error(`createSubscription saving ${err}`);
error = err;
}
});
if (!error) {
logger.info(
`Subscription created => id: ${doc._id} store: ${doc.store}`
);
return doc;
} else {
return error;
}
} catch (err) {
logger.error(`createSubscription ${err}`);
}
};
The schemaModel file essentially contains the schema and exports model. Everything seems to work fine if I would do all the operations in the test file (schema+model+controller module)which defeats the purpose of testing my modules but not if I am importing. In this case I would have to run find() twice to get the results.
I have been trying multiple things from what I could find from googling, but no luck! Any help or lead would be appreciated. Also let me know if you need any other details.
Thank you!!
The only problem that posted code contains is that Mongoose promise API is mixed with legacy callback API. It appears that save results in race condition that is has been circumvented by random delay that extra find provides.
Although Mongoose documentation mentions that methods unconditionally return promises, a common pattern for JavaScript APIs that support both promises and callbacks is to enable promise control flow by omitting callback argument, and vice versa. This is most likely what happens here.
A way to avoid race conditions in such cases is to stick to promise control flow, e.g.:
beforeAll(async () => {
try {
await mongoose.connect(
process.env.MONGO_URL,
{ useNewUrlParser: true, useUnifiedTopology: true },
)
} catch (err) {
console.error(err);
process.exit(1);
}
});
i am using firebase cloud functions for my current project (learning management system using reactjs,nodejs and firebase).i have over 100 routes in single function called api. so i would like to know what is the performance difference if i split these routes into multiple functions.
sample code
app.get("/getReviews", (req, res) => {
let queryString = req.query.id || "";
console.log(req.query.id);
admin
.firestore()
.collection("users")
.doc(queryString)
.collection("reviews")
.get()
.then((data) => {
let reviews = [];
data.forEach((doc) => {
reviews.push(doc.data());
});
console.log(reviews);
return res.status(200).json(reviews);
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err });
});
});
exports.api = functions.https.onRequest(app);
I'm learning how to use Sqlite3 with Node, and I'm running into a strange issue. In componentWillMount() on my react front end's main App.js, I make an axios request to the route /all so I can populate a contact list.
What's weird is that, when I hit my other route, /add with a different axios request when I add a contact, it reaches my then() as such,
axios
.post('/add', contactData)
.then(res =>
console.log(`Contact ${contactData.name} added successfully`)
)
.catch(err => console.log('Error encountered: ', err));
With a slight delay too, because I setState before making my axios request, which makes me think that the contact is added into the contacts table.
But when I access localhost:5000/all directly, I receive an empty array [] as the response. I'm not sure what's going on.
Here's my server.js
const express = require('express');
const sqlite3 = require('sqlite3');
const path = require('path');
const cors = require('cors');
const dbName = 'my.db';
const tableName = 'Contacts';
const dbPath = path.resolve(__dirname, dbName);
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.listen(port, () => console.log(`Server running on port ${port}`));
app.get('/all', (req, res) => {
let db = new sqlite3.Database(dbPath);
let sql = `SELECT number FROM ${tableName}`;
db.run(
`CREATE TABLE IF NOT EXISTS ${tableName}(name text, number text, address text)`
);
db.all(sql, [], (err, rows) => {
if (err) {
return res.status(500).json(err);
} else {
return res.json(rows);
}
});
});
app.post('/add', (req, res) => {
let db = new sqlite3.Database(dbPath);
db.run(
`INSERT INTO ${tableName}(name, number, address) VALUES(${req.name},${
req.number
},${req.address})`,
[],
err => {
if (err) return res.status(500).json(err);
}
);
return res.json({ msg: 'success' });
});
Edit:
I should note that when I navigate to /all I get this,
and when I try to post to /add, I get the error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
No where am I sending multiple responses though.
I would not init your db and create your table every time you hit /all.
Try this:
// get this out of the `/all` route, no need to initialize the db object over and over again
let db = new sqlite3.Database(dbPath);
// also leave this outside the `/all` route:
// no need to create the table over and over again.
db.run(
`CREATE TABLE IF NOT EXISTS ${tableName}(name text, number text, address text)`
);
app.get('/all', (req, res) => {
let sql = `SELECT number FROM ${tableName}`;
// according to the sqlite3 api, the second parameter is optional, so just leave it out:
db.all(sql, (err, rows) => {
if (err) return res.status(500).json(err); // if you use return, you don't need 'else' because the code will never reach it.
res.json(rows)
});
});
Your /add route also looks a bit off. Try this:
app.post('/add', (req, res) => {
// let db = new sqlite3.Database(dbPath); // remove this as you already defined it at the beginning of your code.
db.run(
`INSERT INTO ${tableName}(name, number, address) VALUES(${req.name},${req.number},${req.address})`,
err => {
if (err) return res.status(500).json(err);
res.json({ msg: 'success' }); // put the success in the callback (after the query is run!), else, when you get an error, express.js will try to send an error message AND a success message giving you the error "Can't send headers after they are sent"
}
);
});
You can fix this issue Using Async-Await in Node.js.
JavaScript is asynchronous in nature and so is Node. Asynchronous programming is a design pattern which ensures the non-blocking code execution.
The non-blocking code does not prevent the execution of a piece of code. In general, if we execute in Synchronous manner i.e one after another we unnecessarily stop the execution of those codes which is not depended on the one you are executing.
Asynchronous does exactly opposite, the asynchronous code executes without having any dependency and no order. This improves system efficiency and throughput.
But in some case, we need to wait for the response.
app.get('/all',async (req, res) => {
let db = new sqlite3.Database(dbPath);
let sql = `SELECT number FROM ${tableName}`;
await db.run(
`CREATE TABLE IF NOT EXISTS ${tableName}(name text, number text, address text)`
);
await db.all(sql, [], (err, rows) => {
if (err) {
return res.status(500).json(err);
} else {
return res.json(rows);
}
});
});
app.post('/add',async (req, res) => {
let db = new sqlite3.Database(dbPath);
await db.run(
`INSERT INTO ${tableName}(name, number, address) VALUES(${req.name},${
req.number
},${req.address})`,
[],
err => {
if (err) return res.status(500).json(err);
}
);
return res.json({ msg: 'success' });
})
await db.all(sql, [], async (err, rows) => {
if (err) {
await return res.status(500).json(err);
} else {
await return res.json(rows);
}
});