how do i call a method from another controller node - javascript

I have 2 models and 2 controllers.
UserModel.js, UserNameModel.js, and userController.js, userNameController.js
After I successfully register a user, I want to record & store that new username. the goal is to not reuse an already-used username
How do I design this function without calling a controller in the other controller,
userController.js
const register = async (req, res) => {
const { userName, email } = req.body
const newUser = await createNewUser(name, email)
res.status(StatusCodes.OK).json({ newUser })
}
userNameController.js
......

The module.exports is a special object which is included in every JavaScript file in the Node.js application by default. The module is a variable that represents the current module, and exports is an object that will be exposed as a module. So, whatever you assign to module.exports will be exposed as a module.
so, you can use the separator file to write all functions you need for each model like users, contacts, and messages....
users.js
module.exports.createNewUser = async (data) => {
write your code here.
return user;
};
module.exports.checkUserExist = async (email) => {
write your code here.
return user;
};
module.exports.deleteUser = async (userId) => {
write your code here.
return true;
};
and if you want to use the file on any controller, should call it like.
userController.js
const {createNewUser, checkUserExist, deleteUser} = require(../users.js);
router.get('create-user', async (req, res) => {
const newUser = await createNewUser(req.body);
res.status(200).json({ newUser });
}

Related

mongoose instance methods are showing different results

i'm currently making a server with javascript, koa, and mongoose.
i made two different mongoose instance methods, which receives password from JSON body
and hashes it with bcrypt, saves it on a 'hashedPassword' on user.
Initially, i wrote the second setPassword function (which uses promise, 'then' methods.),
but when the user document saved, the 'hashedPassword' property didn't showed up.
so i tried with first one, it worked well and user document has saved with hashedPassword.
although these two instance methods looks similar, i wonder why these methods leads to different result.. please help
import mongoose from "mongoose";
import bcrypt from "bcrypt";
const UserSchema = mongoose.Schema({
username: String,
hashedPassword: String,
});
UserSchema.methods.setPassword = async function (password) {
const hash = await bcrypt.hash(password, 10);
this.hashedPassword = hash;
};
// UserSchema.methods.setPassword = async function (password) {
// bcrypt.hash(password, 10).then((hash) => {
// this.hashedPassword = hash;
// });
// };
// this code didn't save the hashed
const User = mongoose.model("user", UserSchema);
export default User;
user controller would be like this
const register = async (ctx) => {
const { username, password } = ctx.request.body;
try {
const user = new User({
username,
});
await user.setPassword(password);
await user.save();
catch(e) { ctx.throw(500,e) }
}
This is not a mongoose-specific issue, you need to revise your understanding of how promises work.
Adding await before bcrypt.hash in the commented-out code should make it work, but there is no reason for you here to prefer .then over await.
In the second example, setPassword only fires the hashing process and doesn't ask the caller to wait until the hashing is done, and then adds the hashed password to the document, while the first one does.
You can try this approach using schema built-in methods:
UserSchema.pre('save', async function (next) {
if (this.isModified('hashedPassword')) {
this.hashedPassword= await bcrypt.hash(this.hashedPassword, 10)
}
next()
})
Use this pre-save function inside the schema like that :
UserSchema.pre('save', async function (next) {
// Only if password was moddified
if (!this.isModified('hashedPassword')) return next();
// then Hash password
this.hashedPassword = await bcrypt.hash(this.hashedPassword, 10);
next();
});

Unable to export db properties from nodejs module

I am trying to export database properties stored in properties file from Javascript module. By the time I read database properties file, Javascript file is already exported and data properties appear undefined wherever I use in other modules.
const Pool = require('pg').Pool;
const fs = require('fs')
const path = require('path');
class DbConfig {
constructor(dbData) {
this.pool = new Pool({
user: dbData['user'],
host: dbData['host'],
database: dbData['database'],
password: dbData['password'],
max: 20,
port: 5432
});
}
}
function getdbconf() {
const dbData = {};
fs.readFile("../../db_properties.txt"), 'utf8', (err, data) => {
if (err) {
console.error(err)
return
}
// dbData = {"user":"postgres", "password": "1234"...};
return dbData;
});
}
let db = new DbConfig(getdbconf());
let dbPool = db.pool;
console.log("dbpool : -> : ",dbPool); // username and password appear undefined
module.exports = { dbPool };
Is there a way to read data before exporting data from Javascript module?
Usually database config or any other sensitive info is read from a .env file using dotenv .
Or
you could also provide env from command line itself like
DB_HOST=127.0.0.1 node index.js
inside your index.js
console.log(process.env.DB_HOST)
Please create a new file (connection-pool.js) and paste this code:
const { Pool } = require('pg');
const poolConnection = new Pool({
user: 'postgresUserName',
host: 'yourHost',
database: 'someNameDataBase',
password: 'postgresUserPassword',
port: 5432,
});
console.log('connectionOptions', poolConnection.options);
module.exports = poolConnection;
For use it, create a new file (demo-connection.js) and paste this code:
const pool = require('./connection-pool');
pool.query('SELECT NOW();', (err, res) => {
if (err) {
// throw err;
console.log('connection error');
return;
}
if (res) {
console.log(res.rows);
pool.end();
}
});
This is an alternative option 🙂
Exporting the result of async calls
To export values which have been obtained asynchronously, export a Promise.
const fs = require('fs/promises'); // `/promise` means no callbacks, Promise returned
const dbDataPromise = fs.readFile('fileToRead')); //`readFile` returns Promise now
module.exports = dbDataPromise;
Importing
When you need to use the value,
const dbDataPromise = require('./dbdata');
async init() {
const dbData = await dbDataPromise;
}
//or without async, using Promise callbacks
init() {
dbDataPromise
.then(dbData => the rest of your code that depends on dbData here);
}
Current code broken
Please note that your current code, as pasted above, is broken:
function getdbconf() {
const dbData = {};
fs.readFile("../../db_properties.txt"), 'utf8', (err, data) => {
//[...] snipped for brevity
return dbData;
});
}
fs.readFile "returns" dbData, but there is nothing to return to, since you are in a callback which you did not call yourself. Function getdbconf returns nothing.
The line that says let db = new DbConfig(getdbconf()); will NOT work. It needs to be inside the callback.
The only way to avoid putting all of your code inside the callback (and "flatten" it) is to use await, or to use readFileSync
Avoiding the issue
Using environment variables
Suhas Nama's suggestion is a good one, and is common practice. Try putting the values you need in environment variables.
Using synchronous readFile
While using synchronous calls does block the event loop, it's ok to do during initialization, before your app is up and running.
This avoids the problem of having everything in a callback or having to export Promises, and is often the best solution.

Node test not running mongoose function

I have a controller which I want to test
// user model
const userModel = require('../models/user')
// get user model from handler
const User = userModel.User
function index(req, res) {
User.find(function(err, users) {
if (err) console.log(err)
console.log('debug')
res.render('../views/user/index', {
title: 'User index',
users: users
})
})
}
module.exports = {
index
}
using the following code
// dependencies
const sinon = require('sinon')
// controller to be tested
const controller = require('../controllers/user')
// get user test
describe('test user index', function() {
it ('should return users', function() {
var req = {}
var res = {
render: function() {
sinon.spy()
}
}
controller.index(req, res)
})
})
When I run the test it doesn't execute the console.log('debug') which seems to me indicates that the test can't run the User.find() function. Any help would be appreciated.
As a temporary solution, you can include DB connection string at the top of the file.
Explanation: I guess your controller is part of an app, so it connects to DB on init. However, here you are testing an isolated controller, therefore DB connection isn't initialized yet. That's quick solution, for proper way of doing that refer to official docs

How to mock a method inside Express router Jest test?

I'm trying to test a router in Node.js app with Jest + Supertest, but my router is making a call to service, which is calling the endpoint:
router.post('/login', async (req, res, next) => {
try {
const { username, password } = req.body;
// I WANT TO MOCK userService.getUserInfo FUNCTION, BECAUSE IT IS MAKING A POST CALL
const identity = await userService.getUserInfo(username, password);
if (!identity.authenticated) {
return res.json({});
}
const requiredTenantId = process.env.TENANT_ID;
const tenant = identity.tenants.find(it => it.id === requiredTenantId);
if (requiredTenantId && !tenant) {
return res.json({});
}
const userResponse = {
...identity,
token: jwt.sign(identity, envVars.getVar(envVars.variables.AUTH_TOKEN_SECRET), {
expiresIn: '2h',
}),
};
return res.json(userResponse);
} catch (err) {
return next(err);
}
});
This is my test that works well:
test('Authorized - respond with user object', async () => {
const response = await request(app)
.post('/api/user/login')
.send(users.authorized);
expect(response.body).toHaveProperty('authenticated', true);
});
this is how getUserInfo function looks like:
const getUserInfo = async (username, password) => {
const identity = await axios.post('/user', {username, password});
return identity;
}
but it executes the method getUserInfo inside a router and this method is making a REST call - I want to mock this method in order to avoid REST calls to other services.
How it could be done?
I've found a mockImplementation function in Jest docs https://jestjs.io/docs/en/mock-function-api.html#mockfnmockimplementationfn
but how I can mock func inside a supertest testing?
You can use jest's auto mocking at the top of your test
like so:
jest.mock('./path/to/userService');
// and include it as well in your test
const userService = require('./path/to/userService');
it will generate a mock of the entire module and every function will be replaced with jest.fn() with no implementation
and then depending on the userService if it's just an object it's getUserInfo method will be a jest.fn() and you can set it's return value like this:
// resolved value as it should return a promise
userService.getUserInfo.mockResolvedValue(mockIdentity);
and the mockIdentity will have to look something like this:
const mockIdentity = {
authenticated: true,
tenants: [
{
id: "x12",
mockInfo: "mock-info-value"
}
],
mother: "Superwoman",
father: "Superman"
})
}

Exporting and importing in Node express

I am creating my first project in Node and someone told me that it is a good practise to create a .js file having all the common and frequently used function
For example suppose I want to query something to my mongoose, to fetch data or update data, I should create a js file from where all the operations should happen.
Consider I have a Mongoose Schema which looks like this
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
fullName: String,
email: String,
passowrd: String,
image: String
})
module.exports = mongoose.model('User', userSchema);
To perform a function such as checking if the user exsist in the Db, I created a .js file in my helper function folder known as my_db_query.js where I imported my mongoose schema and made many common function which interact with my schema (or other schemas in my mongoose)
Const user = requeire(./../model/user.js)
//other schema
function findByEmail (email) {
return User.findOne({email: email}).then((currentUser) => {
return currentUser
}
function updateUser (updatedData) {
//to update user
}
function deleteUser (user) {
//to delete a user
}
Now, Suppose I have a folder routes where I need to use them.
route.delete("/:delete", isAdmin, (req, res) => {
})
route.get("/:id", (req, res) => {
})
route.put(/:id, isAdmin, (req, res) => {
})
Question: How can I able to export all function at once and how can I import and use them in my routes file
How can I able to export all function at once ?
module.exports = { findByEmail, updateUser, deleteUser };
Just export an object containing the functions.
and how can I import and use them in my routes file ?
You could destructure the exported object:
const { findByEmail, updateUser, deleteUser } = require("./my_db_query");
findByEmail("test#example.com").then(/*...*/).catch(/*...*/);
In your .js file, you can export whatever functions you would like to use in other parts of your code base. The below is an example of what should go at the bottom of your .js file. It will export the 3 functions you defined.
module.exports = {
findByEmail,
updateUser,
deleteUser
}
In your routes file you can then import the file/functions. At the top of your routes file you need to include the below. In the below example, commonMethods.js is the file name of the .js file and all of the files are located in the same directory.
const commonMethods = require('./commonMethods.js')
You can then use any of the common methods like this: commonMethods.updateUser(updatedData)
Export all function at once:
I prefer to encapsulate everything inside an object.
const index = {};
index.findByEmail = function() { ... }
index. updateUser = function() { ... }
index. deleteUser = function() { ... }
module.export = index;
Import 1
const lib = require('./lib.js');
Import 2
const {findByEmail, updateUser , deleteUser } = required('./lib.js');

Categories

Resources