Best pattern for creating a global JS function in Node

Hi all,

I am using Azure functions for an API and have an issue where I need to pass down my custom built logger function as well as the azure context object into other functions.

This could be a function 1,2,3,4,5 levels deep. For example:

- root level azure function (context)
- Initialize the logger at root level

  ... for other functions, I must pass in the context and logger ...

  - other function (context, logger)
     - child of other function (context, logger)

I’m wondering if there is a way to initialize the logger and the context globally. On the front end we have things like the window object where can attach global vars … But what can I do in NodeJS?

I’ve read about the global keyword as well as process… but both these approaches seem sketchy … Is this best practice or are there alternatives? Perhaps a Singleton design pattern?

Thanks in advance.

Just

function createMaiLogger = (config) => {
  // dostuff
  return { error, info, log, warn };
}

export const maiLogger = createMaiLogger(someConfig);

Then just use it?

import { maiLogger } from "./logger";

someStuff(() => { 
    const result = ofDoingSomething();
    maiLogger.log(result);
  });

Bit difficult to tell what exactly the issue is without more actual code though.

globalThis would be the now-standard way to refer to the global context; not normally that many situations where you want to attach to it (as with window)

This just ensures there’s just a single instance of object, it doesn’t magically make it global (it’s just very often used for a global), you would do that. There tends to not be too much of a need for this in JS though, it doesn’t gain you much. But if you initialise your logger early on anyway, surely you just import it everywhere you want it? Normally doesn’t need much more than that: regardless of if it’s a closure or a plain object or an object created as a class, just init early on then use it wherever is needed

Hey thanks for the reply.

Basically, I can’t import/export the context or the logger because they both depend on the azure function itself (eg) the context comes from the azure function and the logger is initialised with config inside the context.

That’s why I was thinking I need some sort of global solution like so:


global.globalData = {}

export default function azureFunction (context) {
  const logger = initLogger(context)
  globalData = { context, logger }
}

I thought a singleton with the globalThis would remove the problems of any duplication that might arise. Anyway, it is difficult to explain in more depth as I can’t share more code.

Basically I just want to access context and the logger without having to prop drill into multiple functions…

If you define all the functions inside the scope in which logger is defined, then you won’t have to pass it around.

export default function azureFunction (context) {
  const logger = initLogger(context)

  function someHelper() {
    // I can access logger!
  }

  function someOtherHelper() {
    // I can access logger!
  }
  
  function yetAnotherHelper() {
    // I can access logger!
  }
}

(Not really an answer to your question, but I think this is more understandable than throwing stuff onto globalThis!)

Hi Colin,

Yes that makes sense. But there are cases where we have functions 4,5,6 levels deep that are using the logger and they have the logger/context as a function param. Some of those functions are exported from within other files as well…

So yeah the complexity of some functions is high, and by passing the logger down and down as a param is the current way of doing things…

I feel like there is a better way (hence why I thought maybe globals?)

Anyway, thanks for your help

Yeah, I don’t know if there’s a great solution here…

Are you running any automated testing on these modules? Assuming you can mock the logger whether it’s a function parameter or a global object, I guess it doesn’t matter which way you choose.

I’m curious to see what you decide, though! Keep us posted.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.