Wondering what would be the best practice for my case.
I have a variable I need to set its value on app load and access this value many times across my code. what is the best way to get this value?
Right now I'm just overriding a config file property. Does a global variable is better? Is there another way to do this?
The priority standard for configs IMHO is:
command line parameter
environment var
local config file
global config file
if no cli parameter found, fall to look into environment vars, then local config file, then global
I do this.
Put the variable in .env file:
# .env
APP_PORT=4000
In my app source code, I create a file named constants.js:
// constants.js
import { load as loadEnvVars } from 'dotenv'; // run `npm i dotenv` to install
// load the .env file content to process.env
loadEnvVars();
// export the variables I need
export const APP_PORT = process.env.APP_PORT || 3000;
I import that file when I need it like this:
// server.js
import Express from 'express';
// import the constant
import { APP_PORT } from './constants';
const app = Express();
app.listen(APP_PORT, () => console.log('server deployed');
Related
I want to load environment variables from the .env file using Vite
I used the import.meta.env object as mentioned in Docs
.env file:
TEST_VAR=123F
when trying to access this variable via the import.meta.env -> import.meta.env.TEST_VAR it returns undefined.
so, how can I access them?
According to the docs, you need to prefix your variables with VITE_:
To prevent accidentally leaking env variables to the client, only
variables prefixed with VITE_ are exposed to your Vite-processed code.
If you are trying to access env vars outside your app source code (such as inside vite.config.js), then you have to use loadEnv():
import { defineConfig, loadEnv } from 'vite';
export default ({ mode }) => {
// Load app-level env vars to node-level env vars.
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
return defineConfig({
// To access env vars here use process.env.TEST_VAR
});
}
For svelteKit
// vite.config.js
import { sveltekit } from '#sveltejs/kit/vite';
import { defineConfig, loadEnv } from 'vite';
/** #type {import('vite').UserConfig} */
export default ({ mode }) => {
// Extends 'process.env.*' with VITE_*-variables from '.env.(mode=production|development)'
process.env = {...process.env, ...loadEnv(mode, process.cwd())};
return defineConfig({
plugins: [sveltekit()]
});
};
if you want to access your env variable TEST_VAR you should prefix it with VITE_
try something like
VITE_TEST_VAR=123f
you can access it with
import.meta.env.VITE_TEST_VAR
Here are three mistakes/gotchas that tripped me up.
Ensure the .env files are in the root, not the src directory. The filename .env and/or .env.development will work when running locally.
Restart the local web server for the variables to appear: npm run dev
Prefix the variables with VITE_ (as already mentioned by Mahmoud and Wonkledge)
Another solution that worked for me is to manually call dotenv.config() inside the vite.config.js. That will load variables from .env (all of them!) into process.env:
import { defineConfig } from 'vite'
import dotenv from 'dotenv'
dotenv.config() // load env vars from .env
export default defineConfig({
define: {
__VALUE__: process.env.VALUE
},
//....
}
where .env file could be:
VALUE='My env var value'
As stated in docs, you can change the prefix by mdoify envPrefix.
Env variables starting with envPrefix will be exposed to your client source code via import.meta.env.
So changing it to TEST_ will also work.
export default defineConfig({
...
envPrefix: 'TEST_',
...
})
You can change this option whatever you want except for empty string('').
envPrefix should not be set as '', which will expose all your env variables and cause unexpected leaking of sensitive information. Vite will throw an error when detecting ''.
So overriding the dotenv config directly to remove prefix completely could be an inappropriate action as all fields written in env would send directly into the client.
I had the same issue and solved it by running
pnpm add dot-env
pnpm add -S dotenv-webpack.
Lastly I made sure that I added VITE_ before the name I had for my environment variable, that is from MAP_API_KEY to VITE_MAP_API_KEY.
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 };
How I can set baseUrl for axios using npm param?
I would like to write command like this npm run build --baseUrl={url address} and in axios config set baseURL param to param from command.
It is possible ?
I don't think it is possible if you call that --baseUrl like that, as if you do it like that, you're going to have to define --baseUrl for npm start and other scripts, and you'll quickly end up with a spaghetti code.
I humbly suggest a way to mitigate this issue:
Create an .env file that is filled with your environment variables for development. Add this to .gitignore and don't push this into your production server. This file is not used for production.
Add a new BASE_URL variable, like:
BASE_URL=http://YOUR_API_LOCATION/
Use libraries like dotenv (install by npm i dotenv) so you can read .env files.
Create a new utility for axios, let's say:
// client.js
const axios = require('axios');
const dotenv = require('dotenv');
// get all environment variables here.
dotenv.config({ path: '.env' });
// create a new axios instance with the base url from your '.env' file.
const axiosInstance = axios.create({
baseURL: process.env.BASE_URL,
/* other custom settings */
});
module.exports = axiosInstance;
You can reuse your axiosInstance like this:
// request.js
const axiosCustomClient = require('./client');
axiosCustomClient.get('relative/path/to/your/api');
The code environment is browser. bundle tool is webpack. I have a router.js file like:
import foo from './views/foo.vue'
import bar from './views/bar.vue'
import zoo from './views/zoo.vue'
//use foo, bar, zoo variables
I've many '.vue' files to import like this under views folder. Is there a programmatical way to auto import all [name].vue as local variable [name]? So when I add or remove a vue file in views, I don't need to manually edit router.js file. this one seems a little dirty.
for (let name of ['foo', 'bar', 'zoo']) {
global[name] = require(`./views/${name}.vue`)
}
Nope, that's it. You have a choice between dynamic import and automation, or explicit coding and type-checking / linting.
Unfortunately, it's one or the other. The only other way to do it is meta-programming, where you write code to write your code.
So you generate the import statements in a loop like that, and write the string into the source file, and use delimiting comment blocks in the source file to identify and update it.
The following works for me with webpack and vue.
I actually use it for vuex and namespaces. Hope it helps you as well.
// imports all .vue files from the views folder (first parameter is the path to your views)
const requireModule = require.context('./views', false, /\.vue$/);
// create empty modules object
const modules = {};
// travers through your imports
requireModule.keys().forEach(item => {
// replace extension with nothing
const moduleName = item.replace(/(\.\/|\.vue)/g, '');
// add item to modules object
modules[moduleName] = requireModule(item).default;
});
//export modules object
export default modules;
I have the following structure of my app:
index.js
const app = require("./app")
exports.api = app(...)
app.js
//all the imports goes here
//and exporting the main function
module.exports = app => {...}
Now I need to bundle app.js with webpack but index.js must stay intact
The question is, after bundling the app.js, how can I require it's main function app from index.js?
After webpacking app.js I'm getting error in index.js that says "app is not a function" which makes sense since app.js content is now wrapped in webpack staff.
Any suggestions are much appreciated.
Your "app" bundle needs to be exported as a commonjs.
module.exports = {
//...
output: {
library: 'app',
libraryTarget: 'commonjs',
}
};
The return value of your entry point will be assigned to the exports object using the output.library value. As the name implies, this is used in CommonJS environments.
So, in your index.js you can require('./dist/bundle-name.js').
seems to be a hack, but I made it work by adding an intermediate file and adjusting app.js and index.js like below:
index.js
const app = require("./bundle").app //changed to named import
exports.api = app(...)
bundle.js //an intermediate file which is now an entry point for webpack
import app from "./app"
exports.app = app
app.js
//all the imports goes here
//and exporting the main function
export default app => {...} //changed to default export
If anyone has explanation on why it works this way and how to simplify it without adding that bundle.js file, you are very welcome to comment!