Search code examples
node.jswindowswindows-servicesdotenvnode-windows

How to pass the dotenv config path through a Windows service created with node-windows


Windows Server 2008 R2 Enterprise

Node version 12.13.1

node-windows version 1.0.0-beta.5

When I call my node application, instead of using require('dotenv') in code to load the environment variables (e.g. from a default .env file), I need to pass the path to a specific environment file. This environemnt file is different depending for which customer the application is started for (e.g. different database, paths, customer code, etc..).

In the cli, I can succesfully do it this way:

node --require dotenv/config bin/www dotenv_config_path=C:\projects\abc\env\XYZ.env

As you can see I use an absolute path for the location of the env file, but it also works with a relative path. I'm just trying to eliminate this as a reason why I can't make this work with node-windows.

I'm trying to use node-windows to create a Windows service wrapper that calls my node application and also loads a specific env file like the above code does. Can't make it work so far, after creating the Windows service, it quits after a moment, which tells me it's missing the environment variables it needs to function. Which means it can't load of find the environment file.

Here is my script to create the Windows service using node-windows:

#!/usr/bin/env node

// Usage:
// npm run install-win XYZ

// Notes:
// 1. Before creating the windows service, make sure to delete any previous files in the /bin folder (i.e. all files abcXYZ.*)
// 2. After creating the windows service, change the Log On account to the ******* user to avoid persmission issues when using paths on other production servers

const args = process.argv;
const codeclient = args[2];

const serviceName = `abc${codeclient}`;
const environmentPath = `C:\\projects\\abc\\abc-api\\env\\${codeclient}.env`; // Make sure to use the full absolute path here

const Service = require('node-windows').Service;

// Create a new service object
const svc = new Service({
    name: serviceName,
    description: serviceName,
    script: require('path').join(__dirname, 'www'),
    scriptOptions: `dotenv_config_path=${environmentPath}`,
    nodeOptions: [
        '--require=dotenv/config',
        '--harmony',
        '--max_old_space_size=4096'
    ]/*,
    env: {
        name: 'DOTENV_CONFIG_PATH',
        value: environmentPath
    }*/
});

// Listen for the "install" event, which indicates the
// process is available as a service.
svc.on('install', function(){
    svc.start();
});

svc.install();

I've tried both the "scriptOptions" approach and the "env" approach in various configurations, but nothing works.

If anyone has managed to make something like this work before, I'd very much like to know how you did it.


Solution

  • So the way I ended up doing this is instead just pass my codeclient variable through the scriptOptions of node-windows, and then using that in my node application to have dotenv load a specific env file. It's more simple really.

    The only issue I had with the approach is that node-windows would fail with my numerical codeclient, always assuming it's a number type instead of a string (node-windows tries to call String.split() on it later). I had to append the underscore in front to force it as a string.

    Script to create the Windows service with node-windows:

    #!/usr/bin/env node
    
    // Usage:
    // npm run install-win 123
    
    // Notes:
    // 1. Before creating the windows service, make sure to delete any previous files in the /bin folder (i.e. all files abc123.*)
    // 2. After creating the windows service, change the Log On account of the service to the ******** user to avoid persmission issues when using paths on other production servers
    
    const args = process.argv;
    const codeclient = args[2];
    
    const serviceName = `abc${codeclient}`;
    
    const Service = require('node-windows').Service;
    
    // Create a new service object
    const svc = new Service({
        name: serviceName,
        description: serviceName,
        script: require('path').join(__dirname, 'www'),
        scriptOptions: `_${codeclient}`,
        nodeOptions: [
            '--harmony',
            '--max_old_space_size=4096'
        ]
    });
    
    // Listen for the "install" event, which indicates the
    // process is available as a service.
    svc.on('install', function(){
        svc.start();
    });
    
    svc.install();
    

    Loading the env file in the node application:

    // Load environment variables into process.env
    if (process.argv[2]) {
        // Load a specific env file for the codeclient passed as argument
        const codeclient = process.argv[2].replace('_', '');
        require('dotenv').config({ path: `./env/${codeclient}.env` });
    }
    else {
        // Load the default .env file
        require('dotenv').config();
    }