I am still trying to fully understand how exporting and importing modules works in Nodejs.
I am using the following file to seed a mongodb database. This file runs exactly as it should and returns exactly the result I am expecting, when I execute it as a standalone file. My issue is I want to use this file in two different places in my app. So I am trying to make it an exportable/importable module. Here is what I have tried:
seed.js looks like this:
'use strict';
// library modules
const {ObjectID} = require('mongodb');
const seeder = require('mongoose-seed');
const jwt = require('jsonwebtoken');
const util = require('util');
// local modules
const {Course} = require('./../models/course');
const {Review} = require('./../models/review');
const {User} = require('./../models/user');
const {data} = require('./../data/data.js');
const config = require('./../config/config.js');
/*==================================================
build seeding Courses, Reviews, Users
==================================================*/
// Connect to MongoDB via Mongoose
let seed = seeder.connect(process.env.MONGODB_URI, (e) => {
console.log(`Connected to: ${process.env.MONGODB_URI} and seeding files`);
// Load Mongoose models
seeder.loadModels([
'src/models/user.js',
'src/models/review.js',
'src/models/course.js'
]);
// Clear specified collections
seeder.clearModels(['User', 'Review', 'Course'], function() {
// Callback to populate DB once collections have been cleared
seeder.populateModels(data, function() {
seeder.disconnect();
});
});
});
module.exports = seed;
Within app.js I have these two lines
const seed = require('./middleware/seed');
and
app.use(seed);
I have also tried, to no avail
app.use(seed());
What is missing? I don't want to use the code in-line in two different places (DRY you know).
It is failing with this error:
throw new TypeError('app.use() requires a middleware function')
I am sorry about the formatting I thought I was using markdown, but I am clearly not.
You are executing your seeder function in that module and not returning any middleware function at all. Middleware is always a function and it has the form of:
const seed = function (req, res, next) {
...
next()
}
With what you have now, the best you can do is the following which will certainly seed your models when the module is loaded.
const seed = require('./middleware/seed');
Are you trying to run those two functions as middleware, on every request? In that case you'd do something like this:
const seed = function (req, res, next) {
seeder.connect(..., (e) => {
...
seeder.clearModels(..., ()=>{
...
next()
})
})
})
If you want to run that seeder code at the module level AND to expose it as middleware then wrap the current module level code in function and execute it when the module loads. Notice that the new function optionally handles the next callback if it receives it.
function doSeed(next){
//your code currently running at module level
if(next)
return next()
}
doSeed(); //run the code at the module level
const seed = (req, res, next) => doSeed(next))
module.exports = seed;
One way that I allow myself to have access to a variable in different instances is adding it to the process variable. In node.js no matter where in the project it is, it will always be the same. In some cases, I would do process.DB = seed(); which would allow process.DB to always be the result of that. So inside your main class you can do process.DB = seed(); and all of your other classes running off of that one main class would have access to process.DB keeping your database readily available.
Related
I'm trying to write a basic local api for myself using Next.js, it is a timeline generator, and I am stuck at actually reading the file from the api folder.
What do I want in my local aplication:
1.A simple page where I can input an event, with a date and description
2.Open a list.json file somewhere and push that new event to that json file, writing on it.
What I am currently doing and where I am stuck:
I am aware we cant write on files on the client side, so I started looking at the api routes in next js to access the JSON file, but I cannot even manage to read it!
I have an api folder inside pages folder, and in this api folder I have two files: one is the list.json file, where I previously manually write some events with respective dates; and the other is getlist.js, with this code:
var fs = require('fs');
export default function getList(req, res) {
const rawList = fs.readFile('list.json');
var list = JSON.parse(rawList);
res.json(list);
}
Now on the pages folder I have a index.js file where I try to access this getlist.js api using getStaticProps(), like this:
import getlist from './api/getlist'
export async function getStaticProps(){
var list = getlist();
return {
props: {
list
}
}
}
I have tried using other stuff, like the fecth function, to get to getlist.js, but nothing I do seems to work.
Can anyone help me?
And since I'm already in here, how would I manage to get the input from the form I already have in my client side page and write it to that list.json file in my api folder?
There are two ways how you can read json in next.js:
Import inside getStaticProps [https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation]
export async function getStaticProps(context){
const example = await import('./api/example.json');
return {props: {example: example.default}}
}
Import or read in handler function inside api folder [https://nextjs.org/docs/api-routes/introduction]:
const fs = require('fs');
export default async function handler (req, res) {
const example = await fs.readFile('./example.json');
return res.status(200).json({example});
}
In order to write *.json file you need to send request with value to the server api (handler from api folder that was mentioned before).
That's how the part to write json will look like:
const fs = require('fs');
export default async function handler(req, res) {
//...
if (req.method === 'POST'){
fs.writeFileSync('./example.json', JSON.stringify(req.body))
return res.status(200).json({});
}
//...
}
I want to write functions for repeating tasks. So for example I want to have a file lib/dbFunctions to handle anything with my database, like searching the databse for a specific user.
so I just want to create the function searchUser(username) and just import my functions with e.g. const dbFunctions = require('lib/dbFunctions), so I can call dbFunctions.searchUser("ExampleUser");
How would I solve this in NodeJS?
I only found examples using the express.Router() looking like this:
const express = require('express');
const customRoutes = express.Router();
cusrtomRoutes.get('/login',function(req,res){
res.render('account/login');
});
module.exports = {"CustomRoutes" : customRoutes};
However I somehow couldn't figure out how to write a completly custom class with functions.
I usually do such kind of stuff by making a class such as UserRepository.
class UserRepository() {
static searchUser(user){
// your logic here
}
static getUserByEmail(email){
// your logic here
}
}
module.exports = UserRepository;
You can call this function from anywhere like this:
const UserRepository = require('./user-repository.js');
UserRepository.searchUser(user);
The advantage of above approach is that you can process other logic in constructor which would be require before talking to the database. This is OOP based approach basically.
The other way is (not OOP based):
const searchuser = (user) => {};
const getUserByEmail = (email) => {};
module.exports = {
searchuser,
getUserByEmail,
};
You can use in the same way:
const UserRepository = require('./user-repository.js');
UserRepository.search(user);
Sounds like you need to know how to make a module.
Say you have an index.js like this:
const dbFunctions = require('./lib/dbFunctions')
console.log(dbFunctions.searchUser('Dan'))
You can make your module work by creating a file at <project_root>/lib/dbFunctions.js which can look like this:
const searchUser = (name) => `found user ${name}`
module.exports = {searchUser}
Now if you run node index.js you'll see found user Dan print in your console.
It's important to note that when you're importing modules node expects a path like lib/dbFunctions to exist inside node_modules whereas when you're trying to import a local module within your project you need to specify the relative path from where you're importing hence I used ./lib/dbFunctions in my example.
Another common way to create a custom lib in your project is using a pattern called barrel files or barrel exports.
Instead of having a file called dbFunctions.js you'd create a directory at /lib/dbFunctions with an index.js inside the root of that directory. You can then seperate out all of your functions into their own modules like this for example:
// /lib/dbFunctions/searchUser.js
const searchUser = (name) => `found user ${name}`
module.exports = searchUser
// /lib/dbFunctions/searchPost.js
const searchPost = (id) => `found post ${id}`
module.exports = searchPost
And then your index.js file would look like this:
// /lib/dbFunctions/index.js
const searchUser = require('./searchUser');
const searchPost = require('./searchPost');
module.exports = {
searchUser,
searchPost
}
Then your main entry point (index.js)
const dbFunctions = require('./lib/dbFunctions')
console.log(dbFunctions.searchUser('Dan'))
console.log(dbFunctions.searchPost(2))
It seems you want to create a module consisting of a bunch of functions. If that's the case, a typical module of functions looks like this:
// file customFunctions.js
function custom1 () { ... }
function custom2 () { ... }
...
module.exports = { custom1, custom2, ... }
The module exports an object containing functions as properties.
Then, just require that file where you need it:
const customFns = require('./customFunctions');
customFns.custom1();
Somewhat new, please bear with me. Trying to use passport to authenticate only specific routes in a website. The routes are in a separate file called blog_a.js. I have the following functions created in the main server.js file:
function checkAuthenticated(req, res, next) {
if (req.isAuthenticated()){
return next()
}
res.redirect('/login')
}
function checkNotAuthenticated(req, res, next) {
if (req.isAuthenticated()){
return res.redirect('/')
}
next()
}
However, I'm trying to pass the above functions into the blog_a.js module, so I can use them as middleware to protect the routes within that module.
I have tried to use module.exports = {checkAuthenticated, checkNotAuthenticated} at the bottom of the main 'server.js' file, and then use a let server = require('../server.js') line to import these functions to the module that contains the routes I want to protect.
However, the above server variable comes back as undefined, and I've tried several permutations / destructuring methods to try to import it. All to no avail, I keep getting the routes failing due to the "undefined" object--Error: Route.get() requires a callback function but got a [object Undefined].
How can I set up the authentication in the server.js file but then pass its authentication functions to be used as middleware within an individual route file?
I looked at this solution, but it doesn't clearly explain how to get the middleware functions from one module--server.js--to another module--blog_a.js.
I got the following response on Patreon from Kyle of Web Dev Simplified, and it worked like a charm!
"You should be able to just create another file that is called authMiddleware.js or something like that. Then in that file define and export the functions at the end (module.exports = { function1, function2 }). Now in the places you need those functions you should be able to import them like so (const { function1, function2 } = require('./authMiddleware'))."
So I followed Kyle's advice and created the following separate file authMiddleware.js:
function checkAuthenticated(req, res, next) {
if (req.isAuthenticated()){
return next()
}
res.redirect('/login')
}
function checkNotAuthenticated(req, res, next) {
if (req.isAuthenticated()){
return res.redirect('/')
}
next()
}
module.exports = { checkAuthenticated, checkNotAuthenticated }
... and then used the following require statements to get access to the functions:
-in main server.js --> const {checkAuthenticated, checkNotAuthenticated} = require('./authMiddleware.js')
-in router file blog_a.js --> const {checkAuthenticated, checkNotAuthenticated} = require('../authMiddleware.js')
Thanks!
I have an Express.js project where I am allowing plugins to be loaded and unloaded at runtime. Plugins have access to the Expess.js router stack to register their paths just like a normal script would such as:
var express = require('express');
var router = express.Router();
module.exports = function(projectCoreObject) {
function Plugin() { }
// Plugin initialize called when a plugin is loaded.
Plugin.Initialize = function (done) {
// Register the router..
projectCoreObject.app.use('/', router);
};
// GET - /test
router.get('/test', function(req, res, next) {
res.send('Success!');
});
return Plugin;
};
While this all works great, I have the issue with unloading plugins removing their router from the stack.
Is there a proper way to remove a full router object from Express.js' stack at runtime? I can do individual middleware using their names but with a route like this example shows, the name is just 'router' in the stack.
I resolved this by using a named function trick to take an anonymous function and turn it into a named one. This way I can remove the router by its name then.
I am using expressjs and want to store project config variables in database for some reason. What is best way to access config variables from database so I will not need to query database on each router i am loading.
Should I use app.locals?
To do this, I don't export the "router" directly but a function that accept an argument which will receive the config. Here's an example :
// router.js
module.exports = function(database) {
return {
getLogin: function(req, res) {
// database exist here
}
}
}
// app.js without the creation of app and database
var app = express();
var database = require('./database');
var router = require('./router')(database)
app.get('/login', getLogin);
This way, you have the modularity and the possibility to interact easily with the database.