Package.json best practices in Node.js

Mayank C
Tech Tonic

--

In this beginner article, we’ll go over the 8 (2 bonus) best practices beginners should follow with package.json in their projects. Let’s get started.

1 Keep it simple and minimal

A cluttered package.json file can lead to confusion, errors, and maintenance headaches. To avoid this, it's essential to keep your package.json file simple and minimal. This means including only the necessary fields and dependencies, and avoiding unnecessary complexity.

Unnecessary fields

Avoid including unnecessary fields like description, keywords, or author if they're not relevant to your package. While these fields might be useful for public packages, they're not essential for internal projects.

Example of an unnecessary field:

{
"name": "my-package",
"version": "1.0.0",
"description": "This is a package description", // Unnecessary field
"dependencies": {
"express": "^4.17.1"
}
}

Minimizing dependencies

Only include dependencies that are essential to your package’s functionality. Avoid including dependencies that are not used or are redundant.

Example of minimized dependencies:

{
"name": "my-package",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1" // Only include necessary dependencies
}
}

By keeping your package.json file simple and minimal, you'll reduce the risk of errors, make maintenance easier, and improve overall package performance.

2 Use precise version numbers

When specifying dependencies in your package.json file, it's important to use precise version numbers instead of version ranges or wildcards. This ensures that your package always uses the exact version of a dependency that you've tested and verified, which is critical for maintaining stability and predictability in your project.

The problem with version ranges

Version ranges (e.g., ^1.2.3 or ~1.2.3) allow for flexibility in dependency versions, but they can also lead to unexpected behavior and errors. When you use a version range, npm or yarn will install the latest version that satisfies the range, which may not be the version you intended or tested.

Example of a version range:

{
"name": "my-package",
"version": "1.0.0",
"dependencies": {
"express": "^4.17.1" // Version range
}
}

The benefits of precise version numbers

Using precise version numbers (e.g., 1.2.3) ensures that your package always uses the exact version of a dependency that you’ve specified. This provides several benefits:

  • Predictability: You know exactly which version of a dependency will be installed.
  • Stability: Your package will always behave the same way, without unexpected changes due to dependency updates.
  • Reproducibility: Your package can be easily reproduced and verified by others, since the exact dependency versions are specified.

Example of precise version numbers:

{
"name": "my-package",
"version": "1.0.0",
"dependencies": {
"express": "4.17.1" // Precise version number
}
}

By using precise version numbers in your package.json file, you can ensure that your package is stable, predictable, and easy to maintain.

3 Specify a license

When creating a package, it’s important to specify a license to declare how others can use, modify, and distribute your work. A license provides legal protection and clarity for both you and users of your package.

Why specify a license?

Specifying a license:

  • Clarifies usage rights: Clearly defines how others can use your package.
  • Protects your work: Ensures that others cannot claim your work as their own or use it in ways you didn’t intend.
  • Encourages collaboration: By specifying a license, you invite others to contribute and build upon your work.
  • Meets legal requirements: Many open-source platforms and repositories require a specified license.

Common licenses

Some popular licenses for packages include:

  • MIT License: Permissive, allows free use and modification.
  • Apache License 2.0: Permissive, allows free use and modification, with some conditions.
  • GNU General Public License (GPL): Copyleft, requires derivative works to be distributed under the same license.

How to specify a license

Add a license field to your package.json file with the name of the license you've chosen.

Example:

{
"name": "my-package",
"version": "1.0.0",
"license": "MIT" // Specify the license
}

Alternatively, you can include a LICENSE file in the root of your package with the full text of the license.

Best practice

Specify a license in your package.json file to provide clear guidance on how others can use and interact with your package.

4 Use scripts

Scripts in package.json allow you to define commands that can be executed with npm or yarn. This provides a convenient way to automate tasks, simplify workflows, and make your package more user-friendly.

Benefits of scripts

Using scripts in package.json offers several benefits:

  • Simplifies tasks: Scripts can automate complex commands, making it easier for users to perform tasks.
  • Improves user experience: Scripts can provide a more intuitive interface for users, hiding complex commands and dependencies.
  • Enhances package functionality: Scripts can add additional functionality to your package, making it more versatile and useful.

Common script examples

Some common scripts include:

  • Start: Starts the application or server.
  • Build: Compiles or builds the package.
  • Test: Runs tests for the package.
  • Lint: Runs code linting and formatting tools.

How to define scripts

Add a scripts field to your package.json file with key-value pairs, where the key is the script name and the value is the command to execute.

Example:

{
"name": "my-package",
"version": "1.0.0",
"scripts": {
"start": "node server.js", // Define a script to start the server
"build": "webpack", // Define a script to build the package
"test": "jest" // Define a script to run tests
}
}

Executing scripts

To execute a script, use the following command:

npm run <script-name>

or,

yarn run <script-name>

Replace <script-name> with the name of the script you defined in package.json.

By using scripts in your package.json file, you can streamline tasks, enhance user experience, and make your package more functional and user-friendly.

5 Organize dependencies

When managing dependencies in your package.json file, it's essential to keep them organized to ensure clarity, maintainability, and scalability. Proper organization helps you and others understand the dependencies required for your project.

Types of dependencies

Dependencies can be categorized into two main types:

  • Dependencies: Essential for the production environment (e.g., express, react).
  • DevDependencies: Only required for development and testing (e.g., webpack, jest).

Organizing dependencies

To organize dependencies, use the following best practices:

  • Group dependencies: Separate dependencies and devDependencies into distinct sections.
  • Alphabetize dependencies: List dependencies in alphabetical order within each section.
  • Use precise versions: Specify exact versions for dependencies to ensure reproducibility.

Example of organized dependencies:

{
"name": "my-package",
"version": "1.0.0",
"dependencies": {
"express": "4.17.1",
"react": "17.0.2"
},
"devDependencies": {
"jest": "27.5.1",
"webpack": "5.75.0"
}
}

Benefits of organized dependencies

Organizing dependencies offers several benefits:

  • Easy identification: Clearly distinguish between essential and development dependencies.
  • Improved maintainability: Simplifies updates and dependency management.
  • Enhanced collaboration: Facilitates understanding and contributions from team members.

By organizing your dependencies in a logical and structured manner, you can ensure a more efficient and scalable development process.

6 Use a consistent naming convention

When creating and managing package.json files, it's essential to use a consistent naming convention for fields, scripts, and dependencies. This ensures clarity, readability, and maintainability, making it easier for developers to understand and work with your package.

Benefits of consistent naming conventions in package.json

Using a consistent naming convention in package.json offers several benefits:

  • Readability: Clearly indicates the purpose and functionality of fields, scripts, and dependencies.
  • Maintainability: Simplifies updates and modifications.
  • Collaboration: Facilitates understanding and contributions from team members.
  • Reusability: Enables easy reuse of packages in other projects.

Best practices for naming conventions in package.json

  • Use camelCase or underscore notation: Choose a consistent naming convention for fields, scripts, and dependencies.
  • Be descriptive: Use clear and descriptive names for fields, scripts, and dependencies.
  • Avoid duplicates: Ensure unique names for fields, scripts, and dependencies.

Example of consistent naming convention in package.json

{
"name": "myPackage",
"version": "1.0.0",
"scripts": {
"startServer": "node server.js",
"buildBundle": "webpack"
},
"dependencies": {
"express": "4.17.1",
"react": "17.0.2"
}
}

In this example, the naming convention used is camelCase, with uppercase letters separating words. The names are also descriptive, indicating the purpose of each field, script, and dependency.

7 Keep dependencies up-to-date

Outdated dependencies can lead to security vulnerabilities, compatibility issues, and bugs in your project. Keeping dependencies up-to-date ensures you have the latest features, security patches, and bug fixes.

Why keep dependencies up-to-date?

  • Security: Outdated dependencies can contain known security vulnerabilities, putting your project at risk.
  • Compatibility: Updated dependencies ensure compatibility with the latest versions of other packages and frameworks.
  • Bug fixes: Updated dependencies often include bug fixes, preventing issues in your project.
  • New features: Updated dependencies provide access to new features and functionality.

How to keep dependencies up-to-date

  • Regularly run npm update or yarn update: This updates all dependencies to their latest versions.
  • Use a dependency manager like npm-check or yarn-check: These tools identify outdated dependencies and provide update suggestions.
  • Set up a continuous integration/continuous deployment (CI/CD) pipeline: Automate dependency updates as part of your build process.
  • Use a package manager like npm or yarn with a lockfile: Lockfiles ensure consistent dependency versions across environments.

Example of updating dependencies with npm

npm update

This command updates all dependencies to their latest versions.

Example of using npm-check to identify outdated dependencies

npm-check

# Output:
# express 4.17.1 (installed: 4.16.4)
# react 17.0.2 (installed: 16.14.0)

This output shows outdated dependencies, indicating the need for an update.

By keeping your dependencies up-to-date, you ensure a secure, compatible, and feature-rich project. Regularly update your dependencies to avoid issues and take advantage of new features and functionality.

8 Validate your package.json

A package.json file is the backbone of any npm package, containing essential metadata and dependencies. However, a single error or inconsistency can lead to issues and errors. Validating your package.json file ensures accuracy, consistency, and reliability.

Why validate package.json?

  • Ensure accuracy: Verify that metadata, dependencies, and scripts are correctly defined.
  • Prevent errors: Catch syntax errors, typos, and inconsistencies before they cause issues.
  • Improve compatibility: Ensure package.json conforms to npm standards, ensuring compatibility with various environments.

How to validate package.json

  • Use npm’s built-in validation: Run npm validate in your project's root directory.
  • Use a package.json linter: Tools like package-json-validator or jsonlint can identify errors and warnings.
  • Manually review package.json: Regularly inspect the file for errors, inconsistencies, and outdated information.

Example of npm validate output

$ npm validate

package.json is valid

If errors are found, npm validate will output a detailed error message, indicating the issue and its location.

Tips and best practices

  • Regularly validate package.json: Include validation in your development workflow to catch errors early.
  • Use a linter or validator: Automate validation to ensure consistency and accuracy.
  • Keep package.json up-to-date: Regularly review and update package.json to ensure it remains accurate and consistent.

By validating your package.json file, you ensure a reliable, error-free, and compatible package that is easy to maintain and publish.

The point number 9 and 10 are not directly related to package.json. They’re included only for completeness.

9 Use a .npmignore file

This isn’t directly related to package.json. When publishing a package to npm, it’s important to exclude certain files and directories that are not essential to the package’s functionality. A .npmignore file allows you to specify which files and directories to ignore, ensuring a lean and efficient package.

Why use a .npmignore file?

  • Reduce package size: Exclude unnecessary files and directories to minimize package size.
  • Improve security: Prevent sensitive files like API keys or credentials from being accidentally published.
  • Simplify package maintenance: Ignore files and directories that are not essential to the package’s functionality.

How to use a .npmignore file

  • Create a .npmignore file in the root of your project: This file specifies the files and directories to ignore.
  • Add files and directories to ignore: Use glob patterns or explicit file paths to specify what to ignore.

Example .npmignore file

# Ignore node_modules directory
node_modules

# Ignore .git directory
.git

# Ignore .env file
.env

# Ignore any files with the .ts extension
**/*.ts

This .npmignore file excludes the node_modules and .git directories, the .env file, and any files with the .ts extension.

Tips and best practices

  • Use glob patterns for flexibility: Glob patterns allow you to ignore files and directories with specific names or extensions.
  • Be explicit for sensitive files: Explicitly ignore sensitive files like API keys or credentials to ensure they are not accidentally published.
  • Keep the .npmignore file up-to-date: Regularly review and update the .npmignore file to ensure it remains effective.

By using a .npmignore file, you can ensure a lean, efficient, and secure package that is easy to maintain and publish.

10 Include a README

Again, not something directly related to package.json. A README file is a crucial component of any package, providing essential information and context for users, contributors, and maintainers. It serves as a starting point, helping others understand your package’s purpose, functionality, and usage.

Why include a README?

  • Provide context: Give users a quick understanding of your package’s purpose and functionality.
  • Document usage: Explain how to install, configure, and use your package.
  • List dependencies: Identify dependencies and their versions.
  • Share contributing guidelines: Encourage contributions and provide guidelines for contributors.
  • License and copyright information: Specify the license and copyright terms.

What to include in a README

  • Package description: Briefly explain the package’s purpose and functionality.
  • Installation instructions: Provide steps to install the package.
  • Usage examples: Offer examples of how to use the package.
  • Dependencies: List dependencies and their versions.
  • Contributing guidelines: Explain how to contribute to the package.
  • License and copyright information: Specify the license and copyright terms.

Example README content

# My Package

A brief description of my package.

## Installation

npm install my-package

## Usage

import myPackage from 'my-package';

myPackage.doSomething();

## Dependencies

* express: ^4.17.1
* react: ^17.0.2

## Contributing

Please submit issues and pull requests to the repository.

## License

MIT License

## Copyright

© 2023 My Name

Tips and best practices

  • Keep it concise: Focus on essential information.
  • Use Markdown: Format your README with Markdown for readability.
  • Include examples: Provide usage examples for clarity.
  • Keep it up-to-date: Regularly review and update your README.

By including a well-structured README file, you provide a valuable resource for users, contributors, and maintainers, making your package more accessible and user-friendly.

Thanks for reading!

--

--