JavaScript coding tips on creating a string from variables tags To familiarize myself with Javascript (and in particular NodeJS) I embarked on a concrete project.
In this project I need to &quot;build&quot; the URLs of different IP cameras.
All Cameras Information are in a JSON file
Everything works correctly, but I think to use some &quot;bad&quot; coding part and that there is certainly better to do (not always easy to adapt and learning to a new language)
Today I therefore submit my code to you for your criticism/advice/Optimization/...
Extract from the URL creation code:
const cameras_cfg = require(&quot;./cfg/cameras.json&quot;);
const replaceAllTagsToValue = (obj, chaine) =&gt; obj.reduce((f, s) =&gt; `${f}`.replace(new RegExp(Object.keys(s)[0], 'g'), s[Object.keys(s)[0]]), chaine)

makeCameraUrl = function (camera, type, flux, user) {
    var url = camera.url;
    var Obj = {
        &quot;@@login@@&quot;: user.login,
        &quot;@@passwd@@&quot;: user.passwd,
        &quot;@@ip@@&quot;: camera.ip,
        &quot;@@stream@@&quot;: camera.flux[type].stream,
        &quot;@@port@@&quot;: camera.flux[type].port,
        &quot;@@flux@@&quot;: camera.flux[type][flux]
    };
    return replaceAllTagsToValue(Obj, url)
}

let cam_id = &quot;102&quot;;
let url = makeCameraUrl(cameras_cfg.devices[cam_id], &quot;rtsp&quot;, &quot;hd&quot;, cameras_cfg.users.view);
console.log(&quot;Url:&quot;, url);

Extract from the cameras JSON file:
{
    &quot;users&quot;: {
        &quot;admin&quot;: {
            &quot;login&quot;: &quot;admin&quot;,
            &quot;passwd&quot;: &quot;xxxxx&quot;
        },
        &quot;view&quot;: {
            &quot;login&quot;: &quot;user&quot;,
            &quot;passwd&quot;: &quot;yyyyyy&quot;
        }
    },
    &quot;devices&quot;: {
        &quot;102&quot;: {
            &quot;id&quot;: 102,
            &quot;name&quot;: &quot;Cam 102 PTZ&quot;,
            &quot;ip&quot;: &quot;192.168.0.102&quot;,
            &quot;url&quot;: &quot;@@stream@@://@@login@@:@@passwd@@@@@ip@@:@@port@@/@@flux@@&quot;,
            &quot;flux&quot;: {
                &quot;rtsp&quot;: {
                    &quot;stream&quot;: &quot;rtsp&quot;,
                    &quot;port&quot;: 554,
                    &quot;hd&quot;: &quot;1&quot;,
                    &quot;sd&quot;: &quot;2&quot;
                }
            }
        },
        
        ../..
        
        &quot;106&quot;: {
            &quot;id&quot;: 106,
            &quot;name&quot;: &quot;Cam 106&quot;,
            &quot;ip&quot;: &quot;192.168.0.106&quot;,
            &quot;url&quot;: &quot;@@stream@@://@@ip@@:@@port@@/user=@@login@@&amp;password=@@passwd@@&amp;@@flux@@&quot;,
            &quot;flux&quot;: {
                &quot;rtsp&quot;: {
                    &quot;stream&quot;: &quot;rtsp&quot;,
                    &quot;port&quot;: 554,
                    &quot;hd&quot;: &quot;channel=1&amp;stream=0.sdp&quot;,
                    &quot;sd&quot;: &quot;channel=2&amp;stream=0.sdp&quot;
                }
            }
        }
    }
}   

 

Thank you in advance for helping me to progress in the right techniques !
Fun project, but I feel like you have overengineered this quite a bit... Why not just build the url from the given values without all the &quot;@@&quot;? I see there is some difference between URLs, but my first suggestion would be to make them uniform and have a simple function that builds the url from the given components to reduce the complexity enormously.
But ok, let us focus on your code and how it can be improved.
I copied your code and it didn't run. In your function replaceAllTagsToValue, you are calling .reduce on a value, which will always be an object in your code. You cannot reduce an object so I don't know how this &quot;works correctly&quot; or, in fact, works at all. Nevermind that, I understand what you're trying to do! Let's have a look at my suggested edits:
const translator = {
  &quot;@@login@@&quot;:  (user) =&gt; user.login,
  &quot;@@passwd@@&quot;: (user) =&gt; user.passwd,
  &quot;@@ip@@&quot;:     (_, camera) =&gt; camera.ip,
  &quot;@@stream@@&quot;: (_, camera, type) =&gt; camera.flux[type].stream,
  &quot;@@port@@&quot;:   (_, camera, type) =&gt; camera.flux[type].port,
  &quot;@@flux@@&quot;:   (_, camera, type, flux) =&gt; camera.flux[type][flux]
};

const makeCameraUrl = (user, camera, type, flux) =&gt;
  Object.entries(translator)
  .reduce(
    (url, [key, value]) =&gt; url.replace(key, value(user, camera, type, flux)),
    camera.url);

const user = json.users.view;
const camera = json.devices[102];
const type = &quot;rtsp&quot;;
const flux = &quot;hd&quot;;
makeCameraUrl(user, camera, type, flux);
// &gt; &quot;rtsp://user:yyyyyy@192.168.0.102:554/1&quot;

You are actually doing a lot of nice stuff:

Properly using const for immutable values
Using arrow notation for simple one-line functions
Tricky higher-order array functions such as reduce

But there seems to be a problem with every one of those points. To start:



⚛️ constantly changing
In javascript, you have four ways of declaring (I know, JS is horrible) a variable: var, let, const or none of those to define a global. For the sake of brevity: never use var (it's weird) and never make globals (this pollutes your namespace and maybe even other people's code).
As for let and const, they function identically to eachother but with an important difference: const declares a variable (or a constant, rather) that is immutable -- it cannot be changed after declaration.
const immutable = 1;
immutable = 2;
// &gt; TypeError: Assignment to constant variable.

You can't reassign it.
This is useful for technical reasons (your javascript engine could optimise your code under the hood), but also for reasons of readability and deliberateness. When you use const, you're telling me, the reader, &quot;I'm declaring a value here that I will not change later on, don't worry.&quot; I know I can trust this because javascript wouldn't even let you re-assign it even if you tried. You start off your program with two consts, but then you drop it and create a global :
makeCameraUrl = function (camera, type, flux, user) {
  //...
}

You have now created a function that will take up memory forever until you explicitly remove it from the global scope, and maybe you have just overwritten some other global value with that name... Globals, like I said, are a nightmare and I strongly advice against ever using them (like this anyways). The simplest fix here is to just plonk a const before it and we're save again:
const makeCameraUrl = function (camera, type, flux, user) {
  //...
}




 lets set this straight
But then, you're suddenly using let...
let cam_id = &quot;102&quot;;
let url = makeCameraUrl(cameras_cfg.devices[cam_id], &quot;rtsp&quot;, &quot;hd&quot;, cameras_cfg.users.view);

The purpose of let is to create a mutable variable; its value can change at any time, in any place. Personally I think that's not a wise way to treat a value because we humans are dumb and history has shown us we are not good at keeping track of mutation of our variables.
But alright, if you have to change a value then go ahead and use let. But what I'm seeing here are values that never change. I know this because I read through the entire code, but I would have liked to know this from the start. When you use let, you're telling the reader &quot;Hey, I'm defining a value here but I'm going to change it later on.&quot; When you never change a variable you've defined with let, you're lying  -- and code should never lie.
Again, the simplest fix here is to just make it const:
const cam_id = &quot;102&quot;;
const url = makeCameraUrl(cameras_cfg.devices[cam_id], &quot;rtsp&quot;, &quot;hd&quot;, cameras_cfg.users.view);




 Consistency
Not only did you use three ways of defining variables, you also use two ways of defining functions. Again, all these inconsistencies make your code have a high amount of WTF/minute.

It doesn't matter wich way you define functions, as long as you are deliberate about it. In javascript there's only one reason to differentiate between function types but that doesn't apply to your code, so I suggest you just use arrow functions like you did in the beginning:
const makeCameraUrl = (camera, type, flux, user) =&gt; {
  //...
}

This code tells me there is nothing fancy going on besides a plain old function -- just how I like them.
This matter of consistency also applies to the way you name things. You use both snake_case and camelCase, but I couldn't really find a reason for the difference.
const cameras_cfg = //...
const replaceAllTagsToValue =  //...

makeCameraUrl =  //...

let cam_id =  //...
let url = //...

If I'm right in this assumption (code that requires assumptions has a high rate of WTF/minute), please stick to either one to avoid such confusion. Again, doesn't matter which one, just stick to it after you choose (things such as casing and other issues of consistency can be alleviated by using code linters such as eslint).
Personally I don't like either way of casing my variables but my preference would go to camelCase because that way I don't have to contort my hand to type underscores all the time. As for legibility, I'd say that snake_case is better. You choose.
const cameraConfig = //...
const replaceAllTagsToValue =  //...

makeCameraUrl =  //...

let cameraId =  //...
let url = //...

On a side-note: I took the liberty to change some variables to better describe what they are:

camera_cfg... Luckily I know that cfg is short for config, but don't make me guess. Plase don't abbreviate your variable names -- we're not in the 90's anymore . Also, is it really a config? Are you using this json to allow for customisation, or are you just using it to store data?
cam_id... Camper id? Camouflage id? I understand that you mean camera, but the less I have to interpret what you mean, the better I understand what you actually mean. Deliberateness is actually the most important part of programming; functioning code is merely a byproduct.




 The meat
Now that the low hanging fruit is out of the way, let's look at the most important part (the meat) of your program:
makeCameraUrl = function (camera, type, flux, user) {
    var url = camera.url;
    var Obj = {
        &quot;@@login@@&quot;: user.login,
        &quot;@@passwd@@&quot;: user.passwd,
        &quot;@@ip@@&quot;: camera.ip,
        &quot;@@stream@@&quot;: camera.flux[type].stream,
        &quot;@@port@@&quot;: camera.flux[type].port,
        &quot;@@flux@@&quot;: camera.flux[type][flux]
    };
    return replaceAllTagsToValue(Obj, url)
}

If I understand correctly, you're using an object to encode information about a camera, and putting that info into the camera's url and return it. That's a great idea, but the way you did it has a few nasty side-effects.
Creating an object in every function call (Obj in this case) is expensive, and the garbage collector will make your browser hang a little everytime your single-use objects need discarding. My proposed solution is to yoink the object out of the function scope, and into the scope of the rest of the program. To keep the dynamic nature of the values of the object, I used functions:
const translator = {
  &quot;@@login@@&quot;:  (user) =&gt; user.login,
  &quot;@@passwd@@&quot;: (user) =&gt; user.passwd,
  &quot;@@ip@@&quot;:     (_, camera) =&gt; camera.ip,
  &quot;@@stream@@&quot;: (_, camera, type) =&gt; camera.flux[type].stream,
  &quot;@@port@@&quot;:   (_, camera, type) =&gt; camera.flux[type].port,
  &quot;@@flux@@&quot;:   (_, camera, type, flux) =&gt; camera.flux[type][flux]
};

The way these functions are set up, allows you to just call them with predefined arguments (makeCameraUrl(user, camera, type, flux)) and always get the correct value back. The underscore (_) in some of these functions is a colloquial way of saying &quot;I'm not interested in this parameter.&quot; It's quite universal between languages.
Lastly, I took the liberty to change your code a bit to make it run so your makeCameraUrl function does what I think you meant it should do:
const makeCameraUrl = (user, camera, type, flux) =&gt;
  Object.entries(translator)
    .reduce(
      (url, [key, value]) =&gt; url.replace(key, value(user, camera, type, flux)),
      camera.url);

We loop over all entries of the translator object, which yields an array like this:
[
  [&quot;@@login@@&quot;,  () =&gt; /*...*/]
  [&quot;@@passwd@@&quot;, () =&gt; /*...*/]
  [&quot;@@ip@@&quot;,     () =&gt; /*...*/]
  [&quot;@@stream@@&quot;, () =&gt; /*...*/]
  [&quot;@@port@@&quot;,   () =&gt; /*...*/]
  [&quot;@@flux@@&quot;,   () =&gt; /*...*/]
]

An entry is a two-element array with its first element being the key, and the second being its value.
We then reduce over these entries, starting with a default value of the camera's URL. I'm going to write out the lengthy version of what this reduce does:
const makeCameraUrl = (user, camera, type, flux) =&gt;
  Object.entries(translator)
    .reduce(
      (url, [key, value]) =&gt; url.replace(key, value(user, camera, type, flux)),
      camera.url);

// Is the same as the following:

const makeCameraUrl = (user, camera, type, flux) =&gt;
  camera.url
    .replace(&quot;@@login@@&quot;,  translator[&quot;@@login@@&quot;](user, camera, type, flux))
    .replace(&quot;@@passwd@@&quot;, translator[&quot;@@passwd@@&quot;](user, camera, type, flux))
    .replace(&quot;@@ip@@&quot;,     translator[&quot;@@ip@@&quot;](user, camera, type, flux))
    .replace(&quot;@@stream@@&quot;, translator[&quot;@@stream@@&quot;](user, camera, type, flux))
    .replace(&quot;@@port@@&quot;,   translator[&quot;@@port@@&quot;](user, camera, type, flux))
    .replace(&quot;@@flux@@&quot;,   translator[&quot;@@flux@@&quot;](user, camera, type, flux));

// Is the same as the following:

const makeCameraUrl = (user, camera, type, flux) =&gt;
  camera.url
    .replace(&quot;@@login@@&quot;,  user.login)
    .replace(&quot;@@passwd@@&quot;, user.passwd)
    .replace(&quot;@@ip@@&quot;,     camera.ip)
    .replace(&quot;@@stream@@&quot;, camera.flux[type].stream)
    .replace(&quot;@@port@@&quot;,   camera.flux[type].port)
    .replace(&quot;@@flux@@&quot;,   camera.flux[type][flux]);

Quite elegant, if this was your intention.



 The end
Hopefully you've learned some things form this answer (if not, sorry for the long post ). It's a good start, but keep my tips in mind and you'll be a JS guru in no time :)