Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Running an Unfamiliar Node.js Service

Hello! This course starts with a practical backend skill: taking a Node.js service you did not write and getting it running without guessing at commands or changing code prematurely. You already work with existing integration code, so the emphasis here is on becoming more independent at the setup stage. Later lessons will trace requests through the service and implement changes; today, the goal is to establish a reliable starting point.

By the end, you should be able to identify the right package, install its dependencies, supply safe local configuration, start it, and verify what actually works.


Read the repository before running it

An unfamiliar repository may contain more than one Node.js package. First locate the directory containing the service’s package.json; that is the package whose scripts and dependencies you need to inspect. In a repository with multiple packages, a command run from the wrong directory may install or start something else.

Use this short inspection sequence before installing anything:

  1. Read the service’s README for setup instructions, required software, and external services.
  2. Open package.json. Look at scripts, dependencies, devDependencies, and engines.
  3. Check for a package-lock.json, a Node-version file such as .nvmrc, and a configuration template such as .env.example.
  4. Inspect install-related scripts, including preinstall, postinstall, and prepare, before installing a repository you do not trust. Dependency installation can execute code.

The scripts object tells you what commands the project’s authors intended you to use. For example, npm run dev executes the command stored under dev; npm start executes the start script. A main field is not an instruction to start the service with that file. Always check the scripts and repository instructions first.

Scripts | npm Docs

Read npm’s Scripts documentation to see how package commands behave, particularly when a script refers to a relative file path.

In “Description,” read the opening paragraph, beginning with the explanation that scripts include lifecycle commands. Then, in “Working Directory for Scripts,” read the first paragraph from scripts’ working directory. Notice both the possible pre/post commands and the package-root path rule.

Suppose a manifest contains "dev": "node src/server.js". That path is interpreted from the package root, not from whichever folder happens to be open in your editor. If there is a predev script, npm may run it before dev as well. Reading the command is more informative than trying several likely filenames until one starts.


Install the dependencies the project declares

dependencies generally contains packages needed by the running application; devDependencies contains tools needed for development or building it. The exact versions available to install depend on the manifest and, when present, the lockfile. The engines field is a clue to the Node.js version the project expects—compare it with node --version before diagnosing a startup failure.

package.json | npm Docs

Use npm’s package.json reference to interpret a service’s dependency and Node-version requirements.

In “dependencies,” read the opening paragraph beginning Dependency declarations. In “devDependencies,” read the opening paragraph beginning development tools and the sentence immediately after it. In “engines,” read from Node compatibility. Focus on what each field tells you during setup, rather than memorizing version-range syntax.

For a repository with a committed package-lock.json, use npm ci in the appropriate package directory for a clean install. It expects the lockfile to agree with package.json and does not rewrite the lockfile to resolve a mismatch. If there is no lockfile, npm install installs the declared packages and can create one. Do not respond to a missing-package error by individually installing every package mentioned in source code: first establish whether you ran the project’s normal install command in the right directory.

If installation fails, read the first useful error and identify whether it concerns a Node version, package resolution, an install script, or software outside npm. A dependency’s bundled README, if present under node_modules, can clarify its local setup requirements. Avoid deleting a declared dependency merely to make installation appear successful.

How to Set Up a Node.js Project

Watch “How to Set Up a Node.js Project” from The Coding Train for a demonstration of installing an existing project rather than creating a new one.

Watch existing-project setup, paying attention to why node_modules is absent and why installing from the manifest matters. Continue through installation failure to see how an error can point to a dependency’s system-level requirements. The presenter temporarily removes a dependency while investigating; treat that as a demonstration, not as a general fix for someone else’s service.


Supply configuration without guessing at secrets

A successful install does not mean the service has what it needs to run. Backend services commonly read configuration from process.env: a port, database connection details, or an outbound API URL, for example. Search the README and configuration template for the expected variable names, then check how the application loads them. A .env file has no effect by itself; the project must load it through its startup command, code, or tooling.

For local setup, copy the template, fill in safe local values, and keep the resulting .env out of commits. Never put a real credential in a shared example, a screenshot, or an error report. If a service requires access you do not have, record that as a setup blocker. Do not replace a required credential with a made-up value and report the integration as working.

A running process is also not proof that all its dependencies work. A /health endpoint might establish only that the HTTP server responds; it might not contact a database or downstream API. State precisely what you verified.

A small backend setup lab

Spend about 15 minutes running this deliberately minimal service. It gives you the same artifacts you would inspect in an unfamiliar project, without requiring real external credentials. In a new scratch directory, create src/server.js and these files:

package.json:

{
  "name": "local-integration-service",
  "version": "1.0.0",
  "private": true,
  "engines": {
    "node": ">=20"
  },
  "scripts": {
    "dev": "node --watch src/server.js",
    "start": "node src/server.js",
    "check": "node --check src/server.js"
  },
  "dependencies": {
    "dotenv": "^16.4.5",
    "express": "^4.21.0"
  }
}

.env.example:

PORT=3000
DOWNSTREAM_BASE_URL=https://example.invalid

src/server.js:

require("dotenv").config();
const express = require("express");

const port = Number(process.env.PORT || 3000);
const downstreamBaseUrl = process.env.DOWNSTREAM_BASE_URL;

if (!downstreamBaseUrl) {
  throw new Error("DOWNSTREAM_BASE_URL is required");
}

const app = express();

app.get("/health", (request, response) => {
  response.json({ status: "ok" });
});

app.listen(port, () => {
  console.log(`Listening on port ${port}`);
});

Here, dotenv loads values from the local .env file into process.env; Express supplies the HTTP server interface. The example URL is intentionally nonfunctional. This service does not call it, so its health response cannot verify an outbound integration.

From the directory containing package.json:

  1. Confirm that node --version satisfies the manifest’s requirement.
  2. Run npm install. This scratch project has no lockfile yet; the command creates one. In a cloned project with a committed lockfile, you would normally use npm ci instead.
  3. Copy .env.example to .env (cp .env.example .env in many shells, or Copy-Item .env.example .env in PowerShell). Keep .env local.
  4. Run npm run check, then npm run dev. Visit http://localhost:3000/health and confirm the JSON response. Stop the server with Ctrl+C.
  5. Temporarily remove DOWNSTREAM_BASE_URL from your local .env and start the service again. Observe the configuration error, then restore the value.

If port 3000 is occupied, choose another port in .env and visit the corresponding address. The useful result of this lab is not merely a working endpoint: you should be able to explain which file declared each command, which packages had to be installed, where configuration came from, and what the health check did not prove. For your next unfamiliar backend repository, capture those points in a short local setup note.


When it does not start

Troubleshoot one layer at a time instead of changing application logic immediately. If npm cannot find a script, inspect the package directory and scripts. If Node cannot find a module, confirm that installation completed in that directory. If a declared script fails during install, read its output and the dependency’s setup instructions. If the application reports a missing environment variable, locate its documented source and loading mechanism. If the server starts but a request fails, keep the startup result separate from the request failure.

This distinction is valuable in integration work: “the process started,” “the HTTP boundary responded,” and “the downstream operation succeeded” are three different claims. Accurate setup notes make later debugging—and handoffs to another engineer—much faster.


You now have a repeatable method for running an unfamiliar Node.js service: inspect its package and instructions, use the appropriate install command, provide configuration safely, start the declared script, and verify a specific behavior. In the next lesson, you will move beyond startup and trace how request data and control flow travel through an integration service.

Can't find a good explanation? Sign up and we'll make it for you

Sign up