Express and MongoDB without Mongoose - mongodb

This is not so much of a question but more of a consult request. I couldn't find resources to check my method's validity so I would like to hear MongoDB experts' opinion.
I was playing around with MongoDB and came up with this middleware method to pass client to my routes. I have this Express middleware:
const addClientToRequest = async (req, _, next) => {
const client = new MongoClient(uri);
await client.connect();
req.client = client;
next();
};
app.use(addClientToRequest);
After that, I use req.client in my routes to access my database.
app.get("/:id", async (req, res) => {
const client = req.client;
const id = req.params.id;
try {
const data = await client.db("mydb").collection("mycollection").findOne({ id });
if (data) return res.status(200).json(data);
} catch (error) {
return res
.status(500)
.json({ message: "Error fetching requested data", error });
}
return res.status(404).json({ message: "Requested data cannot be found" });
});
What would be a problem in this approach? Is it okay to use MongoDB client like this?

In my experience, we have always defined a separate utility to load a connection pool at the app startup and then reused those connections.
In the above approach, you seem to be creating a new connection for every HTTP request that is made and then not terminating (or) closing the connection. This may be expensive for a large app.
db.util.js
const { MongoClient } = require("mongodb");
const uri = `mongodb://${process.env.DB_USER}:${process.env.DB_PASSWORD}#localhost:27017/${process.env.DATABASE}?maxPoolSize=2-&w=majority`;
const client = new MongoClient(uri);
const init = async () => {
try {
await client.connect();
console.log("Connected");
} catch (error) {
console.log(error);
}
};
const getClient = () => {
return client;
};
module.exports.init = init;
module.exports.getClient = getClient;
app.js
//Import modules
require("dotenv").config({ path: __dirname + "/.env" });
const express = require("express");
const dogRoutes = require("./routes/dog.routes");
const db = require("./utils/db.util");
// Define PORT for HTTP Server
const PORT = 9900;
// Initialize Express
const app = express();
app.use(express.json());
app.use(dogRoutes);
(async () => {
await db.init();
app.listen(PORT, (err) => {
console.log(`Server is up at localhost ${PORT}`);
});
})();

I think that what you could do is to put the client outside of the middleware, so you doesn't re define it and re connect to it each time a request is done.
To do so, simply define it and connect before the middleware, and in the middleware, set the client as req.mongoClient or how you want to name it.
const client = new MongoClient(uri);
await client.connect(); // if this is outside of an async function, either use an async function like (async () => {..script..})(), either define a variable isClientReady and set it on true after the promise resolved.
const addClientToRequest = (req, _, next) => {
req.client = client;
next();
};
app.use(addClientToRequest);

Related

Error: connect ECONNREFUSED 127.0.0.1:8000 in Express Js ( When try to Test with Postman )

I am really new to the industry and have this error when trying to check the database connection via API reuests with postman..... Please help me to settle this issue...
I just want to check the mongodb database by sendng API requests. Still I cannot identify the error and I am following a set of tutorials and occure this issue... Anyone can help me to identify the mistake it's highly appreciated....
{ this is dummy text to avoid please add more details...
Here is my code...
const app = express();
const { MongoClient } = require('mongodb');
const PORT = process.env.PORT || 8000;
// Initialize middleware
// we used to install body parser but now it's a built in middleware
// Function of express. It parses incoming JSONpayload
// app.use(express.json({extended:false}));
app.use(express.json({ extended: false }));
// Test Routs
// app.get("/", (req,res)=>res.send("Hello Aruna !!!"));
// app.post("/", (req,res)=>res.send(`Hello ${req.body.name} `));
// app.get("/hello/:name", (req.res)=>res.send(`Hello ${req.params.name}`))
app.get('/api/articles/:name', async (req, res) => {
try {
const articleName = req.params.name;
const client = await MongoClient.connect('mongodb://localhost:27017');
const db = client.db('mernblog');
const articlesinfo = db
.collection('articles')
.findOne({ name: articleName });
res.status(200).jason(articlesinfo);
client.close();
} catch (error) {
res.status(500).jason({ message: 'Error connecting to database', error });
}
});
app.post('/api/articles/:name/add-comments', (req, res) => {
const { username, text } = req.body;
const articleName = req.params.name;
articlesinfo[articleName].comments.push({ username, text });
res.status(200).send(articlesinfo[articleName]);
});
app.post('/', (req, res) => res.send(`Hello ${req.body.name}`));
app.get('/hello/:name', (req, res) => res.send(`Hello ${req.params.name}`));
app.listen(PORT, () => console.log(`Server is running at port ${PORT}`));
Server.js
Terminal
Error and API request in Postman
You have a typo in your code: jason should be json.
Other tips, you should handle your DB connection in a separate method and change your post request since articlesinfo is not a global variable:
const app = express();
const { MongoClient } = require('mongodb');
const PORT = process.env.PORT || 8000;
const client = new MongoClient('mongodb://localhost:27017');
const connectDB = async () => {
try {
await client.connect();
console.log('Successfully connected to DB')
} catch (err) {
await client.close();
console.log('Error connecting to DB');
process.exit(1);
}
}
// Initialize middleware
// we used to install body parser but now it's a built in middleware
// Function of express. It parses incoming JSONpayload
// app.use(express.json({extended:false}));
app.use(express.json({ extended: false }));
// Test Routs
// app.get("/", (req,res)=>res.send("Hello Aruna !!!"));
// app.post("/", (req,res)=>res.send(`Hello ${req.body.name} `));
// app.get("/hello/:name", (req.res)=>res.send(`Hello ${req.params.name}`))
app.get('/api/articles/:name', async (req, res) => {
try {
const articleName = req.params.name;
const db = client.db('mernblog');
const articlesinfo = db
.collection('articles')
.findOne({ name: articleName });
res.status(200).json(articlesinfo);
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to database', error });
}
});
app.post('/api/articles/:name/add-comments', (req, res) => {
const { username, text } = req.body;
const articleName = req.params.name;
const db = client.db('mernblog');
const articlesinfo = db
.collection('articles')
.updateOne({ name: articleName }, { $push: { comments: { username, text } } });
res.status(200).send(articlesinfo);
});
app.post('/', (req, res) => res.send(`Hello ${req.body.name}`));
app.get('/hello/:name', (req, res) => res.send(`Hello ${req.params.name}`));
connectDB();
app.listen(PORT, () => console.log(`Server is running at port ${PORT}`));

How to properly setup socket io in MERN app?

I am new to socket.io. I have basic understanding of how it works, but I am struggling to find proper setup for it within MERN app. If there is any article, or guidance that you can give me, I would appretiate it. I am building social network app, and I need to have live notifications and messages. So I am not sure how to setup socket.io client in react. Should I instanciate it in helper file, like mongoose in express, or is there any other way? Thanks
Install socket.io for server app
Install socket.io - client for client app
import socket.io in server page
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
const cors = require('cors');
const app = express();
const server = http.createServer(app)
const io = socketio(server, { cors: { origin: '*' } }) //for omit cors error
const PORT = 2900;
app.use(express.json());
app.use(cors());
io.on('connect', (socket) => {
console.log("user connected")
socket.on('valor', ({ id, name, }, callback) => {
console.log('data::', id, name)
socket.emit('receiveGreet', { data: 'This message from server' }, (error) => {
console.log('error::', error)
})
callback()
})
socket.on('disconnect', () => {
console.log('user disconnected')
})
})
app.get('/', (req, res) => {
res.json('api running')
})
server.listen(PORT, console.log(`server running in node on port ${PORT}`));
Client side Code May look like this
import io from 'socket.io-client';
let socket: any;
const serverUrl = 'http://localhost:2900';
const MyComponent = () => {
useEffect(() => {
socket = io(serverUrl);
socket.on('receiveGreet', (data) => {
console.log('data::', data);
});
}, []);
return () => {
socket.disconnect();
socket.off();
};
};

Connect Apollo with mongodb

I want to connect my Apollo server with my mongoDB. I know there are many examples out there, but I get stuck at the async part and did not found a solution or example for that (that's strange, am I completly wrong?)
I started with the example from next.js https://github.com/zeit/next.js/tree/master/examples/api-routes-apollo-server-and-client .
But the mongodb integration is missing.
My code
pages/api/graphql.js
import {ApolloServer} from 'apollo-server-micro';
import {schema} from '../../apollo/schema';
const apolloServer = new ApolloServer({schema});
export const config = {
api: {
bodyParser: false
}
};
export default apolloServer.createHandler({path: '/api/graphql'});
apollo/schema.js
import {makeExecutableSchema} from 'graphql-tools';
import {typeDefs} from './type-defs';
import {resolvers} from './resolvers';
export const schema = makeExecutableSchema({
typeDefs,
resolvers
});
apollo/resolvers.js
const Items = require('./connector').Items;
export const resolvers = {
Query: {
item: async (_parent, args) => {
const {id} = args;
const item = await Items.findOne(objectId(id));
return item;
},
...
}
}
apollo/connector.js
require('dotenv').config();
const MongoClient = require('mongodb').MongoClient;
const password = process.env.MONGO_PASSWORD;
const username = process.env.MONGO_USER;
const uri = `mongodb+srv://${username}:${password}#example.com`;
const client = await MongoClient.connect(uri);
const db = await client.db('databaseName')
const Items = db.collection('items')
module.exports = {Items}
So the problem is the await in connector.js. I have no idea how to call this in an async function or how to provide the MongoClient on an other way to the resolver. If I just remove the await, it returns – obviously – an pending promise and can't call the function .db('databaseName') on it.
Unfortunately, we're still a ways off from having top-level await.
You can delay running the rest of your code until the Promise resolves by putting it inside the then callback of the Promise.
async function getDb () {
const client = await MongoClient.connect(uri)
return client.db('databaseName')
}
getDb()
.then(db => {
const apollo = new ApolloServer({
schema,
context: { db },
})
apollo.listen()
})
.catch(e => {
// handle any errors
})
Alternatively, you can create your connection the first time you need it and just cache it:
let db
const apollo = new ApolloServer({
schema,
context: async () => {
if (!db) {
try {
const client = await MongoClient.connect(uri)
db = await client.db('databaseName')
catch (e) {
// handle any errors
}
}
return { db }
},
})
apollo.listen()

Error when combining Express Router with Massive.js db call

When making an async/await call to database from an express router to postgres db via massive.js instance, the correct response from db is received, but the router apparently returns before async function finishes; therefore, the test invocation returns undefined. From the console out (below), it seems clear that the async function is not waited for >_<
Is wrapping the router in order to pass the app instance causing the issue?
app.js
const app = express();
const massiveInstance = require("./db_connect");
const routes = require("./routes");
const PORT = 3001;
const server = massiveInstance.then(db => {
// Add db into our app object
app.set("db", db);
app.use("/api", routes(app));
app.listen(PORT, () => {
console.log("Server listening on " + PORT);
});
});
routes.js
const router = require("express").Router();
const { countRegions } = require("./db_queries");
const routes = app => {
const db = app.get("db");
router.get("/regions/count", async (request, response) => {
try {
const total = await countRegions(db);
console.log(`There are ${total} regions.`);
response.send(`There are ${total} regions.`);
} catch (err) {
console.error(err);
}
});
return router;
};
module.exports = routes;
db_queries.js
const countRegions = db => {
db.regions.count().then(total => {
console.log(`db has ${total} count for regions`);
return total;
});
};
module.exports = {
countRegions,
};
console output
Server listening on 3001
There are undefined regions.
db has 15 count for regions
You are not returning a promise returned by then in countRegions method.
So you should add return in your code like this
const countRegions = db => {
//here
return db.regions.count().then(total => {
console.log(`db has ${total} count for regions`);
return total;
});
};
or simply do,
return db.regions.count();

How to use mockgoose (or any other db mocking) in express app integration test

Using mockgoose in a simple unit test is quite straight-forward. However I'm a bit fuzzy as to how one would go about using mockgoose or other mocking solutions in an acceptance or integration test.
Given a simple express/MongoDB app like the following:
/*app.js*/
const express = require('express')
const app = express()
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var greetingSchema = mongoose.Schema({
greeting: String
});
var Greeting = mongoose.model('Greeting', greetingSchema);
app.get('/', function (req, res) {
Greeting.find({greeting: 'Hello World!'}, function (err, greeting){
res.send(greeting);
});
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
and a simple integration test like this:
/*test.js*/
const app = require('app.js');
const request = require('supertest');
it('sends "Hello World!" on the response body', (done) => {
request(app)
.get('/')
.expect(200, 'Hello World!', done);
});
});
By using the actual app in the request, we are connecting to the app's database ('mongodb://localhost/test'). How then can one use mockgoose, or any other solution, to mock the MongoDB database and still run an integration test like the one shown above?
I had the same problem as you. In my case, I solved using chai + chai-http and breaking the db connection and app in different files:
db.js:
const mongoose = require('mongoose');
const config = require('../../config');
mongoose.Promise = global.Promise;
mongoose.set('debug', process.env.DEBUG != undefined);
function open(){
return new Promise((resolve, reject) => {
if(process.env.DEBUG != undefined) {
let Mockgoose = require('mockgoose').Mockgoose;
let mockgoose = new Mockgoose(mongoose);
mockgoose.helper.setDbVersion("** your mongodb version **");
mockgoose.prepareStorage().then(function() {
mongoose.connect(config.db_test, (err, res) => {
if (err) return reject(err);
resolve();
});
}).catch(reject);
}else{
mongoose.connect(config.db, (err, res) => {
if (err) return reject(err);
resolve();
});
}
});
}
function close(){
return mongoose.disconnect();
}
module.exports = { close, open };
app.js:
const express = require('express');
const bodyParser = require('body-parser');
const api = require('./routes');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use('/api', api);
module.exports = app;
test.js (for test):
const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
const conn = require('./../utils/db'); // <-- db.js
const app = require('../../app'); // <-- app.js
chai.use(chaiHttp);
describe('# Test', function(){
before(function(done) {
conn.open().then(() => done()).catch(done);
});
after(function(done){
conn.close().then(() => done()).catch(done);
});
it(`test something`, function(done){
chai.request(app) // <-- pass the app here
.get('/path/to/test')
.then((res) => {
// expects
done();
})
.catch((err) => {
done(err);
});
});
});
index.js (for development or production):
const conn = require('./utils/db'); // <-- db.js
const app = require('./app'); // <-- app.js
const config = require('./config');
conn.open().then(() => {
app.listen(config.port, () => {
// OK!
});
});
I hope it works for you or anyone.