I'm using mongoose as below.
// .env
MONGO_URI=mongodb://127.0.0.1:27017/users
And mongoDB runs on app.js.
// app.js
const connect = () => mongoose.connect(process.env.MONGO_URI, dbOptions, (err) => {
if (err) {
return console.error('db error, connect in 5sec');
}
return app.listen(4000, () => console.log('App bounded'));
});
connect();
And there is an api as below.
// api.js
const user = require('./models/userSchema')
router.get('/', async(req, res) => {
const result = await user.find({name: 'test'})
res.send(result)
}
But now I want to make one server using diverse collenction in it.
So I have to change the env file.
// .env
MONGO_URI=mongodb://127.0.0.1:27017
And there are many collections in Mongodb like 'users', 'testers' etc.
And Api works like..
// api.js
const user = require('./models/userSchema')
const testers = require('./models/testerSchema')
router.get('/', async(req, res) => {
const result = await user.find({name: 'test'})
const result2 = await testers.find({name: 'test2'})
res.send(result, result2)
}
I found 'createConnection' But I don't know where I should put this and how to read each database and collections in my arquitecture. Could you recommend some solutions?
Thank you so much for reading it.
Related
Previously I had a working, single-file, application that was essentially:
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const io = require('socket.io')(server);
app.set('socketio', io);
import { MongoClient } from 'mongodb';
var mongo_client;
var connection_string = 'mongodb://localhost:27017' // for local development environment
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
}
});
/*
lots of middleware and route handling
*/
server.listen(PORT, () => {
console.log(`running on port ${PORT}`);
});
In middleware, within the same file, I would make a call to the database with something like:
var collection = mongo_client.db('users').collection('users');
var query = { };
var options = { };
try {
var user = await collection.findOne(query, options);
return user;
} catch (err) {
console.log('error: ' + err);
}
I'm in the process of refactoring and have put the MongoClient.connect() block in its own file.
config/database.js now looks like:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
let mongo_client;
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
console.log('FIRST: mongo_client: ');
console.log(mongo_client);
}
});
console.log('SECOND: mongo_client: ');
console.log(mongo_client);
module.exports = mongo_client;
To begin testing how to import it into another file, I am doing this in app.js:
const mongo_client = require('./config/database');
app.get('/', async (req, res) => {
const query = { username: 'my name' };
const collection = mongo_client.db('users').collection('users'); // <----- error is here, mongo_client is undefined
const result = await collection.findOne(query);
res.json({ result: result });
});
(note, when I better understand how all this works, I will move the route and middleware handling out of app.js)
The console logs are:
SECOND: mongo_client:
undefined
FIRST: mongo_client:
MongoClient {
// ... lots of mongodb client properties here
}
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'db' of undefined
So it seems like MongoClient.connect() is asynchronous and therefore mongo_client is not defined before I try and call mongo_client.db('users').collection('users').
My questions are:
How can this issue be resolved?
Why wasn't it happening before I extracted MongoClient.connect() out of app.js?
I've been reading articles like these:
MongoClient connection pooling
MongoClient docs
But they seem to either:
Include subsequent calls to the database within the MongoClient.connect() function block (and i can't wrap my whole app within that function) OR
They call app.listen() within the MongoClient.connect() function block (and this is also not feasible as I do several things with express() and http and socket.io)
Environment:
node v14.18.1
"mongodb": "^4.1.4" (native driver)
Edit:
I tried the following changes in config/database.js per this article.
I replaced this:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
let mongo_client;
MongoClient.connect(connection_string, (err, client) => {
if (err) {
throw err;
} else {
mongo_client = client;
}
});
module.exports = mongo_client;
with this:
const MongoClient = require('mongodb').MongoClient;
const { connection_string } = require('./environment_variables');
const mongo_client = new MongoClient(connection_string);
const is_this_the_right_thing_to_do = async () => {
await mongo_client.connect();
};
is_this_the_right_thing_to_do();
module.exports = mongo_client;
The error went away and it is returning a result, but I am not sure if this is the correct way to do it.
Also, I am not sure how I can use the single instance of mongo_client in all routes and middleware. I tried requiring it at the top of app.js, hoping that the routes and middleware (in other files) that were required afterwards would have access to it, but they don't, this was the error in the route middleware:
UnhandledPromiseRejectionWarning: ReferenceError: mongo_client is not defined
Another edit:
I've posted an answer on someone else's question here which may be what I am looking for, will update this post after further testing.
If you are trying to access database (mongo_client.db("your database name here") in your case) from other routes then you can maybe try something like this in your app.js or index.js.
const connectDB = async () => {
await client.connect();
db = client.db("greendeckData");
app.use((req, res, next) => {
req.db = db;
next();
});
app.use("/get", getRouter);
};
You can check the source from where I found the solution link, here
I'm creating my first API, I've used .env for database variables in this url:
const uri = `mongodb+srv://${mongo_user}:${mongo_pwd}#cluster0.ynvbj.mongodb.net/${mongo_db}?retryWrites=true&w=majority`
then created the client:
const client = new MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true })
Then, a main function that calls the functions that finds data from Mongo like users:
async function main() {
try {
await client.connect();
await getUser(client, 'users', mongo_db);
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
main().catch(console.err);
//------GET
//user by Dni
async function getUser(client, coll, mongo_db){
const collection = await client.db(`${mongo_db}`).collection(`${coll}`)
.find({dni: 38383838}).toArray(function(err, results) {
console.log(results);
return results;
})
};
And finally, I created the routes, but I get undefined. I've check everything and I don't get what's wrong.
const {getUser, getOrder, getProducts} = require('./db_connection')
const {Router} = require('express')
const router = Router()
//GET
//get user
router.get('/users', async (req, res) => {
let data = await getUser;
res.json({data});
})
module.exports = router;
And this is app.js
const express = require('express')
const app = express()
require('dotenv').config();
//settings
app.set('port', process.env.PORT || 3000)
//middlewares
app.use(express.urlencoded({extended:false}))
app.use(express.json())
//server
app.listen(app.get('port'), ()=> {
console.log(`Server running on port ${app.get('port')}`)
})
//require api
app.use('/api', require('./api_routes'))
It looks like your results are undefined, that could be for a lot of different reasons.
Where you have console.log(results) can you replace that with console.debug(err, results);
That might give you more insight into the reason it's not getting the results.
async function getUser(client, coll, mongo_db){
const collection = await client.db(`${mongo_db}`).collection(`${coll}`)
.find({dni: 38383838})
.toArray(function(err, results) {
console.debug(err, results);
return results;
})
};
I have set up my project to display posts from a MongoDB database. My localhost address is http://localhost:5000/api/posts and it displays my two saved posts. How can I add MongoDB _id to localhost adress to only display one post?
MongoDB _id: 6061890d59ec3b6abcb011fb
I have tried this:
http://localhost:5000/api/posts/6061890d59ec3b6abcb011fb
http://localhost:5000/api/posts/id:6061890d59ec3b6abcb011fb
http://localhost:5000/api/posts/_id:6061890d59ec3b6abcb011fb
All of them returns error Cannot GET /api/posts/and_the_above_parameters_for_each_example`
Index.js to connect my backend to my application.
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
//Middleware
app.use(bodyParser.json());
app.use(cors());
const posts = require("./routes/api/posts");
app.use("/api/posts", posts);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
posts.js to connect to MongoDB database. Below Password, MY_DATABASE and TABLE is changed to real values in my code.
const express = require("express");
const mongodb = require("mongodb");
const router = express.Router();
//Get posts
router.get("/", async (req, res) => {
const posts = await loadPostCollection();
res.send(await posts.find({}).toArray());
});
//Add post
router.post("/", async (req, res) => {
const posts = await loadPostCollection();
await posts.insertOne({
text: req.body.text,
createdAt: new Date(),
});
res.status(201).send();
});
router.delete("/:id", async (req, res) => {
const posts = await loadPostCollection();
await posts.deleteOne({
_id: req.params.id,
});
res.status(200).send();
});
async function loadPostCollection() {
const client = await mongodb.MongoClient.connect(
"mongodb+srv://MongoDB:PASSWORD#cluster0.5pnzd.mongodb.net/MY_DATABASE?retryWrites=true&w=majority",
{
useNewUrlParser: true,
useUnifiedTopology: true,
}
);
return client.db("MY_DATABASE").collection("TABLE");
}
module.exports = router;
PostService.js to display posts on localhost and methods to post and delete.
import axios from "axios";
const url = "http://localhost:5000/api/posts/";
class PostService {
// Get posts
static getPosts() {
return new Promise((resolve, reject) => {
axios
.get(url)
.then((res) => {
const data = res.data;
resolve(
data.map((post) => ({
...post, //spread operator
createdAt: new Date(post.createdAt),
}))
);
})
.catch((err) => {
reject(err);
});
});
}
// Create posts
static insertPost(text) {
return axios.post(url, {
text,
});
}
static deletePost(id) {
return axios.delete(`${url}${id}`);
}
}
export default PostService;
router.get("/:id", async (req, res) => {
const posts = await loadPostCollection();
res.send(await posts.findOne({
_id: req.params.id,
}));
});
Number 1: http://localhost:5000/api/posts/6061890d59ec3b6abcb011fb is correct, but you're going to need to create a new route to handle that request.
These are often called 'show' routes.
router.get("/:id", async (req, res) => {
// code to handle the logic of that request
// access the url parameter via: req.params.id
});
I have a tiny express server that I want to use to get some data from a collection in my database:
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const app = express();
const PORT = 3000;
const MONGO_URI = 'mongodb://127.0.0.1:27017/test';
async function myReport(schoolId) {
const client = new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
try {
await client.connect()
console.log("Hello!"); // This is never ran unless I remove "await" from the above line :S
const db = client.db();
const result = db.collection('states').find({}).map((a, b, c) => {
console.log("This never runs", a, b, c);
return "asdf";
});
return result;
} catch (err) {
console.log("ERROR", err);
}
client.close();
};
// Hoisting server
app.get('/api/reports/states/:id', async function (req, res, next) {
const report = myReport(req.params.id)
res.json(report); // {}
});
app.listen(PORT, (err) => {
console.log(`reporting listening in`, PORT);
});
I really don't know what I'm doing wrong here. Tried using .each, toArray and I'm not able to get the actual results as a list.
I've been following these docs: https://mongodb.github.io/node-mongodb-native/3.6/api/Cursor.html
Any idea what I'm doing wrong?
As per your saying:
await client.connect()
console.log("Hello!"); // This is never ran unless I remove "await" from the above line :S
I think connection is not establishing. I tried your code with a little bit modification. I created a cluster on Atlas Mongodb and used its URI as a connection string.
const express = require('express');
const MongoClient = require('mongodb').MongoClient;
const app = express();
const PORT = 3000;
const MONGO_URI = 'mongodb+srv://<username>:<password>#cluster0-oqotc.mongodb.net/<dbname>?retryWrites=true&w=majority';
const getListings = async () => {
const client = new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true });
try {
await client.connect()
console.log("Hello!"); // This will print now :-)
const listings = await client.db("sample_airbnb").collection("listingsAndReviews").findOne({});
return listings;
} catch (err) {
console.log("ERROR", err);
}
client.close();
};
// Hoisting server
app.get('/api/get-listings', async function (req, res, next) {
const report = await getListings()
res.json(report);
});
app.listen(PORT, (err) => {
console.log(`reporting listening in`, PORT);
});
You need to change username, password and dbname with your ones.
Note: While using Atlas Mongodb Cluster, if you are getting connection error, you need to whitelist your ip as well.
Hope it will help you. Thanks
You defined myReport as an asynchronous function which returns a promise. Add toArray() back into your code and then get your report like this
app.get('/api/reports/states/:id', async function (req, res, next) {
myReport(req.params.id).then(report => {
res.json(report);
});
});
Because its the call to res.json is also in an asynchronous function I think you can also do
app.get('/api/reports/states/:id', async function (req, res, next) {
const report = await myReport(req.params.id);
res.json(report);
});
I'm trying to figure out why my data hasn't been saving in MongoDB, despite calling the .save() method. I'm aware that this is an asynchronous process, but as you can see below, I set up a callback using .then() - which executes without error. The target database has a single empty collection in it, so I was expecting the data to appear there or in a new collection, neither of which happened. Here's the code for my index.js file:
const express = require('express');
const mongoose = require('mongoose');
const { body, validationResult } = require('express-validator/check');
const router = express.Router();
const Registration = mongoose.model('Registration');
router.get('/', (req, res) => {
res.render('form', { title: 'Registration Form'});
});
router.post('/',
[body('name')
.isLength(1)
.withMessage('Please enter a name.'),
body('email')
.isLength(1)
.withMessage('Please enter an email address.')
],
(req, res) => {
//console.log(req.body);
const errors = validationResult(req);
if(errors.isEmpty)
{
const registration = new Registration(req.body);
//registration.markModified('collection_1');
registration.save()
.then(() => { res.send('Thank you for your registration!'); })
.catch(() => { res.send('Sorry! Something went wrong.'); });
} else {
// re-renders form with error message
res.render('form', {
title: 'Registration form',
errors: errors.array(),
data: req.body,
});
}
});
module.exports = router;
Let me know if any other files would be useful. Any assistance would be greatly appreciated.
EDIT: Here's my start.js file.
require('dotenv').config();
const mongoose = require('mongoose');
require('./models/Registration');
mongoose.connect(process.env.DATABASE, { useMongoClient: true });
mongoose.Promise = global.Promise;
mongoose.connection
.on('connected', () => {
console.log(`Mongoose connection open on ${process.env.DATABASE}`);
})
.on('error', (err) => {
console.log(`Connection error: ${err.message}`);
});
const app = require('./app');
const server = app.listen(3000, () => {
console.log(`Express is running on port ${server.address().port}`);
});