How to create and publish an npm module
Tags: javascript, react, node, beginners
Published: 2020-09-08 06:24:22

### Introduction

In this tutorial, you will create your own npm package and publish it to the npm repository.

By doing this, you will understand:

1. How to create an npm package
2. How to install it locally before publishing to test its functionality
3. How to install and use the published package using ES6 import syntax or using Node.js require statement
4. How to manage semantic versioning of the package
5. How to update the package with the new version and publish it again

_To be precise, you will build a package that will return a list of GitHub repositories of the specified username sorted by the number of stars for each repository._

## Prerequisites

You will need the following to complete this tutorial:

-  A valid installation of Git version control
-  Node.js installed locally, which you can do by following the instructions given on [this page](https://nodejs.org/en/download/)

This tutorial was verified with Node v13.14.0, npm v6.14.4 and axios v0.20.0

## Step 1 — Initial Setup

Create a new folder with the name `github-repos-search` and initialize a `package.json` file

```js
mkdir github-repos-search
cd github-repos-search
npm init -y
```

Initialize the current project as a git repository by running the following command from `github-repos-search` folder:

```js
git init .
```

Create a `.gitignore` file to exclude the `node_modules` folder. Add the following contents inside `.gitignore` file

```js
node_modules
```

Install the `axios` package that you will use to make a call to the GitHub API.

```js
npm install axios@0.20.0
```

Your `package.json` will look like this now:

```js
{
  "name": "github-repos-search",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "axios": "^0.20.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
```

> Inside the `package.json` file, the value for name is `github-repos-search`. So our package name after publishing to npm repository will become `github-repos-search`. Also, the name has to be unique on the npm repository so first check if such npm repository already exists or not by navigating to `https://www.npmjs.com/package/<your_repository_name_from_package_json>`. Otherwise  you will get an error while publishing the package to the npm repository if the name already exists.

## Step 2 — Writing the code

Create a new file with the name `index.js` and add the following contents inside it:

```js
const axios = require('axios');
const getRepos = async ({
  username = 'myogeshchavan97',
  page = 1,
  per_page = 30
} = {}) => {
  try {
    const repos = await axios.get(
      `https://api.github.com/users/${username}/repos?page=${page}&per_page=${per_page}&sort=updated`
    );
    return repos.data
      .map((repo) => {
        return {
          name: repo.name,
          url: repo.html_url,
          description: repo.description,
          stars: repo.stargazers_count
        };
      })
      .sort((first, second) => second.stars - first.stars);
  } catch (error) {
    return [];
  }
};

getRepos().then((repositories) => console.log(repositories));
```

Let's understand the code first.


* You have created a `getRepos` function that accepts an optional object with `username`, `page` and `per_page` properties.
* Then you used object destructuring syntax for getting those properties out of the object.
* Passing an object to the function is optional so we have initialized it to default values if the object is not passed to the function like this:

```js
{
  username = 'myogeshchavan97',
  page = 1,
  per_page = 30
} = {}
```

* The reason for assigning an empty object `{}` is to not get an error while destructuring `username` from the object if the object is not passed. Check out my [previous article](https://medium.com/javascript-in-plain-english/what-is-so-special-about-es6-destructuring-6be45fbec52c?source=friends_link&sk=6b8ace6e0db36ec4f5b525ef19c62a41) to learn about destructuring in detail.
* Then inside the function, you are making a call to the GitHub API by passing the required parameters to get the repositories of the specified user sorted by the updated date.

```js
const repos = await axios.get(
      `https://api.github.com/users/${username}/repos?page=${page}&per_page=${per_page}&sort=updated`
    ); 
```
* Here, you are using async/await syntax so the `getRepos` function is declared as async.
* Then you are selecting only the required fields from the response using the Array `map` method 

```js
repos.data
  .map((repo) => {
    return {
      name: repo.name,
      url: repo.html_url,
      description: repo.description,
      stars: repo.stargazers_count
    };
  })
```

Then that result is sorted by descending order of stars so the first element in the list will be with the highest stars

```js
.sort((first, second) => second.stars - first.stars);
```

* If there is any error, you are returning an empty array in the catch block.
* As the `getRepos` function is declared as `async`, you will get back a promise so you are using `.then` handler to get the result of the `getRepos` function call and printing to the console.

```js
getRepos().then((repositories) => console.log(repositories));
```

## Step 3 — Executing the code

Now, run the index.js file by executing the following command from the command line:

```js
node index.js
```

You will see the following output with the first 30 repositories:

![output of node index.js](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_1.png)

In the file, you have not provided the username so by default my repositories are displayed.
Let's change that to the following code:

```js
getRepos({
  username: 'gaearon'
}).then((repositories) => console.log(repositories));
```

Run the file again by executing `node index.js` command and you will see the following output:

![output of node index.js](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_2.png)

You can choose to pass the `page` and `per_page` properties to change the response to get the first 50 repositories.

```js
getRepos({
  username: 'gaearon',
  page: 1,
  per_page: 50
}).then((repositories) => console.log(repositories));
```

Now, you know that the functionality is working. Let's export this module so you can call this `getRepos` method from any other file.

So remove the below code from the file

```js
getRepos({
  username: 'gaearon',
  page: 1,
  per_page: 50
}).then((repositories) => console.log(repositories));
```

and add the below line instead

```js
module.exports = { getRepos };
```

Here, you are exporting the `getRepos` function as a property of the object so later if you want to export any other function you can easily add it to the object.

So the above line is the same as

```js
module.exports = { getRepos: getRepos };
```

## Step 4 — Testing the created npm package using require statement

Now, you are done with creating the npm package but before publishing it to the npm repository, you need to make sure it works when you use it using `require` or `import` statement.

There is an easy way to check that. Execute the following command from the command line from inside the `github-repos-search` folder:

```js
npm link
```

![output of npm link](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_3.png)

Executing `npm link` command creates a symbolic link for your current package inside the global npm `node_modules` folder (The same folder where our global npm dependencies get installed)

So now you can use your created npm package inside any project.

Now, create a new folder on your desktop with any name for example `test-repos-library-node` and initialize a `package.json` file so you can confirm that the package is installed correctly:

```js
cd ~/Desktop
mkdir test-repos-library-node
cd test-repos-library-node
npm init -y
```

If you remember, the name property in our package’s `package.json` file was `github-repos-search` so you need to require the package using the same name.

Now, execute the following command from inside the `test-repos-library-node` folder to use the package you created:


```js
npm link github-repos-search
```

![output of npm link github-repos-search](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_4.png)

Create a new file with the name `index.js` and add the following code inside it:

```js
const { getRepos } = require('github-repos-search');

getRepos().then((repositories) => console.log(repositories));
```

Here, you have imported the package directly from the `node_modules` folder( This was only possible because you linked it using npm link)

Now, run the file by executing it from the command line:

```js
node index.js
```

You will see the correct output displayed:

![output of node index.js](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_5.png)

> This proves that when you publish the npm package on the npm repository, anyone can use it by installing it and using the require statement.

## Step 5 — Testing the created npm package using the import statement

You have verified that the package works by using the require statement. Let's verify it by using the ES6 import statement.

Create a new React project by executing the following command from your desktop folder:

```js
cd ~/Desktop
npx create-react-app test-repos-library-react
```

Now, execute the following command from inside the `test-repos-library-react` folder to use the package you created:

```js
npm link github-repos-search
```

Now, open `src/App.s` file and replace it with the following content:

```js
import { getRepos } from 'github-repos-search';
import React from 'react';
import './App.css';

function App() {
  getRepos().then((repositories) => console.log(repositories));

  return (
    <div className="App">
      <h2>Open browser console to see the output.</h2>
    </div>
  );
}
export default App;
```

Start the React app by executing the following command from the terminal:

```js
yarn start
```

If you check the browser console, you will see the output as expected:

![output of yarn start](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_6.png)

> This proves that when you publish the npm package on npm repository, anyone can use it by installing it and using import statement.

## Step 6 — Publish to the npm repository

Now, you have verified that the package is working fine. 
It’s time to publish it to the npm repository.

Switch back to the `github-repos-search` project folder where you have created the npm package.

Let’s add some metadata in the `package.json` file to display some more information about the package

Here is the final `package.json` file:

```js
{
  "name": "github-repos-search",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "homepage": "https://github.com/myogeshchavan97/github-repos-search",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/myogeshchavan97/github-repos-search.git"
  },
  "dependencies": {
    "axios": "^0.20.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "github",
    "repos",
    "repositories",
    "sort",
    "stars"
  ],
  "author": "Yogesh Chavan <myogeshchavan97@gmail.com>",
  "license": "ISC"
}
```

You have added `homepage`, `repository`, `keywords` and `author` for more information(These are optional). Make changes as per your GitHub repository.

Create a new GitHub repository [HERE](https://github.com/new) and push `github-repos-search`  repository to GitHub.
Navigate to https://www.npmjs.com/ and create a new account If you don’t already have an account.

Open terminal and from inside the `github-repos-search` folder execute the following command:

```js
npm login
```

and enter your npm credentials to login.

![output of npm login](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_7.png)

Now, to publish it to the npm repository run the following command:

```js
npm publish
```

![output of npm publish](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/725540d2815e59065bd5d3d728d60f44c9b2d93c/npm_8.png)

If you navigate to https://www.npmjs.com/package/github-repos-search in browser, you will see your published package:

![published npm package](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/6a73241a4b3423740fd75e07c6363d42042e57b5/npm_9.png)

Now, let’s add a `readme.md` file for displaying some information regarding the package.

Create a new file with name `readme.md` inside the `github-repos-search` folder with the contents from [here](https://raw.githubusercontent.com/myogeshchavan97/github-repos-search/master/readme.md)


Let's try to publish it again using the npm publish command.

![error while re-publishing changes](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/6a73241a4b3423740fd75e07c6363d42042e57b5/npm_10.png)

You will get an above error. This is because you are publishing the module with the same version again.
 
If you check our `package.json` file, you will see that, the version mentioned in the file is `1.0.0`You need to increment it every time publishing a new change. So what should you increment to? For that, you need to understand the semantic versioning concept.

## Step 7 — Semantic versioning in npm

The version value is a combination of 3 digits separated by `dot` operator. Let’s say the version is `a.b.c`
1. First value (`a` in `a.b.c`) specifies the major version of the package — It means this version has Major code changes and it might contain breaking API changes.
2. Second value (`b` in `a.b.c`) specifies the minor version which contains minor changes but will not contain breaking API changes.
3. Third value (`c` in `a.b.c`) specifies the patch version which usually contains bug fixes.

In our case, you just added a `readme.md` file which is not an API change so you can increment the patch version which is the last digit by 1.

So change the version inside `package.json` file from `1.0.0` to `1.0.1` and run the `npm publish` command again.

![output of npm publish with next version](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/6a73241a4b3423740fd75e07c6363d42042e57b5/npm_11.png)

If you check the npm package now, you will see the updated npm package live [here](https://www.npmjs.com/package/github-repos-search)

![published npm package page](https://gist.github.com/myogeshchavan97/e0be7fc4c838544e2d00afeb3a82ae10/raw/6a73241a4b3423740fd75e07c6363d42042e57b5/npm__.png)

To learn in detail about `semantic versioning` check out my [previous article](https://medium.com/javascript-in-plain-english/semantic-versioning-in-npm-54d5b02c3536?source=friends_link&sk=e1c56eee8295d198ad986a8c3849d16a)

## Conclusion

In this tutorial, you created a npm package and published it to the npm repository.

For the complete source code of this tutorial, check out the [github-repos-search](https://github.com/myogeshchavan97/github-repos-search) repository on GitHub. You can also see the published npm module [here](https://www.npmjs.com/package/github-repos-search)

**Don't forget to subscribe to get my weekly newsletter with amazing tips, tricks and articles directly in your inbox [here](https://yogeshchavan.dev).**

[<img src="https://cdn.buymeacoffee.com/buttons/default-yellow.png" >](https://www.buymeacoffee.com/myogeshchavan97)