Four files and one directory carry every npm-managed project. Knowing what each one does turns you from someone who runs commands into someone who can debug them.
package.json is the project manifest. It declares the project's name, version, scripts, runtime dependencies, dev dependencies, peer dependencies, the entry points (main/module/exports), and metadata. Every JS tool reads this. Treat it as the project's identity card; commit every change.
package-lock.json is the exact tree of installed packages, including transitive deps, with content hashes. Always commit it. It's what guarantees that everyone on your team and every CI run installs identical bytes. Manual edits are forbidden; only npm itself should rewrite it.
node_modules/ is where packages physically live. Never commit it (always .gitignore). npm's structure is mostly flat, with deduplication moving common deps to the top — which is fast but allows phantom dependencies, where your code accidentally imports a package not declared in package.json but present because some other dep needed it. (pnpm fixes this; npm doesn't.)
Workspaces let you run multiple sub-packages from one repo. Declare a workspaces array in the root package.json pointing at sub-package paths; npm hoists shared deps to the root node_modules. It's monorepo support — basic, but functional. (pnpm and Yarn are stronger here.)