Node JS Global Variables is not working after call - javascript

I'm trying to call my global variables in my controller but i got an error variable is not defined. Please see the code below for your reference. Hoping to solve my problem. Thank you Guys
**server.js **
const serverConfig = require('./config/server.config')
const app = require('fastify')({ logger: true })
require('./models/response.model')
const mongoose = require('mongoose')
const conn = require('./config/monggo.config')
require('./routes/dbm.routes')(app)
const connect = async () => {
try {
await mongoose.connect(conn.uri)
console.log('Connected to Mongoose!')
} catch (error) {
console.log(error)
}
}
connect();
app.listen(serverConfig.port, '::', (err, address) => {
if (err) {
app.log.error(err)
process.exit(1)
}
console.log('Listening at', address)
})
response.model.js
module.exports = function () {
global.successModel = {
status: 'sucess',
statusCode: 0,
isSuccess: true,
message: ''
}
global.failModel = {
status: 'failed',
statusCode: 1,
isSuccess: false,
message: 'Error encountered while processing request.'
}
}
**monggo.controller.js
**
exports.getProducts = async (req, res) => {
//find Products in the databse
Product.find({}, (err, product) => {
//send error message if not found
if (err) {
res.send(err);
}
//else pass the Products
res.send(successModel);
})
await res;
}
Hoping to solve my problem. Thank you

The './models/response.model' file exports a function that you need to call to "install" your globals.
- require('./models/response.model')
+ require('./models/response.model')()
As a suggestion, you should avoid to use globals, in fastify you can use:
decorator to add to your app instance useful data such as configs
Moreover, your mongoose connection is unrelated with Fastify, you can encapsulate it into a plugin to be sure that fastify is starting after connecting to the database:
const fp = require('fastify-plugin')
app.register(fp(async function connect (instance, opts) {
await mongoose.connect(conn.uri)
}))

Related

How to get specific errors from called sub functions in the catch block of the main function

A fews weeks ago I have asked a first question about this topic Catch errors when using async/await
My register function was getting pretty long and some parts of it are reusable for other controller functions so I decided to decompose the code into various reusable functions.
const findOrCreateEntity = async (req, next) => {
let entity, entity_type;
if(req.body.enterprise_number) {
entity = await Company.findOne({ enterprise_number: req.body.enterprise_number });
entity_type = 'Company';
if(!entity) {
return next(new ErrorResponse('Company not found', `We were unable to find the company for the given enterprise number ${req.body.enterprise_number}`, 400, 'company_not_found'));
}
} else {
entity = await PrivatePerson.findOne({ email: req.body.email });
entity_type = 'PrivatePerson';
if(!entity) {
[entity] = await PrivatePerson.create([{
_id: mongoose.Types.ObjectId().toHexString(),
...req.body,
source: req.app
}], { session: req.session })
}
}
return {
entity, entity_type
};
};
const createEnergyMandate = async (req, entity_id, entity_type, next) => {
let energy_mandate;
energy_mandate = await EnergyMandate.findOne({ entity: entity_id });
if(energy_mandate) {
return next(new ErrorResponse('Email already in use', `An account with the provided email address already exists. Please login instead.`, 409, 'email_already_in_use'));
}
let preferences = {
push_notifications: true,
sms_notifications: true,
email_notifications: true
}
if(!req.app.notifications) {
preferences = {
push_notifications: false,
sms_notifications: false,
email_notifications: false
}
}
[energy_mandate] = await EnergyMandate.create([{
_id: mongoose.Types.ObjectId().toHexString(),
entity: entity_id,
start_date: Date.now(),
status: 'edit_customer',
legal: {
privacy_policy_version: '2.1',
privacy_policy_signed: true,
privacy_policy_signed_at: Date.now(),
privacy_policy_signed_from_ip: req.user_ip,
general_conditions_version: '2.1',
general_conditions_signed: true,
general_conditions_signed_at: Date.now(),
general_conditions_signed_from_ip: req.user_ip
},
preferences: preferences,
sales_channel: req.app.channel
}], { session: req.session });
return energy_mandate;
}
export const register = asyncHandler(async (req, res, next) => {
const session = await mongoose.startSession();
session.startTransaction();
req.session = session;
try {
// Find or create entity
const { entity, entity_type } = await findOrCreateEntity(req, next);
// Create energy mandate
const energy_mandate = await createEnergyMandate(req, entity._id, entity_type, next);
// Create auth0 user unless req.app.user is false
if(!req.app.user) {
auth0_user = await createAuth0User(req, next);
auth0_uid = auth0_user.user_id;
} else {
auth0_uid = req.user.uid;
}
return res.status(200).json({
message: 'User registered',
data: {
user_id: auth0_uid
}
});
} catch (error) {
await session.abortTransaction();
if(auth0_user) {
await auth0ManagementClient.deleteUser({
id: auth0_user.user_id
});
}
next(error);
} finally {
session.endSession();
}
});
As you can see there are some points where I use an ErrorResponse to write very specific errors like return next(new ErrorResponse('Company not found', `We were unable to find the company for the given enterprise number ${req.body.enterprise_number}, 400, 'company_not_found'));`
Based on the previous question I asked and the answer I received, I should wrap each function in the register controller function into it's own try/catch block so the register function stops executing.
I've got two question about that:
How can I return the specific errors like return next(new ErrorResponse('Company not found', `We were unable to find the company for the given enterprise number ${req.body.enterprise_number}`, 400, 'company_not_found')); to the client but also calling the catch block.
Will the 'global catch' where the transaction is being aborted still be hit if I have all these smaller try/catch blocks? In other words will the main catch block of a try/catch be executed if the try/catch block has one or multiple try/catch blocks?

Issue while trying to call an existing route from within another route in Express JS project

In an Express JS connected to a mySQL db, I am trying to get some data of an already defined route/ query:
// customers.model.js
CUSTOMERS.getAll = (result) => {
let query = "SELECT * FROM customers"
sql.query(query, (err, res) => {
if (err) {
console.log("error: ", err)
result(null, err)
return
}
result(null, res)
})
}
// customers.controller.js
// GET customers is a standalone route and should output all the customers when called.
const CUSTOMERS = require("../models/customers.model.js")
exports.findAll = (req, res) => {
return CUSTOMERS.getAll((err, data) => {
if (err)
res.status(500).send({
message: err.message ||
"Some error occurred while retrieving customers...",
})
else res.send(data)
})
}
In payments.controller.js I would firstly like to get all users so I can do something with the data:
// payments.controller.js
// GET payments is also a standalone route and should get the customers,
// do something with the data and output a calculation with the help of this data
const CUSTOMERS = require("../models/customers.model.js")
exports.calculateAll = (req, res) => {
const customers = CUSTOMERS.getAll((err, data) => {
console.log('this always has correct data', data)
if (err) return err
else return data
})
console.log('this is always undefined', customers)
...
res.send(whatEverCalculatedData)...
}
But that data here is always undefined.
What am I doing wrong in the above, and what's the correct way to call this route inside another route?
I know it has similarities with this question but I couldn't sort it out for my particular example.
It's due to your call which is asynchronous.
You must wait your data being ready before rendering the results.
Maybe you could to use Promises or async/await statements.
For example:
CUSTOMERS.getAll = async () => {
const query = "SELECT * FROM customers";
try {
return await sql.query(query);
} catch (e) {
console.log(`An error occurred while fetching customers: ${e.message}.`);
return null;
}
}
exports.calculateAll = async (req, res) => {
try {
const data = await CUSTOMERS.getAll();
res.send(whatEverCalculatedData);
} catch (e) {
res.send(`Something went wront: ${e.message}.`);
}
}

countDocuments() is not working in api call

I am trying to get a count of products using api calls but in postman its keep loading
router.get(`/get/count`, async (req, res) => {
const productCount = await Product.countDocuments((count)=>count)
if (!productCount) {
res.status(500).json({ success: false });
}
res.send({
productCount: productCount
});
});
(node:28030) UnhandledPromiseRejectionWarning: MongooseError: Query was already executed: Product.countDocuments({})
without async and await also its not working
I try to catch the error and i got this error in postman
{
"success": false,
"error": "Query was already executed: Product.countDocuments({})"
}
code to catch error:
router.get(`/get/count`, (req, res) => {
Product.countDocuments((count)=>count).then((pcount)=>{
if(pcount){
return res.status(200).json({success:true})
}else{
return res.status(404).json({success:false})
}
}).catch((err)=>{
return res.status(400).json({success:false, error:err.message})
})
});
I think in Mongoose operations you want to either await or provide a callback, but not both. Attempting to do both causes it to internally execute the query twice.
Try just:
const productCount = await Product.countDocuments();
If you want to count all the products in your product collection, try this
db.product.countDocuments({})
router.get(`/get/count`, async(req, res) => {
let productCount = await Product.countDocuments();
if(!productCount){
res.send(500).json({success:false})
}
res.send({productCount: productCount})
});
Two way you can do that
Try it you don't need use callback (count)=>count
const express = require('express')
const router = express.Router();
const {Product} = require('../models/products')
const {Category} = require('../models/category')
const catchAsyncErrors = require('../middleware/catchAsyncError')
const mongoose = require('mongoose');
// Get all product
router.get(`/`, async (req, res) => {
let productList = await Product.find().populate('category')
if(!productList) {
res.status(500).json({
success: false
})
}
res.status(200).json({
success: true,
count: productList.length,
productList
});
})
router.get(`/count`, catchAsyncErrors(async(req, res) => {
const countProductList = await Product.countDocuments();
if(!countProductList){
res.status(500).json({
success: false
})
}
res.send({
success: true,
countProduct: countProductList
})
}))
You don't need to include ((count)=>count).
Use Product.countDocuments() instead

unable to get data from postgres using pg-promise and vue.js

I want to use pg_promise with feathersjs and vuejs to display results of query run on postgres db, my sql.js looks as follows
sql.js
const express = require('express');
const jsonexport = require('jsonexport');
const dbgeo = require('dbgeo');
const router = express.Router();
function dbGeoParse(data) {
return new Promise((resolve, reject) => {
dbgeo.parse(
data,
{
outputFormat: 'geojson',
},
(err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
},
);
});
}
// expose sql endpoint, grab query as URL parameter and send it to the database
router.get('/sql', (req, res) => {
const { app } = req;
const sql = req.query.q;
const format = req.query.format || 'topojson';
console.log(`Executing SQL: ${sql}`, format); // eslint-disable-line
// query using pg-promise
app.db
.any(sql)
.then((data) => {
console.log('data',data)
// use dbgeo to convert WKB from PostGIS into topojson
return dbGeoParse(data).then((geojson) => {
res.setHeader('Content-disposition', 'attachment; filename=query.geojson');
res.setHeader('Content-Type', 'application/json');
return geojson;
});
})
.then((data) => {
res.send(data);
})
.catch((err) => {
// send the error message if the query didn't work
const msg = err.message || err;
console.log('ERROR:', msg); // eslint-disable-line
res.send({
error: msg,
});
});
});
module.exports = router;
I have configured my app.js to use sql.js as a route by adding following lines to it
const pgp = require('pg-promise')({
query(e) {
console.log(e.query); // eslint-disable-line
},
});
const connectionString = 'postgres://abc:abc123#localhost:5678/gisdb';
// initialize database connection
const app = express(feathers());
app.db = pgp(connectionString);
app.use('/sql', require('sql'));
but when I call handleSubmit fuction shown below from my vue component
handleSubmit() {
const SQL = "select * from spatial_search(searchmethod := 'id',sale_price :=10000,tax_ogcid :=
84678,distance := 0.5)";
const queryType = 'sql';
fetch(`/${queryType}?q=${encodeURIComponent(SQL)}`)
.then(res => {console.log('res',res.json())})}
I get the following error
Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
I think I might be doing something wrong here. Can someone help me?

Sinon test failing after change made to function

I have this test:
describe('createNote', () => {
beforeEach(() => {
res = {
json: sinon.spy(),
sendStatus: sinon.spy(),
};
});
afterEach(() => {
noteService.createUserNote.restore();
});
it('should return user note object', async () => {
// Arrange
modelResponse = {
id: 1,
userId: req.user.id,
...req.body,
};
sinon.stub(noteService, 'createUserNote')
.resolves(modelResponse);
// Act
await userController.createNote(req, res);
// Assert
sinon.assert.calledWith(
noteService.createUserNote,
req.user,
req.body.note,
);
sinon.assert.calledWith(res.json, { note: modelResponse });
});
It fails on line sinon.assert.calledWith(res.json, { note: modelResponse });
I don't really understand sinon so I'm not sure why though.
This is my userController code:
createNote: async (req, res, next) => {
try {
const createNote = await noteService.createUserNote(
req.user,
req.body.note,
);
const note = await noteService.getUserNote(
req.user.id,
createNote.id,
);
return res.json({ note });
} catch (err) {
return next(err);
}
},
I recently changed it from this so assume something in what I've done has caused the test to fail:
createNote: async (req, res, next) => {
try {
const note = await noteService.createUserNote(
req.user,
req.body.note,
);
return res.json({ note });
} catch (err) {
return next(err);
}
},
This is the error I get:
1) User userController
createNote
should return user note object:
AssertError: async (user, text) => {
const [note] = await db.Note.createUserNote(user.id, text, db);
await emailService.userAlert(text, user.name);
return note;
} is not stubbed
at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
at /opt/atlassian/pipelines/agent/build/node_modules/sinon/lib/sinon/assert.js:35:24
at Array.forEach (<anonymous>)
at verifyIsStub (node_modules/sinon/lib/sinon/assert.js:22:5)
at Object.assert.(anonymous function) [as calledWith] (node_modules/sinon/lib/sinon/assert.js:77:9)
at Context.it (app/__tests__/controllers/user/userController.test.js:56:20)
at <anonymous>
Can anybody explain what is wrong and how to fix this?
You need to mock getUserNote as well. After the change, you are getting note from getUserNote and then sending it to res.json
But in the test case you have not stubbed it. Try adding this in the test case:
sinon.stub(noteService, 'getUserNote')
.resolves(modelResponse);

Categories