JavaScript

Let’s Get Ready to Rumble! Composer vs npm

TL;DR?

NPM 🥊 Composer 👊
npm install (libraries go in node_modules, executables in node_modules/.bin) composer install (libraries go in vendor, executables in vendor/bin)
npm install --production composer install --no-dev
npm install --package-lock-only composer update --lock
npm install <package> composer require <package>
npm install <package> --save-dev composer require --dev <package>
npm install -g <package> composer global require <package>
npm update composer update
npm update <package> composer update <package>
npm outdated composer outdated
npm run <script-name> composer run-script <script-name>

The Complete Picture

As developers, we’re used to saying, “It works on my machine.” A decade ago, this phrase was reserved for snarky conversations with your friendly quality assurance person or the product owner who is testing a feature.

As our technology tooling has become more advanced (read: more complex), I’ve seen it come up as inter-developer conversation, frequently between a frontend and backend engineers:

Composer and Node package manager (npm) are the de-facto package managers for PHP and JavaScript. We use them every day at WebDevStudios. Let’s help each other get to know their similarities and their differences, as both tools can do unexpected things that you may not realize.

Init

While initializing a project isn’t the most common command, it’s useful to cover it first; in case you want to try to initialize a Composer or npm project in an empty directory.

$ composer init

Composer will interactively ask you some questions about your PHP project including: package name, description, package type, etc. Newer versions of Composer will prompt you to add the vendor directory to your .gitignore file. You should do this!

You can also just create your own composer.json file. Here’s an example minimal composer.json:

{
    "name": "webdevstudios/composer-vs-npm",
    "description": "php side of composer-vs-npm",
    "license": "GPL-3.0-or-later"
}

npm has the same command:

$ npm init

It will also interactively ask you a few questions about your JavaScript project including: package name, description, license, etc. After gathering this info, it will create a package.json file.

You can instead create a package.json file yourself. A minimal package.json is just {}, but npm will warn you if you don’t have these fields:

{
  "name": "@webdevstudios/composer-vs-npm",
  "description": "js side of composer-vs-npm",
  "license": "GPL-3.0-or-later",
  "repository": "https://github.com/WebDevStudios/composer-vs-npm"  
}

Init Best Practices

For the npm package name, it’s a good idea to add a scope using the @ symbol, your organization name, and a slash before the package name. While it’s not required like Composer’s vendor name, it will help group your packages together.

npm does not offer to create an entry in .gitignore for the node_modules directory, but you should.

You can validate your composer.json file format by running:

$ composer validate

I recommend not including version in your composer.json. It’s just one more thing that developers will forget or ignore. Let your version numbers be managed through tagging in your version control software.

npm requires a version entry in package.json only if you’re publishing your package. If you’re publishing your package, make sure keeping this up to date is part of your workflow. Otherwise, I suggest to omit it.

Both Composer and npm use the same license strings. A license entry is not strictly required, but both will print a warning, if you don’t include it.

Saving Dependencies

I’m getting a little ahead of myself because we’re going to talk about npm install before the Install section below, but just bear with me. In Composer land, telling it that your project relies on a library is done by requiring it.

$ composer require <vendor>/<name>

This will instruct Composer to add an entry to the require section of your composer.json file, install the package (usually in a vendor directory), and create and/or update a composer.lock file with info about the version it installed.

npm does the same thing, but with their install command:

$ npm install <package>

This will instruct npm to add an entry to the dependencies section of your package.json file, install the package (in a node_modulesdirectory), and create and/or update a package-lock.json file with info about the version it installed.

Saving Options

You may have PHP or JavaScript dependencies that are only needed for development, and aren’t needed to use your project when it’s deployed. You can specify that those packages are installed as development dependencies:

$ composer require --dev <vendor>/<name>
$ npm install <name> --save-dev

Both Composer and npm can require specific versions of a package:

$ composer require <vendor>/<name>:<version>
$ npm install <name>@<version>

If you omit the version number, they’ll both specify the latest stable version with a carat (^) pre-pended. If you require lodash via npm, it will specify "lodash": "^4.17.20" in your package.json file. This means if you were to tell npm to update Lodash, it would upgrade to newer versions up to the next major version—5.0.0. If you ever get unexpected results from version numbers, you can use these excellent semantic versioning (semver) tools to test:

Install

As the “Dad Joke Dog” meme explains above, the most common command to run is install.

$ composer install

Running this in a folder that has a composer.json file will instruct composer to read that file (along with composer.lock if present) and install any requirements specified.

If the composer.lock file is present, it will install the exact versions specified by that file, even if a newer version exists. If there’s no composer.lock file, composer will install the latest packages according to the version constraints in composer.json and create a composer.lock file with those versions.

Usually packages will go into a vendor directory, next to the composer.json file.

$ npm install

Running this in a folder that has a package.json file will instruct npm to read that file (along with package-lock.json if present) and install any requirements specified.

If the package-lock.json file is present, npm (similarly) will install the exact versions specified by that file, even if a newer version exists. If there’s no package-lock.json file, npm will install the latest packages according to the version constraints in package.json and create a package-lock.json file with the exact versions.

Installing without development dependencies

To install packages without the development dependencies, like you would for a production environment, run either of these commands:

$ composer install --no-dev
$ npm install --production

This will skip installing require-dev or devDependencies for Composer or npm (respectively).

Update

This is one of those sections where I like to ask:

Are you sure you want to do this?

$ composer update
$ npm update

I like to ask are you sure because when you don’t supply any arguments to update, Composer and npm have the potential to make a lot of changes that you may not have intended. Both will use the version constraints to update your specified dependencies (including dev dependencies) and it will update the child dependencies of the packages, too. So, if your version constraints are fairly “loose,” a lot of stuff is going to get updated, possibly some things you didn’t intend.

A more focused approach would be to upgrade one specific package, not all of them. Like this:

$ composer update composer/installers
$ npm update lodash

If you’re curious what is out of date, and what would be updated if you ran the update command without any arguments, you can run an outdated report instead:

$ composer outdated
$ npm outdated

Other useful commands

If you change something in your composer.json file that is unrelated to the version constraints—like reordering the required packages—Composer will complain that your composer.lock file is not up to date. To update just the content-hash part of the composer.lock file without updating any package versions, run:

$ composer update --lock

You can do the same thing with npm:

$ npm install --package-lock-only

Conclusion

As you can see, there are several commands that do the same thing and look the same in both Composer and npm. However, there are also commands that do the same thing but look very different.

It’s good to make sure you know what that command is going to do before you do it, so you don’t have to undo things you didn’t want to happen. 😬

Are there any other commands you use often that I missed? Let us know in the comments.

Comments

Have a comment?

Your email address will not be published. Required fields are marked *

accessibilityadminaggregationanchorarrow-rightattach-iconbackupsblogbookmarksbuddypresscachingcalendarcaret-downcartunifiedcouponcrediblecredit-cardcustommigrationdesigndevecomfriendsgallerygoodgroupsgrowthhostingideasinternationalizationiphoneloyaltymailmaphealthmessagingArtboard 1migrationsmultiple-sourcesmultisitenewsnotificationsperformancephonepluginprofilesresearcharrowscalablescrapingsecuresecureseosharearrowarrowsourcestreamsupporttwitchunifiedupdatesvaultwebsitewordpress