# The anatomy of a package.json

Hello good people! In this article, I want to deep dive with you into a usually overlooked file in a project. The `package.json` is crucial to a lot of frontend/backend NodeJS projects. Yet a lot of developers don't really know what it can really do.

We'll first define what's a `package.json`. Next, we'll generate our own file and understand how it works basically. Then, we'll manage a dependency and understand how npm manages them. Just after, we'll see how we can use our `package.json` to customize how our project runs. Finally, we'll touch on life cycle scripts in npm.

Before we begin, I've added a commit hash that links to a [demo repository](https://github.com/tarkant/package-json-guide) here to help you understand what's happening.

## What's a `package.json` anyway? 🤔

The `package.json` file is what defines a *Node Package Manager* (npm) package with a set of *[metadata](https://en.wikipedia.org/wiki/Metadata)*. It helps to define the package name, version, author, license, and other useful information like the list of dependencies it relies on and other exotic data like test scripts, build scripts, and much more.

If you've forked a project from GitHub or created an app using some CLI tool like Create React app, NextJS, or Angular-CLI, you might have noticed that it generates a new `package.json` with a lot of metadata. In the next section, we're going to generate our own `package.json`.

## Initialize a `package.json` from scratch ✨ [#a1eb599](https://github.com/tarkant/package-json-guide/commit/a1eb5993c2565447195d23833ec22b28f056b6fb)

Let's go through the steps together so you can get a grasp of how it works.
Fire up your favorite command-line tool or terminal and create a folder:

```bash
mkdir my-test-app
cd my-test-app
```

Next, input the following command:

```bash
npm init
```

You'll be greeted with some text from npm to walk you through the initialization of your `package.json`, in your case, for example, the package name will default to the folder name *"my-test-app"* of course, you can change that to anything you like. If you want to go with the default values, just press enter at each step. After prompting you about the license type, npm will print on the terminal your `package.json` for confirmation. If everything looks right, just type yes

![npm-init.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1619636037134/WrXBpa0nH.gif)

Now that it's done, let's check out our newly generated file:

```JSON
// package.json
{
  "name": "my-test-app",
  "version": "1.0.0",
  "description": "My awesome new app!",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/tarkant/package-json-guide.git"
  },
  "keywords": [
    "npm",
    "nodejs",
    "package.json",
    "tutorial"
  ],
  "author": "Tarek Jellali",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/tarkant/package-json-guide/issues"
  },
  "homepage": "https://github.com/tarkant/package-json-guide#readme"
}
```

**Note ℹ:** In this example I'm initializing the npm package in an empty GitHub repository, npm detects this and populates the *repository*, *bugs*, and *homepage* fields with relevant links.

**Protip 💡:** You can skip all the interactive prompts with `npm init -y` to initialize your project with defaults that you can customize later.

## Installing dependencies 🌳 [#d1777da](https://github.com/tarkant/package-json-guide/commit/d1777daa294150aea607cbf64bde09111105b200)

Now that our `package.json` is initialized, let's see how to install and manage dependencies. To keep things simple, we're just going to create a hello world HTTP server. [Although you can make it without any dependency](https://nodejs.org/en/knowledge/HTTP/servers/how-to-create-a-HTTP-server/), we'll be using [Express](https://expressjs.com/) just so we can install it as a dependency.

Let's start by running the following command:

```bash
npm install express --save
```

If you check your project you'll notice few new things:
* A new file called `package-lock.json` has been added with a bunch of entries.
* A `node_modules` folder has also been added by npm and contains a lot of subfolders.
* A new entry under `dependencies` in your `package.json` has been added as follows: `"express": "^4.17.1"`.

Let's investigate each of those three points.

### The generated `package-lock.json` 🔒

This file helps npm process the dependency tree of your packages easier. In our example, by installing the package *Express* we implicitly told npm to install all the packages that Express depends on.

**Protip 💡:** You need to commit your `package-lock.json`, if you're curious to know why [read more here](https://docs.npmjs.com/cli/v7/configuring-npm/package-lock-json).

### The `node_modules` folder 🧩

This folder contains all the dependencies you've installed or needed by packages that you install. You shouldn't play around with this folder as npm uses it with the combination of `package-lock.json` to ensure that your `nodes_modules` content is the same as other people working with you on that project/repository/package.

**Protip 💡:** Don't forget to add the folder `node_modules` to your `.gitignore` file to avoid committing all your dependencies to your repository. [#81b30e5](https://github.com/tarkant/package-json-guide/commit/81b30e5ca9b604d68c28e4c914c1bb99f794b374)

### The new `dependencies` entry 🔗

Back to our `package.json`, we have a new sub-section as follows:

```JSON
// package.json
{
  [...]
  "dependencies": {
    "express": "^4.17.1"
  }
}
```

This is effectively the dependency that you've installed. As you might notice, there is the name of the dependency but also what looks like a version number. You might also have noticed the ^ prefix. This prefix tells npm if you're looking for a precise version of a dependency or if it's more flexible. For example;

* `^1.2.3` tells npm to install the package version 1.2.3 or later **as long as it's compatible with version 1.2.3 eg. version 1.4.0 or 1.5.3**. This lets you update your packages if there are minor updates without breaking your app.
* `~1.2.3` tells npm to install the package version 1.2.3 or later **as long as it's not a new minor version eg. version 1.2.4 or 1.2.11 but not 1.3.0**.
* `1.2.3` without any prefix tells npm that it has to install version 1.2.3 no matter what.

If you're really curious about how prefixes work, you can check out [the official documentation here](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#dependencies).

## Running your package & development dependencies 🏗 [#e3871db](https://github.com/tarkant/package-json-guide/commit/e3871db73ae631ca66cb68a6347864f3046ad916)

Let's add a file called `index.js` and insert the [hello world Express app](https://expressjs.com/en/starter/hello-world.html):

```javascript
// index.js
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('Hello World!')
});

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
});
```

After saving your file, you can run the following command in your `my-test-app` folder:

```bash
node ./index.js
```

If you head to [http://localhost:3000](http://localhost:3000) you'll be greeted by a plain *Hello world!*.

Say you want to run `npm start` to start your server instead of `node ./index.js`, this can be easily done by adding the following under the `scripts` entry:

```JSON
// package.json
{
  ...
  "scripts": {
    "start": "node ./index.js"
  },
 ...
}
```

Now you can run a simple `npm start` and your server will be up and running. But let's say you also want to have a dedicated development mode when you run `npm run dev` that starts your server in development mode and hot reloads the server each time you change something? Let's do this;

First, we need to install a *"development dependency"*, this means that it's a dependency useful only when we're in development mode. Run the following command:

```bash
npm install nodemon --save-dev
```

As you might have already guessed, the `--save-dev` flag tells npm to add this dependency as a dev dependency. You can check your `package.json` and see how it got updated accordingly. Next, let's add our *"dev"* script:

```JSON
// package.json
{
  ...
  "scripts": {
    "start": "node ./index.js",
    "dev": "nodemon ./index.js"
  },
 ...
}
```

Now if you run `npm run dev`, you'll see that your server will be up. You can change the *'Hello World!'* text and press F5 on your server to see that your changes have been reflected without restarting your server, neat! 🙌

## Life cycle scripts ⏰ [#eaf2028](https://github.com/tarkant/package-json-guide/commit/eaf202826e4fce5e6ba7cb45ad3ce53321807189)

As surprising as it might be, the `package.json` can do way more things than what can this article pay tribute to. In this last section, I want to touch on some interesting life cycle scripts.

### What are life cycle scripts? 🧐

Suppose you want to run the command `npm install`, you would like to perform a certain action *before* or *after* that npm runs the install command for a certain purpose. Let's try it by ourselves so you can understand it better. Open your `package.json` file and add the following line just after the *dev* script:

```JSON
// package.json
{
  ...
  "scripts": {
    "start": "node ./index.js",
    "dev": "nodemon ./index.js",
    "postinstall": "echo \"*#*#*# Hi! this is a postinstall script :) #*#*#*\"",
  },
 ...
}
```

Save your file and run the command `npm i` (short for `npm install`), you'll see something like below:

![npm-postinstall-demo.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1619738775629/AkZqkLqHK.gif)

As you can see in my screen recording, running `npm i` outputs the following:

```bash
> my-test-app@1.0.0 postinstall C:\Users\jella\git\my-test-app
> echo "Hi! this is a postinstall script :)"

"Hi! this is a postinstall script :)"
```

The first line tells you what npm is doing, in this case, it's running **my-test-app** **postinstall** script, then it shows you the command it's running. In our case, it's a simple `echo` command that's just like a `console.log` in JavaScript. Finally, it shows the result of that command.

### What's so useful about this? 🤨

That's what I wondered until I had to work on something that needed. You have to understand that life cycle scripts are not limited to simple echo commands. You can run anything with them. The famous package [node-sass](https://www.npmjs.com/package/node-sass) for example has a [*postinstall* script](https://github.com/sass/node-sass/blob/master/package.json) that runs a build of the node binary in your machine.

In my specific use-case, I've used *preinstall* to fire up an HTTP proxy for npm so it can work around our company's very restrictive network policies.

All in all, I want you to keep an open mind about this specific concept. Trust me, it will come useful for you one day! If you want to learn more about all life cycle scripts, you can read [the official docs here](https://docs.npmjs.com/cli/v7/using-npm/scripts).

## Wrap-up 📦

In this article, we've explored the anatomy of a `package.json`. We've learned how to generate one. How to manage our dependencies and scripts. And finally, we've quickly seen how life cycle scripts work.

Although there's much more to explore, I think that I've covered the highlights. I really hope that you've learned something useful with this. Again, you can find [this repository](https://github.com/tarkant/package-json-guide) with each step as a simple commit.

If you feel that I've forgotten a detail or didn't explain something well, please let me know.

Cheers!

Photo by [@brandablebox](https://unsplash.com/@brandablebox) from [Unsplash.com](https://unsplash.com/).
