Build the experiment yourself
Keep your existing working project as a checkpoint. Create this structure by moving your browser files into public/, preserving relative imports. All JavaScript files use .js.
order-workbench/
package.json
server.js
public/
index.html
styles.css
main.js
domain/ (your Course 02 functions)
ui/ (your rendering and form modules)
tests/
Recall: configuration and JavaScript modules
Course 02 introduced package.json and "type": "module". Keep that setting so Node can run your .js imports. The browser still loads its entry point through <script type="module"> in HTML; it does not read package.json.
New: named commands with npm scripts
Node executes JavaScript. npm is a separate command-line tool, usually supplied with a Node installation; here we use it to run named project commands. Check node --version and npm --version in your terminal. A missing npm command is an environment setup issue, not a defect in your pricing function.
Update your practice project's package.json, preserving any existing fields:
The skeleton introduces a Map: a collection of key/value pairs. Here each URL path is a key and its file/content-type pair is the value. files.get(path) retrieves that pair or returns undefined; it does not search your folders automatically. Later examples use set to record a value and has to test whether a key exists.
The node: imports are built into Node. createServer receives a callback for incoming requests; readFile asynchronously reads an allowed file. new URL separates a request's path from its query parameters. import.meta.url identifies this server module so relative file locations resolve beside it. Trace one page request and one API request through the branches before extending the map.
{
"private": true,
"type": "module",
"scripts": {
"start": "node server.js",
"test": "node --test"
}
}
private prevents accidental npm publication. scripts maps names to commands; it does not create the referenced files. JSON requires double-quoted keys and strings, with no comments or trailing commas.
| Command in this practice folder | Configured action |
|---|---|
| npm start | Run node server.js, after you create the server below |
| npm test | Run node --test to discover and execute tests |
| npm run | List the available scripts |
Start and test have shorthand commands; custom names use npm run name. These mappings are project-specific: inspect package.json before assuming what a command does. See the official npm script documentation.
Open the terminal in order-workbench, where this package.json lives, rather than in the portfolio repository. No external dependencies are needed for this lab, so there is nothing to install with npm install.
Keep the existing tests running
Moving files changes relative import paths. Fix those paths and run your original assertions directly, for example node public/domain/tests.js if that is where you moved them. For automatic discovery, move or rename your assertion file to tests/pricing.test.js and adjust its imports to ../public/domain/.... Later, the testing module introduces named cases with node:test.
Run npm test and inspect which files actually executed. A command that exits successfully with zero tests does not verify your pricing. Temporarily make one expected amount incorrect, confirm the command fails, then restore it. You should be able to trace the whole chain: npm command, configured Node command, discovered file, assertion.
Check your understanding: changing the start script does not alter a discount rule. If a script is missing, inspect this project's configuration; if a module is missing, inspect its path. Keep setup errors separate from business failures. Keep using your own code; there is no archive to unpack.
A small server you can explain
Create server.js with the following development-only skeleton. The explicit file map is intentional: add every browser module you actually use to it. Unknown paths remain 404. Do not expose the project directory, tests or environment files.
import { createServer } from 'node:http';
import { readFile } from 'node:fs/promises';
const files = new Map([
['/', ['public/index.html', 'text/html; charset=utf-8']],
['/styles.css', ['public/styles.css', 'text/css; charset=utf-8']],
['/main.js', ['public/main.js', 'text/javascript; charset=utf-8']],
]);
const products = [
{ id: 'bag', name: 'Everyday bag', unitPriceCents: 15000 },
{ id: 'notebook', name: 'Notebook', unitPriceCents: 2000 },
];
createServer(async (request, response) => {
const url = new URL(request.url, 'http://127.0.0.1:4173');
const json = (status, body) => {
response.writeHead(status, { 'Content-Type': 'application/json' });
response.end(JSON.stringify(body));
};
try {
if (request.method === 'GET' && url.pathname === '/api/products') {
const mode = url.searchParams.get('mode');
if (mode === 'error') return json(503, { error: 'unavailable' });
if (mode === 'slow') {
await new Promise(resolve => setTimeout(resolve, 1200));
}
return json(200, { products: mode === 'empty' ? [] : products });
}
if (request.method !== 'GET') return json(405, { error: 'method_not_allowed' });
const file = files.get(url.pathname);
if (!file) return json(404, { error: 'not_found' });
const content = await readFile(new URL(file[0], import.meta.url));
response.writeHead(200, { 'Content-Type': file[1] });
response.end(content);
} catch {
json(500, { error: 'local_server_error' });
}
}).listen(4173, '127.0.0.1', () => {
console.log('Open http://127.0.0.1:4173');
});
Run npm start in this directory and visit the printed URL. Stop it with Ctrl+C. If the port is occupied, stop your previous preview server or change the port consistently. Opening index.html via file:// does not run this API.
For example, if main.js imports ./ui/render.js, add ['/ui/render.js', ['public/ui/render.js', 'text/javascript; charset=utf-8']] to the map. Do the same for every transitive import. A browser console module-loading error often means a missing entry or wrong relative path; inspect its request before changing business code.
Prove the service contract
Visit the normal, empty and error API URLs directly. Inspect their statuses in Network. Visit /api/products?mode=slow and observe the delay. Visit /missing: expect 404. Reload the page and check that all JS requests succeed with a JavaScript content type.
This loopback server is a lab, not a production deployment. It has no authentication, durable database or real order processing. Its deliberate controls let you reproduce failures instead of hoping a network problem occurs during your test.