How to build and publish a node module to private NPM registry on Azure Artifacts

Azure Artifacts + NPM = Perfect Match

NPM is a package manager for the JavaScript programming language. It is the default package manager for the JavaScript runtime environment Node.js. Similar to some other languages, via NPM, Node.js developers can upload and mange their node modules flexibly. NPM offers public registries where developers can freely upload their packages and allows others to consume. NPM also offers private registry so that packages upload there is for private usage.

Azure Artifacts – part of Azure DevOps – introduces the concept of multiple feeds that you can use to organize and control access to your packages. If you’re familiar with using packages from NuGet.org or npmjs, you can think of those places each as a single feed.

Recently, when I was helping one of my clients to set up the development standard, the development team raised the question of using NPM to modularise their node component so different node projects can consume the same set of functionalities. Since their DevOps is entirely set up on Azure DevOps, I had decided to use Azure Artifacts to manage the NPM feed.

I will show you guys how to publish an NPM package to Azure Artifacts NPM private feed. We will use Typescript to build our package.

All the work we do today can be found on my GitHub: https://github.com/PatrickZhao1989/npm-package.git


Prerequisite

You have Node.js and Typescript installed in your system. You have basic understanding about Node.js and Typescript I assume you are using windows system and the commands we run are in PowerShell. You can easily find out the equivalent in other OS and command prompt.


Initialize a project

First of all, we will initialize a project. Create a new folder in your file system, then launch Windows PowerShell and cd to that directory.

Run npm init and follow through the template giving your project a name, version number..etc. Once completed, a package.json file will be generated under your directory.

Next we are going to add Typescript to our project. Under the same directory where your package.json file is, run tsc --init and it will generate a tsconfig.json file in the directory.

Configuration

Since we are going to build the module in Typescript and we want our package consumers to benefit from it, we need to configure Typescript Typescript Transpiler to do it for us. In tsconfig.json, Add "declaration": true and so that it will generate corresponding ‘.d.ts’ file for us during the build. We do also want to define a "outDir" and enable "esModuleInterop". Your tsconfig.json file should look like below.

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "declaration": true, /* Generates corresponding '.d.ts' file. */
        "outDir": "./dist",
        "strict": true,
        "esModuleInterop": true 
/* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    }
}

In our NPM package.json definition, we need to specify "main" and "types" entries. We also want to specify the build command to use tsc command so that Typescript will transpile for us. See below:

{
	"name": "sample",
	"version": "1.0.9",
	"description": "This is the sample npm module",
	"main": "dist/index.js",
	"types": "dist/index.d.ts",
	"scripts": {
		"start": "node dist/index.js",
		"build": "tsc",
		"test": "echo \"Error: no test specified\" && exit 1"
	},
	"repository": {
		"type": "git",
		"url": "git+https://github.com/PatrickZhao1989/npm-package.git"
	},
	"keywords": [
		"node",
		"npm",
		"private",
		"registry"
	],
	"author": "Patrick Zhao",
	"license": "MIT",
	"bugs": {
		"url": "https://github.com/PatrickZhao1989/npm-package/issues"
	},
	"homepage": "https://github.com/PatrickZhao1989/npm-package#readme",
	"dependencies": {
		"@types/node": "^13.1.6"
	}
}

Build our package

Now is the exciting part – building our package. You can build your own modules and organize in the way you need to while as an instruction, we are going to build something super simple. Add an index.ts file in the directory and add following functions to it.

export function helloWorld1(input:string): void{
	console.log(`HelloWorld1 from ${input}`)
}

export function helloWorld2(input:string): void{
	console.log(`HelloWorld2 from ${input}`)
}

export function helloWorld3(input:string): void{
	console.log(`HelloWorld3 from ${input}`)
}

Building the code

Now we can run npm run build to build our module. It will generate 2 files under the dist folder. (NPM-PACKAGE is the root folder name – not to be confused 🙂

The index.js contains the transpiled vanilla JavaScript code while the index.d.ts file contains the typescript information. This way, the consumer can leverage typescript benefit such as static type checking etc.

Setting up Azure Artifacts feed

Now we have built our node module. The next step is of course to publish it. As I mentioned earlier, we are going to publish to Azure Artifact private feed. I assume you have created a project on Azure DevOps. Then we can go to the Artifacts menu and create a new feed.

After creating the feed, we can connect to it. Select npm and Azure DevOps will show us the instructions.

What we need to do is that we need to firstly authenticate with Azure private feed by running the command below.

npm install -g vsts-npm-auth --registry https://registry.npmjs.com --always-auth false

Then all we need to do is following the instructions to set up an .npmrc file in our project directory (next to package.json) which stores the link to the registry and then authenticate with the link.

Publish our package

We have set up our feed on Azure Artifact. Next we are going to publish our package. If we have put our working directory into any source control tool such as Git, we need to commit our changes first. Then we can run npm publish to publish our package on to Azure Artifact. Once the publish command completes, we can then see the package together with its dependencies on the feed.

Update published package version

After we have made any changes to our package, we need to update our package version before we publish it. Run command below to update our package.

npm version <update_type>

update_type can be patch, major, or minor. See here for further reference.

Consume the package

Consuming the package is easy 🙂 We can initialize a project using npm init and connect to the feed in the same way as we did before using .npmrc file.

Next, we can add our package name to the dependencies. Our package is named “sample” so after adding it to dependencies, our consumer program’s package.json file looks like this:

{
  "name": "test-module",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "sample": "^1.0.9"
  }
}

If there is any update in the “sample” package, we can run npm update to update our dependency.

In our consumer app (index.ts), we can use the module in this way:

import {helloWorld1, helloWorld2, helloWorld3} from 'sample'


helloWorld1('Patrick');
helloWorld2('Patrick');
helloWorld3('Patrick');

Executing it will give us the result:

Summary

Build and publishing your own Node.js modules is not a hard thing at all. NPM package management is also the foundation of node applications. In my next few posts, I will discuss more about Node.js applications.