Deep-dive on the Next Gen Platform. Join the Webinar!

Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Working with Node.js
      • Troubleshooting Node.js Apps
      • Node.js Behavior in Heroku
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Working with AI
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Language Support
  • Node.js
  • Working with Node.js
  • Best Practices for Node.js Development

Best Practices for Node.js Development

English — 日本語に切り替える

Last updated December 04, 2024

Table of Contents

  • Start Every New Project with NPM’s Init
  • Stick with Lowercase
  • Cluster Your App
  • Be Environmentally Aware
  • Avoid Garbage
  • Hook Things Up
  • Only Git the Important Bits

This material is a curated and maintained version of a blog post.

Start Every New Project with NPM’s Init

npm’s init command scaffolds out a valid package.json for your project, inferring common properties from the working directory.

$ mkdir example-app
$ cd example-app
$ npm init --yes

Run it with the --yes flag, and then open package.json to make changes. First, specify an engines key with your version of node (node -v).

"engines": {
  "node": "22.x"
}

Stick with Lowercase

Some languages encourage filenames that match class names, like MyClass and ‘MyClass.js’. Node.js uses lowercase files.

let MyClass = require('my-class');

Node.js is the rare example of a Linux-centric tool with great cross-platform support. While OSX and Windows treat myclass.js and MyClass.js equivalently, Linux doesn’t. To write code that’s portable between platforms, match require statements exactly, including capitalization.

The easy way is to stick with lowercase filenames for everything. For example, my-class.js.

Cluster Your App

Because the Node runtime is limited to a single CPU core and ~1.5 GB of memory, deploying a non-clustered node app on a large server is a waste of resources.

To take advantage of multiple cores and memory beyond 1.5 GB, add Cluster support into your app. Even if you’re only running a single process on small hardware today, clustering gives you easy flexibility for the future.

Testing is the best way to determine the ideal number of clustered processes for your app, but it’s good to start with the reasonable defaults offered by your platform, with a simple fallback. For example:

const CONCURRENCY = process.env.WEB_CONCURRENCY || 1;

Choose a Cluster abstraction to avoid reinventing process management. If you want to separate your main and worker files, try Forky. If you prefer a single entrypoint file and function, look at Throng. You can read more about Throng in Optimizing Node.js Concurrency.

Be Environmentally Aware

Don’t litter your project with environment-specific config files. Instead, take advantage of environment variables.

To provide a local development environment, create a .gitignore’d .env file, which heroku local loads.

DATABASE_URL='postgres://localhost/foobar'
HTTP_TIMEOUT=10000

Start your app with heroku local. It automatically pulls in these environment variables into your app under process.env.DATABASE_URLand process.env.HTTP_TIMEOUT. When you deploy your project, it automatically adapts to the variables on its new host.

This way is simpler and more flexible than config/abby-dev.js, config/brian-dev.js, config/qa1.js, config/qa2.js, and config/prod.js, etc.

Avoid Garbage

Node (V8) uses a lazy and greedy garbage collector. With its default limit of about 1.5 GB, it sometimes waits until it absolutely must reclaim unused memory. If your memory usage is increasing, it’s possible that it’s not a leak, but rather Node’s usual lazy behavior.

To gain more control over your app’s garbage collector, you can provide flags to V8 in your Procfile.

web: node --optimize_for_size --max_old_space_size=920 --gc_interval=100 server.js

This control is especially important if your app runs in an environment with less than 1.5 GB of available memory. For example, if you want to tailor Node to a 512 MB container, try:

web: node --optimize_for_size --max_old_space_size=460 --gc_interval=100 server.js

Hook Things Up

Lifecycle scripts make great hooks for automation. Heroku provides custom hooks so that you can run custom commands before or after we install your dependencies. To run something before building your app, you can use the heroku-prebuild script. To build assets with grunt, gulp, browserify, or webpack, do it in a build script.

In package.json:

"scripts": {
  "build": "bower install && grunt build",
  "start": "nf start"
}

You can also use environment variables to control these scripts.

"build": "if [ $BUILD_ASSETS ]; then npm run build-assets; fi",
"build-assets": "bower install && grunt build"

If your scripts start getting out of control, move them to files.

"build": "scripts/build.sh"

Scripts in package.json automatically have ./node_modules/.bin added to their PATH so that you can execute binaries like bower or webpack directly.

Only Git the Important Bits

Most apps consist of necessary files and generated files. When using a source control system like Git, avoid tracking anything generated.

For example, your node app probably has a node_modules directory for dependencies, which you want to keep out of Git. Add these files and directories to .gitignore.

As long as each dependency is listed in package.json, anyone can create a working local copy of your app, including node_modules, by running npm install.

Tracking generated files leads to unnecessary noise and bloat in your Git history. Worse, because some dependencies are native and must be compiled. Checking them in makes your app less portable because you provide builds from just a single, and possibly incorrect, architecture.

For the same reason, don’t check in bower_components or the compiled assets from grunt builds.

If you accidentally checked in node_modules before, that’s okay. You can remove it.

$ echo 'node_modules' >> .gitignore
$ git rm -r --cached node_modules
$ git commit -am 'ignore node_modules'

Also, ignore npm’s logs so that they don’t clutter the code.

$ echo 'npm-debug.log' >> .gitignore
$ git commit -am 'ignore npm-debug'

By ignoring these unnecessary files, your repositories are smaller, your commits are simpler, and you avoid merge conflicts in the generated directories.

Keep reading

  • Working with Node.js

Feedback

Log in to submit feedback.

Using WebSockets on Heroku with Node.js Deploying Node.js Apps on Heroku

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • heroku.com
  • Terms of Service
  • Privacy (日本語)
  • Cookies
  • Cookie Preferences
  • Your Privacy Choices
  • © 2025 Salesforce.com