How to keep one instance of the database using express - node.js

I am using express along with sequelize to create a REST API. I want to keep just one instance of my database and use it in all the routes I may create. My first thought was to create the instance as soon as the app runs. Something like this.
const express = require('express')
const databaseService = require( './services/database')
const config = require( './config')
const userRoute = require( './routes/user')
const app = express()
databaseService.connect(config.db_options).then(
connectionInstance => {
app.use('user', userRoute(connectionInstance))
app.listen(3000)
}
)
I have not seen something like that so I believe it's not a good approach.
Any ideas ?

A strategy that I use extensively in several production applications to great success is to define the database connection logic in a single file similar to the following:
database.js
const dbClient = require('some-db-client')
let db
module.exports.connectDB = async () => {
const client = await dbClient.connect(<DB_URL>)
db = client.db()
}
module.exports.getDB = () => {
return db
}
Obviously, the connection logic would need to be more complex then what I have defined above, but you can get the idea that you would connect to your database using whichever database client library that you choose and save a reference to that single instance of your database. Then, wherever you need to interface with your database, you can call the getDB function. But first, you would need to make a call to your connectDB function from your server application entry file.
server.js
async function run() {
const db = require('./database')
await db.connectDB()
const app = require('express')()
const routes = require('./routes')
app.use('/', routes)
app.listen(3000, () => {
console.log('App server listening to port 3000')
})
}
You would make the call to initialize your database connection in the server application entry file before initializing express. Afterwards, any place in your application that needs access to the database can simple call the getDB() function and use the returned database object as necessary from there. This does not require overly complicated connection open and close logic and saves on the overhead of constantly opening and closing connections whenever you want to access the database by maintaining just a single database connection that can be used anywhere.

Related

Create a routes-wide reusable MongoDB connection in Express

I am writing a small application. You could think of it like this:
Server
-- Security
-- Microservices
-- Others
The server routes to each of those. Each of those routes uses a different MongoDB database (same server).
I don't find the way to make a single, reusable connection to MongoDB server, that could be reused each route.
For example:
//server.js
//import modules etc
async function connectDB(){
const uri = process.env.MONGOURI;
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
return (await client.connect());
}
But I can not pass the returned client to any route, as the routes take no parameters:
//server.js
import security from "./security";
async function run() {
// security
app.use("security", security) //cant pass mongo client
If I write the client in the routes themselves:
//security.js
router.post("/register", async (req, res) => {
const database = await connectDB()
const users = database.collection("users");
I will have to run connectDB() to each route.
Question
Is there a way to create a connection in the server.js that will be reused in other routes?

Aws lambda with mongoDb connection

Hello guyz i need an answer to an simple question.I am using Aws lambda with serverless framework and i am using mongoDb connection in aws lambda.I have used connection code inside my handler function and i have used connection pooling.
Now when i deploy my app on AWS using sls deploy and after deploying when i call my lambda for first time then connection is established only once and after that on other lambda API calls it is reusing my connection instead of creating new connection.so this thing is fine.
Now after this process i am running a script which is not related with my AWS app to test my concurrent lambda requests.I have called my same lambda API using request npm module in for loop in script and in that case all time my new connnection is created till loop terminates instead of using my existing one generated from first call.Can someone tell me why it is happening and what is the reason behind this? Why my connection is creating again when this script runs when already i have created my connection on first lambda call.
And same api when i call from postman then it is resuing my connection after first lambda call but when i run this script and from inside script i call this API(using request NPM module) using command "node app.js" then all time till loop terminates it creates new connection.
Please help me out in this.
'use strict'
const bodyParser = require('body-parser')
const express = require('express')
const serverless = require('serverless-http')
const cors = require('cors');
const mongoConnection = require('./connection/mongoDb');
const app = express()
app.use(cors())
app.use(bodyParser.json())
const handler = serverless(app);
let cachedDb = null;
module.exports.handler = async (event, context) => {
context.callbackWaitsForEmptyEventLoop = false;
if (cachedDb == null) {
let Database = await mongoConnection();
console.log("DB", Database);
cachedDb = Database
}
const baseRouter = require('./routes/index');
app.use('/api', baseRouter);
const result = await handler(event, context);
return result;
};
Here is a node.js example that shows the connection parameters. Perhaps this will help?
const express = require("express");
const bodyParser= require("body-parser")
const app = express();
const MongoClient = require("mongodb").MongoClient
MongoClient.connect("mongodb://myusername:mypassword#localhost:27017", (err, client) => {
if (err) return console.log(err)
var db = client.db("mydatabase")
db.collection("mycollection").countDocuments(getCountCallback);
app.listen(3000, () => {
console.log("listening on 3000")
})
})
function getCountCallback(err, data) {
console.log(data);
}
app.use(bodyParser.urlencoded({extended: true}))
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html")
})
app.post("/quotes", (req, res) => {
console.log(req.body)
})
Your example code does not show any hostname for your database server, nor does it specify which port to use. Please compare your code and contrast to my example.
I see you defined the cachedDb variable outside the handler scope, so that makes it available when the container is reused. However, there is no guarantee that the container will be reused (see my previous link on that) because that's not how Lambda works. If you invoke the same functions many times very quickly after eachother, Lambda needs to scale out horizontally to be able to handle the requests quickly. They each get their own container and connection.
When the invocation is finished, AWS will keep the container for a bit (how long depends on many factors like function size & RAM limit). If you invoke it again the containers can reuse their connection. You can try to invoke the function 20 times with 1 second interval and counting the number of connections that have been openend. It will be lower than 20, but higher than 1.

NodeJS Construct a custom object without requiring the file

Is there a way in nodejs to define a custom object type in it's own file, then instantiate it anywhere else in the codebase without needing to require in the file?
For example, every time I create a new map somewhere, I don't have to require in the map definition. I want to be able to do
var user = new User();
from anywhere without requiring in the definition.
If you don't want to require a dependency everywhere then you could use Dependency Injection. Dependency Injection is a pattern for passing dependencies or common information from one context to another.
The power of Dependency Injection is decoupling the Dependency itself with resolving and/or initializing that Dependency. In JavaScript, closures can be used to hide configuration/initialization details of a Dependency by locking in their context to the scope that created them. This means that your files implementing the Dependency don't need to be concerned with how to get it initialized (ie. a config) which, probably also means more thing it needs to require.
Below is an example of how a User can be required in one file, injected into a separate file and then be used as if it were required within the file it was injected to. I chose to use an Express Server and Dependency Injection into a Router as the example because a popular reason to use Node.js is to build a Web API. For another example of Dependency Injection, see this blog post from RisingStack Engineering (you'll need to scroll down to the Dependency Injection section).
The example has 3 files
lib/models/User.js
This is the module that is Injected into our UserRouter
/routes/user.js
This is the UserRouter and it returns a function that accepts a models object which, contains all Models the UserRouter needs. Once the router is created it is returned for use within an Express Server
server.js.
The Express Server that is going to be responsible for injecting the User Model into the UserRouter so it can create new users in a cache.
lib/models/User
class User {
constructor(name) {
this.name = name
}
}
module.exports = User
routes/user.js
const Router = require('express').Router
const UserRouter = models => {
// Use Destructuring to get the User model from the models object passed into the Router
// You can use regular ES5 for this as well: var User = models.User
const {User} = models
const router = new Router()
const users = []
// Return all users in cache
router.get('/', (req, res) => res.status(200).json({users}))
router.post('/', (req, res) => {
const {name} = (req.body || {})
if (!name) {
return res.status(400).send('A User must have a name')
}
// Create the new User
let newUser = new User(name)
// add it to the users cache
users.push(newUser)
// return 201 CREATED and send the user as the message body
return res.status(201).json(newUser)
})
// Return the router instance
return router
}
module.exports = UserRouter
server.js
const express = require('express')
const port = process.env.PORT || 1337
// require our UserRouter
const UserRouter = require('./routes/user')
// import the User model
const User = require('./lib/models/User')
// create an object called Models with the User now as a property
const Models = {User}
const server = express()
// Inject the Models directly into the UserRouter
server.use('/users', UserRouter(Models))
server.listen(port, () => console.log(`Listening on ${port}`))

How do you use functions from other files with a request in node?

const express = require('express');
var router = express.Router()
const app = express();
const pgp = require('pg-promise')();
const pug = require('pug');
const connect = require('../core/connect.js');
const model = require('../models/model.js');
app.get('/data',function (req, res)
{
const data = model.getData(connect.db, 'Select * FROM items');
res.send(data);
});
Looking at the code here, I should be able to use the getData function from model because I required it within the actual get request to data. However for some reason, it does not seem to be performing the query. I have verified the data does exist through postgres. I also know that if I move the const data line out of the app.get, it also works.
Can someone please explain how I used my functions from the model.js file within GET and POST requests?
I'm not using Ajax or Jquery. I read through that article and I'm still not sure what exactly I'm missing. It is saying something about a callback, but I'm not too clear on that either.

How to transfer variable to routes.index

In file app.js i have:
, mongoose = require('mongoose')
, db = mongoose.connect('mongodb://localhost/expressdb');
app.get('/', routes.index);
and i have file routes/index.js, If I want to transfer db variable to routes.index What should I do?
I've setup an example on how you can achieve that, by passing the db as a parameter to the route module, and that module returns a function (in which db is visible):
routes.index
module.exports = function (db) {
return function(req, res, next) {
// you can access db here
}
}
app.js
...
routes = {};
routes.index = require('./routes')(db);
...
I think it's fairly standard in libraries that connect to a database for the connection to be shared if you try to create another connection (ie: the DB interface is a singleton). You could try simply calling
db = mongoose.connect('mongodb://localeyes/expressdb');
in your routes/index.js file, and it will likely use the same connection. I'd recommend reading the code to see if it will use an existing connection or if it tries to reconnect every time you call it.
edit: on second thought, you have another, less savory option:
You could make it global like so
global.db = db;
and then in your routes/index.js
var db = global.db;

Resources