# Accessing external libraries

**URL:** <https://discourse.nodered.org/t/accessing-external-libraries/77340>\
**Category:** General\
**Created:** [7 April 2023 13:24 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340 "2023-04-07T13:24:31Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![bwims](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bwims/32/64907_2.png) [@bwims](https://discourse.nodered.org/u/bwims)\
**Post date:** [7 April 2023 13:24 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/1 "2023-04-07T13:24:31Z")

</div>

Hello!

One for the JS programmers...

I can gain access to an external library using the setup tab of the function node and this works well in the case where access is gained either by a "require (module)" or an "import (module)"

Where I get unstuck is in the situation where the declaration is of the form:

`import { parseDomain, ParseResultType } from "parse-domain";`

with the following exports

`export parseDomain(hostname: string | typeof NO_HOSTNAME, options?: ParseDomainOptions): ParseResult`

and

`export ParseResultType`

An object that holds all possible [ParseResult](https://github.com/peerigon/parse-domain#api-ts-ParseResult) `type` values:

`const ParseResultType = { Invalid: "INVALID", Ip: "IP", Reserved: "RESERVED", NotListed: "NOT_LISTED", Listed: "LISTED", };`

Is it possible to access these routines using the Setup tab of the function node?

Thanks!

B.

---

<div class="post-metadata">

**Author:** ![msillano](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/msillano/32/17424_2.png) [@msillano](https://discourse.nodered.org/u/msillano)\
**Post date:** [7 April 2023 15:27 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/2 "2023-04-07T15:27:55Z")

</div>

Developing a project involving many flows, I needed to create function libraries, function arrays, and singleton objects (with data and methods), shared and accessible from each node/flow.

The 'standard' solution (import etc.) doesn't excite me.  
I found a solution that works best for me: it uses singleton objects, and this is a good replacement for shared libraries, having the following advantages, also keeping the distribution issues in mind:

- Easy to implement.
- Easy to debug.
- Visible in node-red 'Context data' debug pad.
- No extra files: all in JSON import/export.
- No config file editing, auto-installation.

An implementation can be seen here: [‘Meteo Utils’ functions library](https://discourse.nodered.org/t/meteo-utils-functions-library-meteoutils-constructor/62977).

In short, place in 'On Start' of some function node, in your flow or in one flow of your project:

```auto
global.set("a_sing_obj", {}); // only data, empty in this example
context.global.a_sing_obj = global.get("a_sing_obj");

```

Then add methods, like:

```auto
context.global.a_sing_obj.one_method= function(....){
                // more code, you can use 'this.' here
                 }

```

done: then you can do calls like:

```auto
context.global.a_sing_obj.one_method(...)

```

in any node and in any flow or subflow.

---

<div class="post-metadata">

**Author:** ![marcus-j-davies](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/marcus-j-davies/32/103435_2.png) [@marcus-j-davies](https://discourse.nodered.org/u/marcus-j-davies)\
**Post date:** [7 April 2023 15:51 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/3 "2023-04-07T15:51:01Z")

</div>

@bwims

Sorry, not sure I follow - have just come back from a loonnngggg walk - so might not be in top gear 😅

BUT...

In the setup tab you just import as-is `parse-domain`  
then you can call any exported method/property/enum, such as `parseDomain`

so, in a `function` node

```auto
const Result = parsedomain.parseDomain("www.some.example.co.uk")
if(Result.type == parsedomain.ParseResultType.Listed){
  //do Something
}

```

Here are the possible values.

```auto
export enum ParseResultType {
  Invalid = "INVALID",
  Ip = "IP",
  Reserved = "RESERVED",
  NotListed = "NOT_LISTED",
  Listed = "LISTED",
}

```

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [7 April 2023 16:16 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/4 "2023-04-07T16:16:46Z")

</div>

> [@bwims](#):
>
> import

Import works only with ES Modules and they cannot be `require`d.

To get an ES Module into a CommonJS module (which is all of Node-RED's modules and libraries), you have to use a dynamic import

```auto
const { parseDomain, ParseResultType } = import("parse-domain")

```

However, dynamic imports are async which means that your javascript has to take that into account.

The whole thing is an absolute pigs-ear and the most ridiculous part of "modern" JavaScript.

To be honest, your best bet is probably to use something like esbuild to turn the ES Module into a CommonJS one - if it can.

---

<div class="post-metadata">

**Author:** ![bwims](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bwims/32/64907_2.png) [@bwims](https://discourse.nodered.org/u/bwims)\
**Post date:** [7 April 2023 16:22 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/5 "2023-04-07T16:22:07Z")

</div>

Thanks - I did try that, but, probably because of the next answer, there is a mismatch. I got this error message:

`"TypeError: Cannot read properties of undefined (reading 'parseDomain')"`

---

<div class="post-metadata">

**Author:** ![bwims](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bwims/32/64907_2.png) [@bwims](https://discourse.nodered.org/u/bwims)\
**Post date:** [7 April 2023 16:24 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/6 "2023-04-07T16:24:02Z")

</div>

This looks hopeful! I presume that goes into settings.js and use "await" when calling the function?

As for converting, I did see this, but couldn't figure how I could use it in Node Red (being a newby!)  
[ESM](https://www.npmjs.com/package/esm)

Thanks!

---

<div class="post-metadata">

**Author:** ![marcus-j-davies](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/marcus-j-davies/32/103435_2.png) [@marcus-j-davies](https://discourse.nodered.org/u/marcus-j-davies)\
**Post date:** [7 April 2023 16:29 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/7 "2023-04-07T16:29:58Z")

</div>

This is where the CommonJS, ES and all that horrific sounding jazz evades me.  
I should really know to be honest, given I use typescript these days 😬

---

<div class="post-metadata">

**Author:** ![bwims](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bwims/32/64907_2.png) [@bwims](https://discourse.nodered.org/u/bwims)\
**Post date:** [7 April 2023 16:30 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/8 "2023-04-07T16:30:38Z")

</div>

> [@TotallyInformation](#):
>
> `const { parseDomain, ParseResultType } = import("parse-domain")`

didn't work in settings.js ...

I tried it in a function and got the following crash, so luck so far!

```auto
7 Apr 17:28:26 - [error] TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.
    at new NodeError (node:internal/errors:372:5)
    at importModuleDynamicallyCallback (node:internal/process/esm_loader:39:9)
    at Function node:11896326bed805e9 [function 11]:2:42
    at Function node:11896326bed805e9 [function 11]:22:3
    at Script.runInContext (node:vm:139:12)
    at processMessage (C:\Users\williams_b\AppData\Roaming\npm\node_modules\node-red\node_modules\@node-red\nodes\core\function\10-function.js:401:33)
    at FunctionNode._inputCallback (C:\Users\williams_b\AppData\Roaming\npm\node_modules\node-red\node_modules\@node-red\nodes\core\function\10-function.js:335:17)
    at C:\Users\williams_b\AppData\Roaming\npm\node_modules\node-red\node_modules\@node-red\runtime\lib\nodes\Node.js:210:26
    at Object.trigger (C:\Users\williams_b\AppData\Roaming\npm\node_modules\node-red\node_modules\@node-red\util\lib\hooks.js:166:13)
    at FunctionNode.Node._emitInput (C:\Users\williams_b\AppData\Roaming\npm\node_modules\node-red\node_modules\@node-red\runtime\lib\nodes\Node.js:202:11)

```

---

<div class="post-metadata">

**Author:** ![bwims](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bwims/32/64907_2.png) [@bwims](https://discourse.nodered.org/u/bwims)\
**Post date:** [7 April 2023 16:31 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/9 "2023-04-07T16:31:36Z")

</div>

I think the routines that I am trying to use are in typescript... at least they have file types as .ts

I went to github to have a look and noticed that.

---

<div class="post-metadata">

**Author:** ![marcus-j-davies](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/marcus-j-davies/32/103435_2.png) [@marcus-j-davies](https://discourse.nodered.org/u/marcus-j-davies)\
**Post date:** [7 April 2023 16:39 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/10 "2023-04-07T16:39:35Z")

</div>

Typescript (.ts) is precompiled source code.  
what you see in Github is the "source code" - later (during publish/release) it is compile to JS files.

The main use for Typescript is the clue in its name **Type**. - it allows (during development) type safety, better error checking, its more for the developer than the end user - the End User will usually only deal with the resulting JS files.

I use Typescript during development, then compile to JS before releasing

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [7 April 2023 19:26 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/11 "2023-04-07T19:26:38Z")

</div>

> [@bwims](#):
>
> As for converting, I did see this, but couldn't figure how I could use it in Node Red (being a newby!)  
> [ESM](https://www.npmjs.com/package/esm)

That's 4 years out of date and has many unanswered issues sadly.

> [@marcus-j-davies](#):
>
> This is where the CommonJS, ES and all that horrific sounding jazz evades me.

Basically, I cannot find any way to realistically migrate from CommonJS (`const x = require('x')`) to ESM in any meaningful way for something as complex as Node-RED. ES Modules can consume CommonJS ones easily enough but the reverse is not true.

I've no idea how Node-RED will be able to transition to ESM and I can't find a sensible way to create an ESM based node or even to incorporate an ESM only module such as the latest `execa` module.

While you can use a dynamic import on an ES Module, it requires you to effectively rewrite much of your node as async from the top down - just to accommodate a single ES Module.

> [@marcus-j-davies](#):
>
> I use Typescript during development, then compile to JS before releasing

And just make sure that you direct the typescript compiler to output CommonJS modules and then you won't have an issue.

---

<div class="post-metadata">

**Author:** ![marcus-j-davies](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/marcus-j-davies/32/103435_2.png) [@marcus-j-davies](https://discourse.nodered.org/u/marcus-j-davies)\
**Post date:** [7 April 2023 19:29 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/12 "2023-04-07T19:29:21Z")

</div>

> [@TotallyInformation](#):
>
> And just make sure that you direct the typescript compiler to output CommonJS modules and then you won't have an issue.

Oh! Of course

Snippet of tsconfig

```auto
{
	"compilerOptions": {
		"module": "commonjs",
		"target": "ES2020",
         ....
    }
}

```

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [7 April 2023 19:33 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/13 "2023-04-07T19:33:58Z")

</div>

> [@marcus-j-davies](#):
>
> `"target": "ES2020",`

For reference, ES2020 maps roughly onto node.js v14 LTS. ES2021 maps roughly onto Node 16LTS.

---

<div class="post-metadata">

**Author:** ![marcus-j-davies](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/marcus-j-davies/32/103435_2.png) [@marcus-j-davies](https://discourse.nodered.org/u/marcus-j-davies)\
**Post date:** [7 April 2023 19:36 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/14 "2023-04-07T19:36:58Z")

</div>

Yup!

The core driver i use (I'm sure you know - ZWave JS) its min version is Node 14, hence why I target.. that target 🤪, to allow greater compatibility

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [7 April 2023 19:40 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/15 "2023-04-07T19:40:37Z")

</div>

Same here. But not for much longer because I think v14 LTS is near end of life. The next major version of uibuilder (some way off as I'm busy on smaller updates at present) will target v16 as one of its potentially breaking changes for example.

The next major release of Node-RED will also likely target v16. It too currently targets v14.

---

<div class="post-metadata">

**Author:** ![bwims](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/bwims/32/64907_2.png) [@bwims](https://discourse.nodered.org/u/bwims)\
**Post date:** [7 April 2023 20:23 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/16 "2023-04-07T20:23:01Z")

</div>

Thanks for all the help folks! It's been an education! At least I didn't miss anything simple!

B.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [6 June 2023 20:23 UTC](https://discourse.nodered.org/t/accessing-external-libraries/77340/17 "2023-06-06T20:23:13Z")

</div>

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