Skip to content

Dependencies

Jonathan Sharpe edited this page Aug 24, 2024 · 8 revisions

In short:

  • Because the development dependencies are stripped out, anything you need Express to access at runtime must be a production dependency; and
  • To keep the build size small (e.g. Heroku has a slug size limit of 500Mb), anything that's not needed by Express at runtime should be a development dependency.

Build process

The intended deployments go through the following build process:

  1. npm install - install all dependencies, development and production
  2. npm run build - build the output in api/static/
  3. npm prune --production - strip out the development dependencies

At runtime, they just have to npm start. The development dependencies aren't needed (or available!) at runtime.

New dependencies

When adding a new dependency, classify it as follows:

Needed by At runtime During development
Client only npm install --workspace web <thing>
(examples: React, React Router)
npm install --save-dev --workspace web <thing>
(examples: Vite(st), Testing Library)
E2E tests only N/A npm install --save-dev --workspace e2e <thing>
(examples: Playwright)
Server only npm install --workspace api <thing>
(examples: Express, Morgan)
npm install --save-dev --workspace api <thing>
(examples: Jest, SuperTest)
Two or more workspaces N/A npm install --save-dev <thing>
(examples: Concurrently, ESLint)

If you're not sure whether something is needed at runtime or not, consider: will you be importing it in a non-.test file?

Audit

When you install or update your dependencies, you may see a message like this:

63 moderate severity vulnerabilities

To address issues that do not require attention, run:
  npm audit fix

To address all issues possible (including breaking changes), run:
  npm audit fix --force

Some issues need review, and may require choosing
a different dependency.

Run `npm audit` for details.

Many of the dependencies are only used during development, and not exposed to any end-user inputs, so aren't such a big deal (see e.g. facebook/create-react-app#11174 for more discussion). To focus on the ones that are used at runtime (assuming you've categorised any new dependencies appropriately, see Dependencies#new-dependencies), which is where the greatest risk lies, omit the development dependencies from the audit report:

$ npm audit --omit=dev
found 0 vulnerabilities

npm provides some commands you can use to manage the dependency tree (and automatically update the package.json "package file" and package-lock.json "lock file" if needed):

  • npm audit fix will update any packages in your dependency tree where:

    1. there is a known vulnerability in a version you have installed; and
    2. there is a fixed version that's semver-compatible with what's in your package file;
  • npm explain <package>/npm ls <package> tell you why the package is in your dependency tree (vulnerabilities won't always be in direct dependencies like react, sometimes they're in transitive dependencies):

     $ npm ls word-wrap
     starter-kit@0.0.1 path/to/starter-kit
     └─┬ jest-environment-jsdom@29.5.0
       └─┬ jsdom@20.0.3
         └─┬ escodegen@2.0.0
           └─┬ optionator@0.8.3
             └── word-wrap@1.2.3
  • npm outdated will show you a list of packages you have installed that have newer releases:

     $ npm outdated
     Package             Current   Wanted   Latest  Location                         Depended by
     cypress             12.14.0  12.16.0  12.16.0  node_modules/cypress             starter-kit
     eslint-plugin-jest   27.2.1   27.2.2   27.2.2  node_modules/eslint-plugin-jest  starter-kit
     react-router-dom     6.13.0   6.14.0   6.14.0  node_modules/react-router-dom    starter-kit
     webpack              5.87.0   5.88.0   5.88.0  node_modules/webpack             starter-kit
  • npm update will update all packages in your dependency tree to the latest semver-compatible releases;

    • npm update --save will also update the package file so that the minimum version is the latest semver-compatible one;
    • npm update <package>@<version> will update to the specified version (e.g. npm update react@latest would install the latest version) whether or not it's compatible with what's in the package file.

⚠️ Do not run npm audit fix --force. That will apply changes that are not compatible with the dependencies declared in the package file, which can break things outright or cause hard-to-debug errors. If you need to apply major-version updates, do them one at a time using npm update <package>@<version> and test each one.

If it's a package you've added (i.e. one that's not required by the base starter kit), a final option is to look for alternatives if it's not a well-maintained dependency (e.g. infrequent releases, lots of reported bugs that don't get fixed).

Clone this wiki locally