How can I split my node module down into additional files? - javascript

I'm trying to split my node module functions into additional files as there are numerous functions I'm looking to add.
I would like to call the main file's functions into files loaded from lib and be able to call the lib functions directly, see:
In my main file index.js:
function Api(opt) {
// set options
}
Api.prototype.get = function (endpoint) {
return this.request('GET', endpoint, null);
};
Api.prototype.Catalog = require('./lib/catalog.js');
module.exports = Api;
Then in lib/catalog.js
function Catalog () {};
Catalog.prototype.getCategories = function () {
return Api.get('categories');
}
module.exports = Catalog;
Then I'm hoping to achieve the following when the module is required, so the Catalog file will give access to :
var Module = require('module');
api = new Module({
url: 'http://example.com', // without trailing slash
username: 'username',
password: 'password'
});
api.Catalog.getCategories();
When doing it this way I am getting the following error:
TypeError: Cannot read property 'getCategories' of undefined
Is there a recommended way to achieve this or perhaps splitting it down into multiple node modules?

Try adding require for the new modules
var Module = require('module');
var Catalog = require('Catalog');
var Api = require('Api');
api = new Module({
url: 'http://example.com', // without trailing slash
username: 'username',
password: 'password'
});
api.Catalog.getCategories();

Related

Customizing service in passwordless plugin in Strapi

I am trying to modify the behaviour of the "passwordless" strapi plugin so that it would generate the verification code only with digits. So for this I need to override a function createToken in the plugin's service.
I created a folder with the name of the plugin inside extensions folder as specified in Strapi docs. And inside this folder(strapi-plugin-passwordless) I created a file strapi-server.js in which I export a function:
module.exports = (plugin) => {
plugin.services.passwordless.createToken = async (email, context) => {
const settings = await this.settings();
const tokensService = strapi.query("plugin::passwordless.token");
tokensService.update({ where: { email }, data: { is_active: false } });
const body = customAlphabet("1234567890", 6);
const tokenInfo = {
email,
body,
context: JSON.stringify(context),
};
return tokensService.create({ data: tokenInfo });
};
return plugin;
};
The only difference between original createToken function the custom one is that the variable body is in the form of 6 digits.
However, Strapi doesn't run this function, instead it runs the original one on node_modules?
What may I possibly be doing wrong ?

cy.visit() must be called with a url or an options object containing a url as its 1st argument - Cypress.env('StudentPortal')

I'm trying to use Environment Variables via plugin method (https://docs.cypress.io/api/plugins/configuration-api#Switch-between-multiple-configuration-files), this has worked previously but recently has stopped working for me, any pointers would be great.
plugin/index.js
const fs = require('fs-extra')
const path = require('path')
function getConfigurationByFile(file) {
const pathToConfigFile = path.resolve('cypress', 'config', `${file}.json`)
return fs.readJson(pathToConfigFile)
}
module.exports = (on, config) => {
const file = config.env.configFile || 'build'
return getConfigurationByFile(file)
}
ConfigFile > build.json
{
"env": {
"StudentPortal": "https://www.google.co.uk"
}
}
Usage
cy.visit(Cypress.env('StudentPortal'));
As I said, this used to work and would visit the URL within the configFile, now I just get the following error:
CypressError
cy.visit() must be called with a url or an options object containing a url as its 1st argumentLearn more
cypress/support/commands.js:17:8
15 | Cypress.Commands.add('StudentPortalLogin', (email, password) => {
16 |
17 | cy.visit(Cypress.env('StudentPortal'));
It appears the baseURL may be missing. cy.visit() is looking for something in reference to the baseURL. I don't see one defined in the build.json file, that might fix the error to add a baseURL, "https://www.google.co.uk", then put authentication parameters inside the env{} part, per the example in your link.

Graphql requiring module outside vs inside GraphQLObjectType

May be the title is not suitable with my problem but let me explain my scenario.
I am working with Graphql schema.Here is my initial schema.js file https://github.com/sany2k8/graphql-udemy/blob/master/schema/schema.js
and it was working fine then I decided to split it into different small files e.g root_query_type.js, mutation.js, user_type.js and company_type.js. All the files are exported as module and required circularly. For example -
user_type.js
const graphql = require('graphql');
const axios = require('axios');
const { GraphQLObjectType, GraphQLString, GraphQLInt } = graphql;
//const CompanyType = require('./company_type'); // *this line causing error*
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
id:{ type: GraphQLString},
firstName:{ type: GraphQLString},
age:{ type: GraphQLInt},
company :{
type: require('./company_type'), // *this line fix the error*
resolve(parentValue, args){
return axios.get(`http://localhost:3000/companies/${parentValue.companyId}`)
.then(res => res.data)
}
}
})
});
module.exports = UserType;
company_type.js
const graphql = require('graphql');
const axios = require('axios');
const { GraphQLObjectType, GraphQLString, GraphQLList } = graphql;
const UserType = require('./user_type');
const CompanyType = new GraphQLObjectType({
name: "Company",
fields: ()=> ({
id: { type: GraphQLString},
name: { type: GraphQLString},
description: { type: GraphQLString},
users:{
type: new GraphQLList(UserType),
resolve(parentValue, args){
return axios.get(`http://localhost:3000/companies/${parentValue.id}/users`)
.then(res => res.data)
}
}
})
});
module.exports = CompanyType;
On my user_type.js file when I use const CompanyType = require('./company_type'); at the top of file like this const UserType it is showing me below error message
Error: User.company field type must be Output Type but got: [object
Object].
but if I comment out that line and put it directly then it works.
company :{
type: require('./company_type'),
resolve(parentValue, args){
return axios.get(`http://localhost:3000/companies/${parentValue.companyId}`)
.then(res => res.data)
}
}
So basically my question is why it is not working with const CompanyType = require('./company_type'); and but working withtype: require('./company_type'). I could be a simple logical issue but It couldn't able to find.Please help me out.
The behavior you're seeing is not specific to GraphQL, but rather node in general. You have cyclic dependencies in your modules, which is causing your require statement inside user_type.js to resolve to an incomplete copy of company_type.js.
According to the docs, given two modules (a.js and b.js) that require each other:
When main.js loads a.js, then a.js in turn loads b.js. At that point,
b.js tries to load a.js. In order to prevent an infinite loop, an
unfinished copy of the a.js exports object is returned to the b.js
module. b.js then finishes loading, and its exports object is provided
to the a.js module.
Moving the require statements inside your exports definition is one solution. You could also move your exports definition above your require calls to get the same affect. This question looks at cyclic dependencies in more depth and also offers some alternative solutions.
As a side note, this is one of the reasons I would recommend moving away from declaring a GraphQL schema programatically. You can use graphql-tools's generate-schema to generate a schema from a GraphQL language document. This prevents you from dealings with potential cycle dependencies and results in a much more readable schema. You can modularize your schema easily as well; your type definitions are just strings and your resolvers are just objects -- both of which can easily be combined.

NodeJS Variable Scope

I'm very, very new to the whole NodeJS stack, and I'm trying to rough up a simple login system for practice.
Jumping to my question,
app.js
...
var mongoose = require( 'mongoose' );
var templates = require( './data/inc.js' ); // includes schema structures
...
user.js - included in inc.js
...
module.exports =
{
"Schema" : new exports.mongoose.Schema({
"uid": mongoose.Schema.Types.ObjectId,
"username": { type:String, unique:true },
"alias": String,
"credentials":
{
"salt": String,
"password": String,
"key": String
},
"profile":
{
"age": { type: Number, min: 18 }
},
"last_login": Date,
"updated": { type: Date, default: Date.now }
})
}
...
The 'user.js' script above will not work because it doesn't have access to the mongoose object variable instantiated in the 'app.js' script. In PHP any included/required scripts would be able to access variables from the parent script, but in NodeJS as I know it for example I have to re-require/state the mongoose variable in order to create my schema tree.
user.js
...
* var mongoose = require( 'mongoose' ); // must include in script to use mongoose object
module.exports
{
...
}
...
Is there any work-around that will allow me the same scope access as PHP?
The answer is that there are workarounds, but you really don't want to use them, ever, ever, except for things which you want to hack into the global scope of all running modules in your application up to and including all dependencies (mongoose) and all of ITS dependencies.
override.js
global.thisIsNowAvailable = true;
flaky-file.js
if (thisIsNowAvailable) { /* ... */ }
index.js
require("./override");
require("./flaky-file");
The same will work for overriding methods on global prototypes, et cetera.
Unless your library is super-awesome and is intended to intercept, parse and interpret code at require-time
require("babel/register"); // all loaded modules can now be written in ES6
doing this for other reasons leads to horrible code-bases...
broken-index.js
require("flaky-file");
require("override");
// you might have just attempted to reference a variable that doesn't exist,
// thrown an error and crashed your entire server
// (not just a single connection, like PHP... ...the entire server went down,
// for everyone, and it has to be restarted).
Think of modules as separate function scopes.
It's really simple to do something like:
needs-mongoose.js
function doSomeInitWithMongoose (db) { /* ... */ }
function doSomeRuntimeWithMongoose (db, params) { /* ... */ }
module.exports = mongoose => {
doSomeInitWithMongoose(mongoose);
return {
run: params => {
/* ... app is run here ... */
doSomeRuntimeWithMongoose(mongoose, params);
}
};
};
configures-mongoose.js
var mongoose = require("mongoose");
function configure (db, cfg) { /* ... */ return db; }
module.exports = config => {
var configuredDB = configure(mongoose, config);
return configuredDB;
};
main.js
// to support arrow functions and other awesome ES6, including ES6 modules
require("babel/register");
var config = require("./mongoose-config");
var db = require("./configures-mongoose")(config);
var app = require("./needs-mongoose")(db);
app.run({ /* ... */ });
EDIT
Updated the last few files to be a structurally-correct pseudo-program (which does absolutely nothing, of course);
Of course, if index.js or server.js were to require("babel/register"); and then load main.js (without the Babel include in it), all of the require statements south of Babel could be written as ES6 modules, without issue.
server.js
require("babel/register");
require("./es6-main");
es6-main.js
import config from "./mongoose-config";
import configureDB from "./configures-mongoose";
import loadApp from "./needs-mongoose";
const db = configureDB(config);
const app = loadApp(db);
app.run({ /* ... */ });
Note that now I'm naming the functions I was originally returning, because in JS when you return a function, you can immediately call it...
getFunc( config )( data );
...but you can't act immediately on import statements.
Rule of thumb is that if you're going to export an object to the outside world, it should have 0 external dependencies, or all external dependencies will be set up later, by setters of some kind:
var utils = require("./utils"); // doesn't need any information
utils.helperFunc(data);
or
var catsAndPorn = true;
var internets = [];
var SeriesOfTubes = require("series-of-tubes");
var internet = new SeriesOfTubes( catsAndPorn );
internets.push( internet );
or
var bigOlFramework = require("big-ol-framework");
bigOlFramework.setDBPool( myDBCluster );
http.createServer( bigOlFramework.connectionHandler ).listen( 8080 );
None require outside information for their actual init (though may require their own internal dependencies).
If you want to return something which does rely on external init, either export a factory/constructor, or export a function, which accepts your config/data, and then returns what you want, after an init sequence.
EDIT 2
The last piece of advice here is that as far as mongoose usage goes, or Gulp, to a similar extent, or several routers...
...when you want to have a single file which registers its contents to a registry, or requires a core-component, to be able to return something, the pattern in Node which makes the most sense is to return a function which then does the init
var Router = require("router");
var router = new Router( );
require("./routes/login")(router);
require("./routes/usesrs")(router);
require("./routes/articles")(router);
Where "./routes/articles.js" might look like
import ArticlesController from "./../controller/articles"; // or wherever
var articles = new ArticlesController();
module.exports = router => {
router.get("/articles", ( ) => articles.getAll( ));
router.post("/articles", ( ) => articles.create( ));
};
So if you were looking to structure ORM based on schema, you might do similar:
var mongoose = require("mongoose");
var Users = require("./schema/users")(mongoose);
where "./schema/users" looks like:
module.exports = mongoose => {
return new mongoose.Schema({ /* ... */ });
};
Hope that helps.
Why don't you just do this?
var mongoose = require( 'mongoose' );
...
"Schema" : new mongoose.Schema({
Instead of:
exports.mongoose.Schema // I'm not sure where you got `exports.mongoose` from.
Also you don't have to use the .js when requiring like:
var templates = require( './data/inc' );
Edit
I believe you can't do it like PHP. Also the requires are cached so no need to worry about re requiring.

How do I extend sails.js library

I would like to extend sailsjs to have something similar to rails strong params
i.e.
req.params.limit(["email", "password", "password_confirmation"])
I have created this already as an add on, but I would like it to automatically attach to the request.params
Addon:
limit = function(limiters){
params = this.all();
self = Object();
limiters.forEach(function(limiter){
if(params[limiter]){
self[limiter] = params[limiter]
}
return self;
}
request:
req.params.limit = limit;
req.params.limit(["email", "password"]);
How would I go about adding this to the framework as a module?
i think you could just create a policy
// policies/limit.js
limit = function(limiters){
params = this.all();
self = Object();
limiters.forEach(function(limiter){
if(params[limiter]){
self[limiter] = params[limiter]
}
return self;
}
module.exports = function limit (req, res, next) {
req.params.limit = limit;
req.params.limit(["email", "password"]);
next();
};
then you can add the policy in your ./config/policies.js file. the example is for all controllers/actions. in the link above is the documentation on how to add it to specific actions.
// config/policies.js
module.exports.policies = {
'*': 'limit'
};
EDIT: of course you can do the call to req.params.limit(...); in your controller if you don't want it static in your policy. policies are in general nothing more than express middlewares
I see the use cases for this on controller actions, limiting possible searches and so on.
On the other hand: When you just want to your model not to take up undefined attributes found in a create request you could simply set schema to true for the corresponding model:
// models/example.js
module.exports = {
schema: true,
attributes: {...}
}
Setting it globally:
// config/models.js
module.exports.models = {
schema: true,
...
}

Categories

Resources