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');
Related
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 });
}
I have two file alarm.js and notifications.js. In alarm.js I need to call a method called sendPush from notifications.js.
What I've tried :
Exporting the function from notifications.js:
module.exports.sendPush = function(params){
console.log("sendPush from notifcations.js called");
console.log(params);
}
Importing it in alarm.js and use it :
let helperNotif = require('./notifications')
router.post("/", async (req, res) => {
const params = {
param1: 'a',
param2: 'b'
}
helperNotif.sendPush(params)
});
The problem:
I keep getting the error saying helperNotif.sendPush is not a function
The question :
How can I call this notification.js sendPush function from my alarm.js file ?
[EDIT] maybe I should add that in notifications.js I have some router.get and router.post and at the end module.exports = router;
If your notifications.js ends with module.exports = router, that will overwrite your module.exports.sendPush = .... If you want to export both the router and the sendPush, you can write
function sendPush(params){
console.log("sendPush from notifcations.js called");
console.log(params);
}
...
module.exports = {router, sendPush};
To import the router elsewhere, you must then write
const {router} = require("./notifications.js");
I have defined a function service in one of the file
import Category from '../models/Category.js';
export const AllCategories = () => {
console.log('hit');
const cursor = Category.find({});
console.log(cursor);
return cursor
}
export default {AllCategories}
I am importing this in the controller file
import express from 'express';
import categoryService from '../services/categories.js'
const router = express.Router();
export const getCategories = async(req,res) => {
try {
const categoriesInfo = categoryService.AllCategories
res.status(200).json(categoriesInfo)
} catch (error) {
res.status(404).json({ message: error.message });
}
}
export default router;
But the issue is that AllCategories is not getting run, what is wrong here
I also tried adding async/await
import Category from '../models/Category.js';
export const AllCategories = async () => {
try {
console.log("hit");
const cursor = await Category.find({});
console.log(cursor);
return cursor
} catch (error) {
return error
}
}
export default {AllCategories}
But still no luck
You're not calling the function, this saves it in the variable categoriesInfo:
const categoriesInfo = categoryService.AllCategories
To get its return value:
const categoriesInfo = await categoryService.AllCategories();
Note: I think you need to make it async if you're doing a db transaction so keep the second version and test it.
You can't use the ES module or ESM syntax by default in node.js. You either have to use CommonJS syntax or you have to do 1 of the following.
Change the file extension from .js to .mjs
Add to the nearest package.json file a field called type with a value of module
So I have written code to handle this, essentially it looks for any placeholder.controller.ts file in my project and uses the exported array of controllers to add routing. Controllers have to be default exported in an array with a specific format.
the format looks like this:
const controller1 = {
endpoint: '/hello/world',
method: 'get',
controller: () => console.log('hello world!'),
}
export default [
controller1
];
The routing code that handles all of this, exists in a routes.ts file and looks like this:
import glob from 'glob';
import path from 'path';
import { Router } from 'express';
import { toArray } from '../lib/utilities/generic-utilities';
import { isRouteType, isArrayWithContent } from '../lib/utilities/type-checking';
import { skip } from '../lib/middleware/generic-middleware';
import { Route } from '../meta/#types/common-types';
import { secureRoutesConstant, extension } from './settings';
import secureRoute from '../lib/middleware/secure-route';
const router: any = Router({ mergeParams: true });
// relative path from routes file to controllers folder.
const controllersPath = '../http/controllers/';
const addRouteToRouter = (route: Route, filename: string) => {
const acceptableRoute: object | boolean = isRouteType(route);
const message: string = `issue with route while exporting a controller in file ${filename}\nroute supplied was:`;
if (!acceptableRoute) console.log(message, route);
if (!acceptableRoute) return;
const { endpoint, controller, method, isSecure = secureRoutesConstant } = route;
const { middlewareBefore = [], middlewareAfter = [] } = route;
const makeRouteSecure: Function = isSecure ? secureRoute : skip;
const middlewareBeforeArr: Function[] = toArray(middlewareBefore);
const middlewareAfterArr: Function[] = toArray(middlewareAfter);
const routeArguments: Function[] = [
...middlewareBeforeArr,
makeRouteSecure,
controller,
...middlewareAfterArr,
];
router.route(endpoint)[method](...routeArguments);
};
const addToRouterForEach = (allRoutes: Route[], filename: string) =>
allRoutes.forEach((route: Route) => addRouteToRouter(route, filename));
glob
.sync('**/*.ts', { cwd: path.join(`${__dirname}/`, controllersPath) })
.filter((filename: string) => filename.split('.').includes('controller'))
.map((filename: string) => ({ defaultsObj: require(`${controllersPath}${filename}`), filename }))
.filter(({ defaultsObj }) => isArrayWithContent(defaultsObj.default))
.forEach(({ defaultsObj, filename }) => addToRouterForEach(defaultsObj.default, filename));
export default router;
And is simply imported into app.ts and used like this:
app.use('/api', router)
Essentially this means I have no routing code as it's all handled for me, I only have to write my services, controllers and models.
Is there any performance or security issues with doing things like this, or with the code itself?
Is there any performance or security issues with doing things like this, or with the code itself?
Performance
No. The auto code will only run on boot and even if it takes a second its not a cost you are paying on individual client request route handling.
Security
The code architecture is secure by itself and does not increase your risk of vulnerabilites.
Trying to refactor my gatsby-node file, by outsourcing a bit of code. Right now trying to do this in my gatsby-node:
const createBlogPostPages = require("./gatsby-utils/createBlogPostPages");
exports.createPages = async ({ actions, graphql, reporter }) => {
//...some code
await createBlogPostPages({ actions, graphql, reporter });
//...some code
}
and my createBlogPostPages, which is in a different file, looks like so:
const path = require("path");
module.exports = async function({ actions, graphql, reporter }) {
const { createPage } = actions;
const blogArticles = await graphql(`
{
allMdx(filter: { fileAbsolutePath: { regex: "/content/blog/.*/" } }) {
edges {
node {
id
fileAbsolutePath
fields {
slug
}
frontmatter {
title
tags
date
tagline
}
}
}
}
}
`);
blogArticles.data.allMdx.edges.forEach(({ node }) => {
let imageFileName = ... //some stuff
createPage({
path: `${node.fields.slug}`,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
slug: `${node.fields.slug}`,
id: node.id,
imageFileName: imageFileName
}
});
});
};
All of this works when its directly in gatsby-node.
However, having moved stuff, I now get:
"gatsby-node.js" threw an error while running the createPages
lifecycle:
blogArticles is not defined
ReferenceError: blogArticles is not defined
gatsby-node.js:177 Object.exports.createPages
/Users/kohlisch/blogproject/gatsby-node.js:177:19
next_tick.js:68 process._tickCallback
internal/process/next_tick.js:68:7
So it looks like it's not waiting for the graphql query to resolve? Or what might this be? I just basically want to move a few things out of my gatsby-node file, into separate functions, so that its not so cluttered. Is this not possible?
There are two rules you need to follow when importing in gatsby-node.js:
1. Use node.js require syntax.
./src/components/util/gatsby-node-functions
const importedFunction = () => {
return Date.now();
};
module.exports.importedFunction = importedFunction;
gatsby-node.js
const { importedFunction } = require(`./src/components/util/gatsby-node-functions`);
// ...
// Use your imported functions
console.log(importedFunction());
Reference: Gatsby repo issue, also incluces hack how to use ES6 import statement if you want to add complexity just for using an import statement.
2. Do not pass gatsby-node.js specific attributes to your imported functions
If you attempt to outsource for example, your createPages function, actions will be undefined:
const importedFunction = (actions, node) => {
const {createPage} = actions; // actions is undefined
createPage({
path: `${node.fields.slug}`,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
slug: `${node.fields.slug}`,
id: node.id,
}
});
};
module.exports.importedFunction = importedFunction;
Feel free to speculate why you cannot pass the attributes. The Gatsby documentation mentions "Redux" for handling state. Maybe Redux does not supply state outside your gatsby-node.js. Correct me if I'm wrong