I am creating a node module and want the end user of this module to be able to pass it configuration options that will have an effect on the modules functions.
For example, with express you can create an express app and pass it to your routes as such:
const express = require('express');
const app = express();
require('./api/routes/login)(app)
and then in your routes file you can use that app -> /api/routes/login
//login.js
module.exports = function(app){
app.get('/login', function(req, res){//blah}
}
So what I am trying to do in my module is something similar in which an end user can pass an optional config parameters for the module's functions to use. This is was I was trying.....
In my module file:
const default_options = {"enabled":true}
module.exports = function(options) {
const options = options || default_options
exports.login = function(credentials) {
if(options.enabled == true){
//do something
}else{
//do something else
}
}
}
And then someone using the module would be able to do something like this:
//some js file
var options = {"enabled":false}
const mod = require('mymodule')(options)
mod.login(creds)
The error I am getting is 'Cannot read property 'login' of undefined'. I suspect it has something to do with me using exports twice but trying to figure out how to structure my module's js file to be able to use these options if passed it and apply them to the exposed functions.
Any help is appreciated, thank you!
You need to return an object from the exported function.
const default_options = {"enabled":true}
module.exports = function(options) {
const options = options || default_options
return {
login: function(credentials) {
...
}
}
}
Related
I have created a helper.js file that will contain helper functions. In order to use the helper functions, I have to import the file in all the locations where I want it to be used. Is there any way I can load/import the helper.js file globally?
helper.js
module.exports = {
responseObject: function (status, message, data, error) {
return {
status: status,
message: message,
data: data,
error: error
}
}
}
index.js
// load mongoose
require('./db/mongoose')
const express = require('express')
const app = express()
const port = process.env.PORT || 3000
app.use(helper)
// load routers
const userRouter = require('./routers/user')
app.use(express.json()) // automatically converts incoming requests to json
app.use(userRouter)
app.listen(port)
UserController.js
const User = require("../models/user")
const helper = require("../helper")
exports.createUser = async (req, res) => {
const user = new User(req.body)
try {
await user.save()
res.status(201).send({
status: true,
message: 'Registration success',
data: user,
error: {}
})
} catch (error) {
const response = helper.responseObject(false, "Wrong", {}, error)
res.status(400).send(response);
}
}
From the documentation:
In browsers, the top-level scope is the global scope. This means that
within the browser var something will define a new global variable. In
Node.js this is different. The top-level scope is not the global
scope; var something inside a Node.js module will be local to that
module.
This means you can't just define your responseObject in the "global" scope, as it always refers to the scope local to the module and will not be available to other modules.
But there's a way to do it, although I would not recommend it (search Google or SO why that's the case, this has been discussed in detail already). You can add your function/variable to the global object (which is somewhat similar to the window keyword in browsers):
// in your app.js "add" the exported stuff from the helper-module to the global object
const express = require('express')
...
global.myHelper = require('./helper');
// in e.g. your user-controller you could then access it like
const response = global.myHelper.responseObject(false, "Wrong", {}, error)
Is there any way to split it without needing to state each app.get?
Like this, I have to add a new app.get for each route.
app.get('/', require('./modules/menu'))
The goal is to make it automatic, so it'll get the path, parameters and stuff and send to the correct file.
Thanks!
Put all URL handler functions to separate files, for example, put all those handlers to files in directory ./modules.
Example handler file:
function handle(req,res){
res.send("file1");
}
module.exports = handle;
Use a for loop to create routes for app:
const glob = require("glob");
const glob_promise = require("glob-promise");
var log = console.log;
var files = [];
async function main(){
files = await glob_promise("./modules/**/*.js");
log(files);
for (let i=0; i<files.length; i++){
let file = files[i];
let url_path = file.replace("./modules","").replace(".js","");
log(url_path);
app.get(url_path, require(file))
}
}
main();
This is the way I work with express
app/
routes/
controllers
app.js
you can use app.js to import the files for controllers and routes
const routes1 = require("./routes/route1.json")
const routes2 = require("./routes/route2.json")
inside routes/route1.json you can then import your controllers for those routes:
let express = require('express')
const authTokenController = require("../controllers/auth/token")
let api = express.Router()
api.post('/my-controller', myController.myMethod)
module.exports = api
The good part is that you can use objects as controllers
class myController {
myMethod(request, response) {
}
}
module.exports = new authTokenController
So now you can load your very well organized app into express
app.use('/api', routes1)
app.use('/app', routes2)
After receveing help about using Express, I continued to follow tutorials about Node.js. I'm at a point where i'm building my own routes in controllers to create a REST API. I have two files, app.js and /controllers/account-api.js.
Here's my app.js shortened (i deleted the parts that were not used my my test), and the line that is returning me some issues.
import express from 'express';
import exphbs from 'express-handlebars';
import * as accountApiRoutes from './controllers/account-controller.js';
import * as bodyParser from 'body-parser';
var app = express();
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
app.use(bodyParser.urlencoded({extended:true}));
app.use(accountApiRoutes); // ISSUE HERE, i tried ('/account-api', accountApiRoutes) too
app.get('/', function(req, res)
{
res.redirect('/server-home');
});
app.get('/server-home', function(req, res)
{
res.render('server-home');
});
app.listen(1337, '127.0.0.1');
console.log('Express Server running at http://127.0.0.1:1337/');
And here's my ./controllers/account-api.js shortened again to give the main elements that causes the issue :
import express from 'express';
const apiRouter = express.Router(); //ISSUE HERE
var accounts = [];
accounts.push( { code: 1, name: 'Pierrette', adress: 'Sur la Lune'} );
// =========== API ROUTES =========== //
// GET
apiRouter.route('/produit-api/produit/:code')
.get( function(req, res, next) {
var codeSended = req.params.code;
var account = findAccountInArrayByCode(codeSended);
res.send(account);
});
// =========== METHODS AND FUNCTIONS =========== //
// GET
function findAllAccounts() {
return accounts;
}
function findAccountInArrayByCode(codeSended) {
var accountFound = null;
for(i in accounts)
{
if(accounts[i].code === codeSended)
{
accountFound = accounts[i];
break;
}
}
return accountFound;
}
module.exports = { //ISSUE HERE
getApiRouter: function() {
const apiRouteur = express.Router();
return apiRouter;
}
}
The problem is.. This code returns me "module" is not defined.
I use Node.JS with Express and Handlebars.
For what I saw online, when using "app.use", it requires a function. And module.exports too. I tried various solutions, like this one :
account-api.js
const apiRouter = function() { return express.Router() }
...
module.exports = apiRouteur;
The problem is that it changes the type of apiRouteur, when calling apiRouteur.get from IRouter to () => Router, and the routes break.
I don't know how to arrange the code to make the module.exports returning a function that works, or if the problem is not even about the type of value returned, but if I'm missing dependancies, etc...
Thanks for your help.
EDIT : With the explanations I got, I replaced all my ES6 calls to commonjs imports. But it doesn't solve the problem. Now it's "require" that's not define.
I was stuck firstly by "require is not defined", and the solution I was given by reading old SO threads about it, the answer was regularly to use ES6 imports...
ack to the begining I guess ! Maybe I miss something in my project?
Your problem is this line app.use(accountApiRoutes); and you are using a mix of ES6 and commonjs modules.
To fix the module imports (as you are using .js files not .mjs) change all your ES6 imports i.e import * as xyz imports to commonjs imports const x = require('...');
The accountApiRoutes is an object but not a Router object.
To fix you just need to pass the router object to the app.use function.
So you will need to make a couple of changes based on what you have supplied above.
// ./controllers/account-api.js
const express = require('express');
...
module.exports = { //ISSUE HERE
getApiRouter: function() {
return apiRouter; // you have already defined the router you don't need to recreate it
}
}
Properly pass the Router object to the express app.
const express = require('express');
const exphbs = require('express-handlebars');
const bodyParser = require('body-parser');
const accountApiRoutes = require('./controllers/account-controller.js');
...
app.use(accountApiRoutes.getApiRouter());
You could also just set module.exports to your configured router in your account-api.js and then you could pass it directly to app.use as you have already done in your server above. Either way should work. To can do that as follows:
// ./controllers/account-api.js
const express = require('express');
...
module.exports = apiRouter;
And in your server.js
const accountRouter = require('./controllers/account-controller.js');
app.use(accountRouter);
Diving into node, I have a question on how an included script, can access the main's script methods or variables
For instance, say that I have a logger object initiated in the main script and I include another js file, which needs access to the logger. How can I do that, without injecting the logger to the included script.
//main node script
var logger = require('logger'),
app = require("./app.js")
//app.js
function something(){
//access logger here !!!!!!!
}
exports.something = something
Hope it is clear
Thanks
Try doing:
//main node script
var logger = require('logger'),
app = require("./app.js")(logger);
//app.js
var something = function (logger){
logger.log('Im here!');
}
module.exports = exports = something;
Edit:
If you want to split your main app's code into different files, on your main script file you can do something like: (This is how I'm splitting my main app.js into different sections)
// main app.js
express = require('express');
...
/* Routes ---------------------*/
require('./config/routes')(app);
/* Socket IO init -------------*/
require('./app/controllers/socket.io')(io);
/* Your other file ------------*/
require('./path/to/file')(app);
...
// config/routes.js
module.exports = function(app){
app.configure(function(){
app.get('/', ...);
...
}
}
// app/controllers/socket.io.js
module.exports = function(io){
// My custom socket IO implementation here
}
// ...etc
Edit 2:
Your function can also return a JS object, in case you want to do something on the main app.js with a custom script.
Example:
// main app.js
...
/* Some controller ---------------------*/
var myThing = require('./some/controller')(app);
myThing.myFunction2('lorem'); // will print 'lorem' on console
...
// some/controller.js
// Your function can also return a JS object, in case you want to do something on the main app.js with this require
var helperModule = require('helperModule');
module.exports = function(app){
var myFunction = function(){ console.log('lorem'); }
// An example to export a different function based on something
if (app.something == helperModule.something){
myFunction = function() { console.log('dolor'); }
}
return {
myFunction: myFunction,
myFunction2: function(something){
console.log(something);
}
}
}
You can also simply export a function or an object containing functions, without sending any parameter like this:
// main app.js
...
var myModule = require('./path/to/module');
myModule.myFunction('lorem'); // will print "lorem" in console
...
// path/to/module.js
module.exports = {
myFunction: function(message){ console.log(message); },
myFunction2: ...
}
Basically, whatever you put inside module.exports is what gets returned after the require() function.
module.exports is what is returned when you require the file. in #DavidOliveros example, that's a function that takes app or io as parameters. the function is executed right after the require takes place.
If you want to expose a method of the included script to the main, try this:
// child.js
module.exports = function(app){
var child = {};
child.crazyFunction = function(callback){
app.explode(function(){
callback();
});
};
child.otherFunction = function(){};
return child;
};
// app.js
var express = require('express');
var app = module.exports = express();
var child = require('./child.js')(app);
app.get('/', function(req, res){
child.crazyFunction(function(){
res.send(500);
});
});
I have an ExpressJS 3 app and have my routes in a separate file. I'd like to declare some configs in my app.js that are available in the routes file - outside of the route definitions themselves.
In my app.js I would like to do something such as:
app.set('path_to_models', '/path/models/');
Then in my routes/index.js file I would like to be able to do this:
var Product = require(app.get('path_to_models') + 'product');
exports.list = function(req, res){
return Product.find(function (err, products) {
if (!err) {
res.render('product/manage', { products: products });
} else {
res.render('5xx');
}
});
};
I've seen a few posts related to this but none really addressed what I am looking for. I know that I can wrap the routes in a function but would like an alternative way that will keep my code as is if at all possible.
I just make a separate config module that holds my configuration and in any module that needs info from the config, I just require it as normal. Why drag express into the mix when a more loosely coupled approach works just fine.
config.js
exports.pathToModels = "/path/to/models";
routes.js
var config = require("./config");
console.log(config.pathToModels);
Simply pass app as an argument to `routes/index.js':
var routes = require('./routes/index.js')(app);
UPDATED: This should be
var routes = require('./routes/index.js').init(app);
and in routes/index.js:
var app;
exports=function(whichApp) {
UPDATED: This should be
exports.init=function(whichApp) {
app=whichApp;
// do initialization stuff
return exports;
}
exports.list=...