I am using nest.js and I want to import object defined in auth.parameters.ts file. File is defined as below. It appears environmental variable is not visible unless below variable is declared in class.
export const obj = {
somevar : process.env.CUSTOM_VAR,
};
In my other classes I would like to import the file with import {SecurityParameters } from '../auth/auth.parameters'
and access the variable with console.log(SecurityParameters.somevar).
I can access the variable directly using process.env.CUSTOM_VAR or
I get undefined if I use somevar : process.env.CUSTOM_VAR in other file.
As you're using Nest's ConfigModule, then unless you're creating a custom config file you shouldn't be doing anything with process.env. Instead, you should be injecting Nest's ConfigService where you need the environment variables and using this.configService.get('CUSTOM_VARIABLE') (or whatever value you need). To get started with this, you need to import the config module
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
#Module({
imports: [ConfigModule.forRoot()],
})
export class AppModule {}
You can pass { isGlobal: true } to the forRoot() call to make the ConfigService injectable anywhere in your application.
If you want a different approach where you can use the process.env object instead, you can add this as the first two lines of your main.ts
import { config } from 'dotenv';
config();
Nest calls this (or something similar) under the hood in the ConfigService, but that doesn't make it immediately available.
Related
I am trying to implement a global module in nest.js
I have created a service like below
export interface ConfigData {
DB_NAME: string;
}
#Injectable()
export class ConfigManager {
private static _instance?: ConfigManager;
public static configData:ConfigData | null;
private constructor() {
console.log(ConfigManager)
if (ConfigManager._instance)
ConfigManager._instance = this;
else{
ConfigManager._instance = new ConfigManager()
ConfigManager.configData = <ConfigData|null><unknown>ConfigManager.fetchSecretData()
}
}
private static async fetchSecretData():Promise<ConfigData|null>{
// some data from db
}
// static get instance() {
// return ConfigManager._instance ?? (ConfigManager._instance = new ConfigManager());
// //return ConfigManager._instance ?? (ConfigManager._instance = ConfigManager.fetchSecretData()) //new ConfigManager());
// }
}
configuration.module.ts
#Global()
#Module({
providers: [ConfigManager],
exports: [ConfigManager],
})
export class ConfigurationModule {}
and in app.module.ts added ConfigurationModule in imports.
Also adding private constructor on service unable it to add in module.ts file.
I am expecting that I should be able to configData anywhere without importing the ConfigManager. but it's not working...
ConfigManager is not available without import.
You've only marked your module with #Global decorator, but the NestJS needs to somehow initialize that module and make it globally available.
What this means is that you have to add this module to your core application module and NestJS will do the rest for you, so something like this (or however your root module is named):
#Module({
imports: [ConfigurationModule],
})
export class AppModule {}
From the documentation
The #Global() decorator makes the module global-scoped. Global modules
should be registered only once, generally by the root or core module.
Global modules in nest only means you don't have to include that module in the imports: [] of every other module that requires it's providers. The providers in the global module still behaves as as normal providers i.e. you need to inject it where you need it.
So in your case since you have already added #Global() to ConfigManager and imported ConfigurationModule in app.module.ts, you will not need to add ConfigurationModule in imports for any other modules who want to use ConfigManager. You would, however, still need to inject the ConfigManager provider - that's what #Injectable() means :).
To do the injection, you need a constructor(private configManager: ConfigManager) {} in the class that needs to use ConfigManager, and since you need access to the type of the class, you'll need import { ConfigManager } from '../path/to/ConfigManager' as well.
this is 'per-design' of es6/ts: you cant use the class without importing it.
you are mixing the concepts of di (instantiation/composition) with importing (defining which classes are available in the module scope)
I have recently switched from CommonJS to ES6 modules in my NodeJS project. One of the challenge I'm facing is to define a global variable before I import one of my module. I used to do that with CommonJS in my main file:
const path = require('path');
global.appRoot = path.resolve(__dirname);
const myObj = require('./my-object-file');
where my my-object-file uses global.appRoot.
With ES6, I have tried the following:
import path from 'path';
global.appRoot = path.resolve(path.resolve());
import myObj from './my-object-file';
with my-object-file.js being:
export default {
root: global.appRoot
}
But I get undefined for global.appRoot in my-object-file.js.
What is going on here?
Are import modules called before anything in my code?
How can I solve this (knowing that I absolutely want to be able to define the path as a global variable accessible in my modules)?
Are import modules called before anything in my code?
Yes, all module imports are resolved before any code in the importing module runs.
However, imported modules are also executed in order, so if you do
import './setupGlobals';
import myObj from './my-object-file';
then the setupGlobals module code is executed before the my-object-file one. So it will work when do
// setupGlobals.mjs
import path from 'path';
global.appRoot = path.resolve(path.resolve());
I absolutely want to be able to define the path as a global variable accessible in my modules
No, you really don't want to do that. Instead of a global variable that might be created anywhere, explicitly declare your dependency!
If you have a separate module to define your globals anyways, just make that export those variables instead of putting the values on the global object:
// globals.mjs
import path from 'path';
const appRoot = path.resolve(path.resolve());
export { appRoot as default }
Then you can declaratively use this global constant in any of your modules:
// my-object-file.js:
import appRoot from './globals';
export default {
root: appRoot
}
I like Bergi's answer with:
import appRoot from './globals';
Then, any file that wants to can get access to appRoot and you preserve modularity.
But, in addition to that approach, my suggestion in comments was that rather than setting a global before you import, you export a function from your module and call that function, passing it the desired path. This is a general purpose way of passing initialization parameters to a module from the parent module without using globals.
And, I suggest you use the import.meta.url work-around for creating the equivalent of __dirname as outline here. What you were doing with path.resolve() is just getting you the current working directory, which is not necessarily __dirname as it depends upon how this module was loaded for whether they are the same or not. Besides if you just wanted the equivalent of cwd, you could just use process.cwd() anyway in your child module. Here's the equivalent for __filename and __dirname in an ESM module.
// create __dirname and __filename equivalents in ESM module
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Then, import the module initialization function (sometimes called a module constructor) and call the module constructor:
import myObjConstructor from './my-object-file';
const myObj = myObjConstructor(__dirname);
Then, inside of my-object-file, you export a function and when that function is called, you initialize your module using the passed in __dirname and return the myObj.
So, inside of my-object-file:
function init(__dirname) {
// do whatever you want for module initialization
// using __dirname
return yourObj;
}
export { init as default };
I am trying to make my Meteor code ES6 compatible. I had a file called /both/global.js containing functions and constants that I wanted to be accessible globally. For ES6 purposes, I moved it to /both/imports/global.js and prefaced all the functions and constants with export const.
I haven't changed the whole directory structure yet. The template javascript files are still in /client/controller/. I have added to them import statements for the exported functions and constants. When I write the import statements in the form:
import { fn1, fn2, ... } from '../../both/imports/global.js';
they work fine.
I would rather import all the functions and constants with a single:
import from '../../both/imports/global.js';
but that doesn't seem to do anything.
What am I doing wrong?
It's to do with the way imports and exports work.
What you need is
import * from '/both/imports/global.js'
Alternatively...
import something from '/both/imports/global.js'
Will look for the default export, and assign it to the variable called something.
import { fn1, fn2, ... } ...
Will import the named variables into your namespace
Another way is to do this:
import globalFunctions from '/both/imports/global.js'
Then your functions are callable like this:
globalFunctions.fn1()
globalFunctions.fn2()
I have injected one service while bootstrapping to use global variables:
bootstrap(AppComponent, [ GloablService ]);
global.service.ts
import {Http} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import 'rxjs/add/operator/share';
export class GlobalService {
baseUrl:string;
constructor() {
this.baseUrl='http://localhost:8080';
}
setBaseUrl(url:string) {
this.baseUrl=url;
}
getBaseUrl() {
return this.baseUrl;
}
}
Now I have bootstrapped this service along with my root component and i am able to access the variable successfully.
But whenever I change the value of the variable in nested components the change is not reflected globally.
I tried to use observables too but no use.
any suggestions?
Don't add providers: [ GloablService ] on any component, otherwise the components will get a different instance. Only provide it once at bootstrap() like you did, or only at the root component AppComponent.
I am trying to use ES6 imorts with babel and Node.js
import "./utils.js";
log("something"); //throw an error : log is not defined
My utils.js look like this :
function log(... params){
console.log(... params);
}
log("module utils executed");
//Is executed and display "module utils executed" in the console.
I have also tryed to use export function log(... params) and export default log(... params) but it doesn't works.
So I don't understand how this is suppose to works...
EDIT:
I know that an other way to import is to do import utils from "./utils.js"
But it's not what I want. I want to be able to use log() without prefixing it with the module variable name. Like in this blog post
there Different ES6 import and Node.js require Question Describe The Difference
In case you will use Node.js require:
your utils.js File will be
function log(params) {
console.log(params);
}
module.exports = {
log
};
The other File will import your Utils Module will be
var utils = require('./utils');
utils.log("test");
In case you will use ES6 Modules:
your utils.js File will be
var log = function (params) {
console.log(params);
}
var utils = {
log: log
}
export default utils;
The other File will import your Utils Module will be
import utils from 'utils';
utils.log("test");
UPDATE
According to your Comment, Yes you can do this But using ES6 Module
your utils.js File will be
function log(params) {
console.log(params);
}
function anotherLog(params) {
console.log(params);
}
export { log, anotherLog }
The other File will import your Utils Module will be
import { log } from 'utils';
log("test");
No, there is no way to import all exported members of a module into the current namespace. Importing a module for side effects (i.e. import 'utils') does nothing with the members of utils.
The closest you can get is something like this:
utils.js
export function log(...params) { ... }
export function foo(a) { ... }
main.js
import * as u from './utils';
u.log(1, 2, 3);
u.foo(4);
or
import { log, foo } from './utils';
log(1, 2, 3);
foo(4);
One of the design goals of the ES6 module spec is a static module structure, which allows resolution of imports at compile time (before executing anything). Allowing blanket imports would make static analysis more difficult.
EDIT (do not do this!)
As #Bergi pointed out in the comments, you can add functions to the global namespace as follows:
utils.js
function log(...params) { ... }
global.log = log;
main.js
import './utils'; // import for side effects, add properties to the global object
log(1, 2, 3); // the global object is in the scope chain, so this is resolved
However this is a bad idea. Global variables are slow to resolve and in general break the modularity you try to achieve by using modules in the first place.