Skip Navigation
Show nav
Heroku Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
  • 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
View categories

Categories

  • Heroku Architecture
    • Dynos (app containers)
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Command Line
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery
    • Continuous Integration
  • Language Support
    • Node.js
    • Ruby
      • Rails Support
      • Working with Bundler
    • Python
      • Background Jobs in Python
      • Working with Django
    • Java
      • Working with Maven
      • Java Database Operations
      • Java Advanced Topics
      • Working with Spring Boot
    • PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
    • Heroku Data For Redis
    • Apache Kafka on Heroku
    • Other Data Stores
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
    • Compliance
  • Heroku Enterprise
    • Private Spaces
      • Infrastructure Networking
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
    • Single Sign-on (SSO)
  • 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
  • Best Practices for Node.js Development

Best Practices for Node.js Development

English — 日本語に切り替える

Last updated December 09, 2021

Table of Contents

  • Start every new project with npm 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 on the same topic.

Start every new project with npm init

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

$ mkdir my-awesome-app
$ cd my-awesome-app
$ npm init --yes

Run it with the --yes flag and then open package.json to make changes. The first thing you should do is specify an ‘engines’ key with your current version of node (node -v):

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

Stick with lowercase

Some languages encourage filenames that match class names, like MyClass and ‘MyClass.js’. Don’t do that in node. Instead, use 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 will treat ‘myclass.js’ and ‘MyClass.js’ equivalently, Linux won’t. To write code that’s portable between platforms, you’ll need to exactly match require statements, including capitalization.

The easy way to get this right is to just stick with lowercase filenames for everything, eg ‘my-class.js’.

Cluster your app

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

To take advantage of multiple cores and memory beyond 1.5 GB, bake Cluster support into your app. Even if you’re only running a single process on small hardware today, Cluster 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, eg:

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

Choose a Cluster abstraction to avoid reinventing the wheel of process management. If you’d like separate main and worker files, you can try forky. If you prefer a single entrypoint file and function, take a look at throng.

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 will be loaded by heroku local:

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

Now start your app with heroku local, and it will automatically pull in these environment variables into your app under process.env.DATABASE_URL and process.env.HTTP_TIMEOUT. And, when you deploy your project, it will automatically adapt to the variables on its new host.

This is simpler and more flexible than ‘config/abby-dev.js’, ‘config/brian-dev.js’, ‘config/qa1.js’, ‘config/qa2.js’, ‘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 has to before reclaiming unused memory. If your memory usage is increasing, it might not be 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 is especially important if your app is running in an environment with less than 1.5 GB of available memory. For example, if you’d like 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

npm’s lifecycle scripts make great hooks for automation. Heroku provides custom hooks that allow you to run custom commands before or after we install your dependencies. If you need to run something before building your app, you can use the heroku-prebuild script. Need 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 you can execute binaries like bower or webpack directly.

Only git the important bits

Most apps are composed of both necessary files and generated files. When using a source control system like git, you should avoid tracking anything that’s generated.

For example, your node app probably has a node_modules directory for dependencies, which you should keep out of git.

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, since some dependencies are native and must be compiled, checking them in makes your app less portable because you’ll be providing builds from just a single, and possibly incorrect, architecture.

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

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

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

I also ignore npm’s logs so they don’t clutter my code:

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

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

Keep reading

  • Node.js

Feedback

Log in to submit feedback.

Using WebSockets on Heroku with Node.js Building Node.js Apps with Grunt

Information & Support

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

Language Reference

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

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing

Subscribe to our monthly newsletter

Your email address:

  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Heroku Podcasts
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Facebook
  • Instagram
  • Github
  • LinkedIn
  • YouTube
Heroku is acompany

 © Salesforce.com

  • heroku.com
  • Terms of Service
  • Privacy
  • Cookies
  • Cookie Preferences