Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
The Client Half  ›  Lesson

Packages and Scripts for a Two-Part App

npm 12 minA full-stack project keeps a separate package.json for the client and the server, listing runtime dependencies, development-only tools and named scripts, with the lockfile committed and node_modules left out.

Packages and Scripts for a Two-Part App

Clone Sprout and you find two folders, client and server, each with its own package.json. Those two small files say what each half needs and how to start it. Read them well and you can run either half with one command, and keep code meant for the browser apart from code meant for the server.

The rule

Each half has its own package.json: what it needs, and how to run it.

sprout/
  client/         React app that runs in the browser
    package.json
    src/
  server/         Node.js API that runs on the server
    package.json
    src/
  .gitignore

Here is the server's file:

{
  "name": "sprout-server",
  "type": "module",
  "scripts": {
    "dev": "node --watch src/index.js",
    "start": "node src/index.js",
    "test": "node --test"
  },
  "dependencies": {
    "express": "^4.19.2",
    "pg": "^8.12.0"
  },
  "devDependencies": {
    "eslint": "^9.9.0"
  }
}
  • dependencies are packages the running app imports: Express and the PostgreSQL driver pg on the server, React in the client.
  • devDependencies only help while you write, test or build: linters, test tools, and the client's build tool, Vite.
  • scripts are named commands. npm run dev starts either half in development; npm start and npm test work without the word run.
  • package-lock.json records the exact version of every installed package. Commit it, so every laptop and the build server install the same tree. npm ci installs exactly what it says.
  • node_modules/ is rebuilt from the lockfile, so it is never committed; it belongs in .gitignore.

The two halves also end up very differently. The server runs as a long-lived process with npm start. The client runs npm run build, which turns the React source into plain HTML, CSS and JavaScript files in client/dist/. In production nothing "runs" the client at all; a web server simply hands those files to browsers.

Separate lists make it obvious what runs where. The browser build never needs the database driver, and the API never needs React. (Tools such as npm workspaces can manage both folders from the root, but each half still keeps its own list.)

The shortcut

For every package, ask two questions. Which half imports it? That picks the folder. Does the running app import it, or only my tools? That picks dependencies or devDependencies.

Adding packages to SproutStep 1 of 4
  1. pg

    The server's code imports it at runtime to talk to PostgreSQL. server, dependencies.

The trap is installing pg in the client folder "because the client needs data". The client never talks to the database. It asks the API.

Drills

Answer fast — trust the rule.

quick drill

Sprout's server imports express when it runs. Where does it belong?

quick drill

Which of these should be committed to git?

quick drill

What does npm run build produce for Sprout's React client?

quick drill

The build server must install exactly the versions the team tested. Which command fits?

Remember

  • Each half has its own package.json: what it needs, and how to run it.
  • Shortcut: which half imports it, and does the running app import it or only your tools?
  • Trap: database packages in the client. The browser asks the API; only the server talks to the database.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainOne package.json too many things

A teammate put pg, express, react and vite together in one root package.json, all under dependencies. Explain how you would split them across Sprout's client and server folders, which list each goes in, and why the split matters.