# Serving my own catalog via Node-RED

**URL:** <https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040>\
**Category:** General\
**Created:** [15 February 2021 17:08 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040 "2021-02-15T17:08:44Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [15 February 2021 17:08 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/1 "2021-02-15T17:08:44Z")

</div>

Hello,  
I'm working on my own Docker image and would like to have a custom node catalog available. Ideally I'd like to host that catalog myself from within Node-RED.  
Is there some way I could access `RED.httpAdmin` from within the `settings.js` file ? Or perhaps I need to create a configuration node for this that would be invisible but open the endpoint there ?

Thanks !

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [15 February 2021 17:15 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/2 "2021-02-15T17:15:45Z")

</div>

The only option via the settings file would be `httpStatic` to expose a directory via the http server - and then ensure the catalog is the only thing in there.

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [15 February 2021 17:28 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/3 "2021-02-15T17:28:34Z")

</div>

And if not via settings.js, could a configuration node work even if it’s not deployed in the flows ?

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [15 February 2021 17:32 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/4 "2021-02-15T17:32:44Z")

</div>

Nothing requires you to call `RED.nodes.registerType` if you have nothing to register.

So you could create a module that contains something that looks like a node in that it has the .js and .html files - but leave the .html largely blank and the .js do whatever you need.

In 1.3 the new plugins feature arrives and would be a semantically better way of doing it.

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [15 February 2021 17:33 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/5 "2021-02-15T17:33:51Z")

</div>

Nice thanks ! I’ll do that for now and migrate to a plugin when it’s ready !

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [15 February 2021 18:35 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/6 "2021-02-15T18:35:31Z")

</div>

I think @hardillb had a version with a catalogue in its own container as part of his multi-tenent how-to series of blog posts

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [15 February 2021 18:42 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/7 "2021-02-15T18:42:40Z")

</div>

I'll have a look !  
My use case is a bit specific as the packages are hosted on Azure Devops and I'd rather have it in the main container but I'll check his blog post for inspiration !

---

<div class="post-metadata">

**Author:** ![hardillb](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hardillb/32/12373_2.png) [@hardillb](https://discourse.nodered.org/u/hardillb)\
**Post date:** [15 February 2021 19:18 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/8 "2021-02-15T19:18:44Z")

</div>

Yep, I've using Verdaccio private NPM repo, setup to make a call to a web endpoint when ever any of the packages get updated. This triggers the catalogue app to rebuild the catalogue.json

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [15 February 2021 22:43 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/9 "2021-02-15T22:43:15Z")

</div>

Ended up creating an 'invisible' node as @knolleary suggested with the following:

```auto
const axios = require('axios')

module.exports = function (RED) {
  RED.httpAdmin.get('/catalog', async (req, res, next) => {
    const options = {
      auth: {
        username: 'user@npm',
        password: process.env.NPM_REGISTRY_TOKEN
      }
    }
    const response = await axios.get('https://feeds.dev.azure.com/orgname/_apis/packaging/Feeds/orgname/packages?api-version=6.0-preview.1&includeDescription=true', options)
    const azureNodes = response.data.value.filter(pkg => pkg.name.includes('node-red-contrib'))
    const catalog = {
      name: 'Node-RED Catalog',
      updated_at: Date.now(),
      modules: azureNodes.map(pkg => {
        return {
          id: pkg.normalizedName,
          version: pkg.versions[0].normalizedVersion,
          description: pkg.versions[0].packageDescription,
          updated_at: pkg.versions[0].publishDate
        }
      })
    }
    res.end(JSON.stringify(catalog, '', 2))
  })
}

Works like a charm ! Thanks !
```

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [16 February 2021 00:18 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/10 "2021-02-16T00:18:52Z")

</div>

I've encountered a small bump in the road: Is the catalog loaded by the editor or by the runtime ?

---

<div class="post-metadata">

**Author:** ![shrickus](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/shrickus/32/517_2.png) [@shrickus](https://discourse.nodered.org/u/shrickus)\
**Post date:** [16 February 2021 18:27 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/11 "2021-02-16T18:27:53Z")

</div>

FWIW, I found a way to serve my own "curated" list of nodes _from within my own node-red instance_ -- Here is the flow I'm using:

```auto
[{"id":"829c111f.31f9c","type":"http in","z":"636b4cd7.6c8974","name":"","url":"/catalog.json","method":"get","upload":false,"swaggerDoc":"","x":180,"y":500,"wires":[["84e9294b.c06cd8"]]},{"id":"84e9294b.c06cd8","type":"http request","z":"636b4cd7.6c8974","name":"https://catalogue.nodered.org/catalogue.json","method":"GET","ret":"obj","url":"https://catalogue.nodered.org/catalogue.json","tls":"","x":510,"y":480,"wires":[["69bb335a.fdd94c"]]},{"id":"69bb335a.fdd94c","type":"change","z":"636b4cd7.6c8974","name":"curated node filter","rules":[{"t":"set","p":"payload.name","pt":"msg","to":"zPaper Nodes Catalog","tot":"str"},{"t":"set","p":"payload.modules","pt":"msg","to":"(\t $contribs := [\t \"node-red-contrib-aftership\",\t \"node-red-contrib-aws\",\t \"node-red-contrib-github\",\t \"node-red-contrib-google\",\t \"node-red-contrib-jira\",\t \"node-red-contrib-moment\",\t \"node-red-contrib-npm\",\t \"node-red-contrib-pdf\",\t \"node-red-contrib-redis-ssl-support\",\t \"node-red-contrib-salesforce\",\t \"node-red-contrib-salesforce-connection-emitter\",\t \"node-red-contrib-salesforce-platform-event\",\t \"node-red-contrib-slack\",\t \"node-red-contrib-xsltransform\",\t \"node-red-contrib-zip\",\t \"node-red-contrib-uibuilder\",\t \"node-red-dashboard\"\t];\t payload.modules[id.$match(/^node-red-node-/) or id in $contribs]\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":830,"y":480,"wires":[["e0bfde23.781e7"]]},{"id":"bd1a45f8.074d88","type":"http response","z":"636b4cd7.6c8974","name":"","statusCode":"","headers":{},"x":1170,"y":500,"wires":[]},{"id":"e0bfde23.781e7","type":"switch","z":"636b4cd7.6c8974","name":"req?","property":"req","propertyType":"msg","rules":[{"t":"null"},{"t":"nnull"}],"checkall":"true","repair":false,"outputs":2,"x":1030,"y":480,"wires":[["c5fce441.cc9598"],["bd1a45f8.074d88"]]},{"id":"c5fce441.cc9598","type":"debug","z":"636b4cd7.6c8974","name":"catalog.json","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":1190,"y":460,"wires":[]},{"id":"d508b24d.b2dff","type":"inject","z":"636b4cd7.6c8974","name":"invoke the endpoint","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":190,"y":460,"wires":[["84e9294b.c06cd8"]]},{"id":"d83abe6.a20f84","type":"comment","z":"636b4cd7.6c8974","name":"Generate a curated list of nodes for Palette Manager","info":"","x":230,"y":400,"wires":[]},{"id":"379ea62b.75d2fa","type":"comment","z":"636b4cd7.6c8974","name":"Modify this list as necessary","info":"","x":840,"y":440,"wires":[]},{"id":"1e4c116b.28152f","type":"comment","z":"636b4cd7.6c8974","name":"Retrieve the full list of nodes from the flows library","info":"","x":520,"y":440,"wires":[]}]

```

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/2/c/2c9e838e716b8b9d6e7fda2ba076df17cd50c0b2.png)

It starts with the `/red/catalog.json` http endpoint on the left, fetches the actual node-red catalog, then uses a JSONata expression to filter only the ones I want people to see. Then i added this to the `editorTheme` section of my settings.js file:

```auto
        palette: {
            catalogues: ['http://localhost:1880/red/catalog.json']
        },

```

Of course, anyone can just change the filter to add whatever they want, so if real security is needed for your use case, then this would not be a good solution. But for our purposes, it serves to focus people on those nodes that we would expect them to use.

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [17 February 2021 01:14 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/12 "2021-02-17T01:14:32Z")

</div>

The one thing I wonder is I think the editor calls the catalog url directly, meaning if we put localhost it will only work if Node-RED runs on the same machine, so in theory it would need to be the publicly reachable URL.

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [17 February 2021 09:38 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/13 "2021-02-17T09:38:37Z")

</div>

Does it work if the url is just relative ? IE just `/red/catalog.json` ?

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [17 February 2021 20:18 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/14 "2021-02-17T20:18:43Z")

</div>

Thanks Dave, that worked like a charm ! Feel dumb for not thinking about that first 😛

---

<div class="post-metadata">

**Author:** ![hardillb](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/hardillb/32/12373_2.png) [@hardillb](https://discourse.nodered.org/u/hardillb)\
**Post date:** [19 February 2021 18:12 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/15 "2021-02-19T18:12:57Z")

</div>

The catalogue is loaded by the editor so needs to be accessible by the browser

---

<div class="post-metadata">

**Author:** ![nlecaude](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/nlecaude/32/25_2.png) [@nlecaude](https://discourse.nodered.org/u/nlecaude)\
**Post date:** [19 February 2021 19:06 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/16 "2021-02-19T19:06:38Z")

</div>

Yup my main issue was that I was using the full URL in my settings file but since the catalog is served in the container itself it would only work for localhost. Using a relative URL solved the issue.

---

<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:** [5 March 2021 19:07 UTC](https://discourse.nodered.org/t/serving-my-own-catalog-via-node-red/41040/17 "2021-03-05T19:07:08Z")

</div>

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