Shopfront guides

Shopfront Guide

All guides
Part 1ProductWhat the app is, who uses it, and what it does.

Collapsed

Shopfront is the storefront for Acme's online shop. Shoppers browse products and track orders; staff use the admin screens to manage the catalog. Sources: README.md, client/src/routes.jsx, server/index.js.

Who uses it

  • Shoppers: browse the catalog, place orders, and look up an order by id
  • Staff: add products through the admin catalog editor (behind the newAdmin flag)

What it does

  • Lists products and product details from PostgreSQL
  • Creates orders from a cart and rejects empty carts
  • Lets staff create products through /api/admin

Where it runs

  • Web client: a React single-page app built with Vite (client/)
  • API: one Express server on port 4000, shipped as a Docker image (Dockerfile)
  • Database: PostgreSQL, reached through DATABASE_URL

Collapsed

What people can do in Shopfront, by area. flag marks features behind a feature flag. Routes match the Site map.

FeatureWhat it isRouteCodeRollout
Shopping
HomeLanding page of the storefront./client/src/pages/Home.jsx—
Product catalogLists every product; the API also serves one product by id./products · GET /api/products[/:id]client/src/pages/Products.jsx, server/routes/products.js—
Place an orderValidates the cart, stores the order, and returns its id. An empty cart gets a 400.POST /api/ordersserver/routes/orders.js (createOrder)—
Order statusShows one order by id./orders/:id · GET /api/orders/:idclient/src/pages/Order.jsx, server/routes/orders.js—
Staff
Admin catalog editorStaff screen for managing products. The create endpoint currently echoes the request body back and doesn't write to the database./admin · POST /api/admin/productsclient/src/pages/Admin.jsx, server/routes/products.jsnewAdmin

Collapsed

Every route in the app and how you reach it. Sources: client/src/routes.jsx, server/index.js.

RouteOpensShown when
/HomeAlways
/productsProduct catalogAlways
/orders/:idOrder status (linked or typed by id; there's no nav menu)Always
/adminAdmin catalog editorOnly when VITE_FLAG_NEW_ADMIN=true at build time; otherwise the route doesn't exist

/ Home
  • /products product catalog
  • /orders/:id one order
  • /admin staff catalog editor · newAdmin
/api Express API (server/index.js)
  • GET /api/products · GET /api/products/:id
  • POST /api/orders · GET /api/orders/:id
  • POST /api/admin/products staff only (requireStaff)
Part 2EngineeringHow it is built: architecture, stack, data, and code layout.

Collapsed

A React client calls one Express API, which reads and writes PostgreSQL (follow one request through it in the Express deep dive and the PostgreSQL guide). GitHub Actions tests every push and deploys main as a Docker image.

Client / UIEdge & messagingCompute & app codeData storeTooling & CISynchronous callAsync / event / queued
Web clientReact 18 · Vite 5client/srcSSO proxyidentifies staffoutside this repoAPI serverNode 20 · Express 4server/index.jsPostgreSQLproducts · ordersDATABASE_URLGitHub Actionslint · jestdeploy on mainDocker imagenode:20-slimDockerfileUSERS & CLIENTSPLATFORMDELIVERY/api/*SQL/api/adminbuild

Staff access: /api/admin is guarded by requireStaff in server/auth.js, which only checks that an x-staff-role header is present. The comment above it says a company SSO proxy sets a signed session cookie, but the code doesn't read a cookie. Confirm which the proxy actually sends.

Collapsed

What Shopfront is built with, by layer. Versions come from package.json (ranges, no lockfile), the Dockerfile, and .github/workflows/ci.yml.

TechnologyWhat it does hereWhere
Frontend
React ^18.3.1UI for every pageclient/src/pages/
React Router ^6.23.0Client routes via createBrowserRouterclient/src/routes.jsx
Vite ^5.2.0Dev server (:5173) and production build; injects VITE_* flagsclient/
Backend
Node.js 20Runtime in the production imageDockerfile
Express ^4.19.2HTTP API and routing. New to it? Read the Express deep dive.server/
Data & storage
PostgreSQLProducts and orders tables. New to it? Read the PostgreSQL guide.server/routes/
node-postgres (pg) ^8.11.0Connection pool from DATABASE_URLserver/db.js
Tooling & CI
Jest ^29.7.0 + Supertest ^7.0.0API tests against the Express app in memorytest/
ESLint ^9.0.0Linting, run in CIpackage.json
concurrently ^8.2.0Runs API and client together in npm run devpackage.json
GitHub ActionsLint, test, and deploy pipeline.github/workflows/ci.yml
DockerProduction image: installs, builds the client, starts the APIDockerfile
Part 3OperationsWhere it runs, how changes ship, and how we watch it.

Collapsed

Where Shopfront runs and how a change gets to production. Sources: .github/workflows/ci.yml, Dockerfile.

EnvironmentURL / projectData and who uses it
Locallocalhost:5173 (client), localhost:4000 (API)Your own PostgreSQL via DATABASE_URL
StagingNot in the repoThe deploy step mentions a staging registry; the URL and data aren't defined in code
ProductionNot in the repoPromoted from staging by tag, according to the deploy step

#StepWhat happens and what can stop it
1Push any branchThe test job runs npm ci, npm run lint, and npm test. A failure blocks deploy.
2Merge to mainThe deploy job runs only on refs/heads/main and only after test passes.
3Build and push the imagePlaceholder: today the step only echos “docker build + push to the staging registry, then promote to production by tag”. The real deploy happens outside this repo.
Part 4Working processHow engineers work in the repo, from setup to a merged PR.

Collapsed

How to get Shopfront running locally and the day-to-day loop. Every command here was checked against the repo's scripts.

StepCommand / notes
Install Node 20Matches FROM node:20-slim in the Dockerfile
Install dependenciesnpm install
Point at a databaseSet DATABASE_URL to a local PostgreSQL with products and orders tables. There are no migrations or seeds in the repo.

ShellFrom a fresh clone
npm install
export DATABASE_URL=postgres://localhost:5432/shopfront   # your local database
npm run dev        # API on :4000, client on :5173

TaskCommand
Start the appnpm run dev (API on :4000, client on :5173)
Turn on the admin screenVITE_FLAG_NEW_ADMIN=true npm run dev
Lintnpm run lint
Testnpm test

Collapsed

Which kind of test to write, where it goes, and how to run it. Sources: test/, package.json, .github/workflows/ci.yml.

LayerToolGood forRun one
APIJest + SupertestRequest validation and responses. Imports the Express app without starting a server (test/orders.test.js).npx jest test/orders.test.js
ClientNone yetNo component or end-to-end tests exist.—

CI runs lint and the full Jest suite on every push.

The one API test today: it imports the app and sends a request through Supertest, so no server or database is needed for this case.

JavaScripttest/orders.test.js
const request = require('supertest');
const app = require('../server');
test('rejects an empty cart', async () => {
  await request(app).post('/api/orders').send({ items: [] }).expect(400);
});
Part 5TopicsDeep dives into the technologies Shopfront is built on.
TopicExpressWhat Express is and how Shopfront uses it.

Collapsed

Express is a minimal web framework for Node.js: it routes HTTP requests to your functions and gives you helpers for reading requests and sending responses. Shopfront's whole API (/api/…) is one Express 4 app. Sources: expressjs.com, Application (4.x API), server/index.js.

In one paragraph

  • An Express app is a request handler for Node's HTTP server. You build it from a stack of middleware functions that run in order.
  • Each middleware gets the request and response, and either answers or calls next() to pass the request on (Using middleware).
  • Routers group endpoints under a path prefix, so each resource gets its own file.

The problem it solves

  • Plain Node http hands you raw requests. Express adds URL routing with parameters, body parsing, and helpers like res.status(201).json(…).
  • It stays unopinionated: no ORM, no project layout, so apps choose their own (Shopfront uses pg directly).

Compared with

  • Node's http module: what Express wraps; app.listen() creates the server for you (docs).
  • Express 5: the current major. Same model, but async errors are handled for you and route-path syntax is stricter (Migrating to 5). Shopfront is on 4.

How Shopfront uses it

  • One app with JSON parsing, three routers (products, orders, admin), one auth middleware, and Postgres behind it (usage map).
  • The React client doesn't call it yet; tests do, through supertest. The repo doesn't record why Express was chosen.

Collapsed

The ideas you need before reading Shopfront's server code, then each one in code. Links and examples are for Express 4, the major version Shopfront's ^4.19.2 range installs (see Versions).

ConceptWhat it meansIn ShopfrontOfficial docs
Handling a request
MiddlewareA function (req, res, next) that either ends the request (sends a response) or calls next(). Doing neither leaves the request hanging. They run in the order registered.express.json(), requireStaffUsing middleware
app.use vs app.get/postapp.use(path, fn) runs for any method and matches the path as a prefix; get/post match one method on that path.server/index.js:7-10Application
Routerexpress.Router() is a mini-app with its own routes, mounted at a prefix with app.use.One per resource in server/routes/Routing
Route parameters:id in a path captures that segment into req.params.id, always a string. Query strings go to req.query./api/orders/:idRouting
Request & response
express.json()Parses JSON bodies (Content-Type application/json, up to 100kb) into req.body. Without a parser, req.body is undefined. Invalid JSON becomes a 400 error.server/index.js:7express.json
Responsesres.status(code) chains with res.json(body); res.sendStatus(code) sends just the status.Every handlerResponse
Errors & testing
Error handlingError middleware has four arguments, (err, req, res, next), and goes last. In Express 4, errors from async handlers aren't passed on automatically: catch them and call next(err).Not used (see gotchas)Error handling
Testing with supertestPass the exported app to request(app) to send real HTTP requests without starting a server yourself.test/orders.test.jssupertest
Calling it: fetchBrowsers call a JSON API with fetch. It only rejects on network errors, so check response.ok for 4xx/5xx.Not used by the client yetMDN: Using Fetch

Short examples adapted from the Express 4 guides, MDN, and the supertest README, using placeholder values. Compare each with the Shopfront file named under it.

Create an app, parse JSON, mount a router. Shopfront does this in server/index.js.

JavaScriptExample (Express 4 docs)
const express = require('express');
const app = express();
app.use(express.json());                 // req.body for JSON requests

const items = express.Router();
app.use('/api/items', items);            // items' routes live under /api/items

module.exports = app;                    // export for tests; listen elsewhere

Routes with a parameter and JSON responses. Compare server/routes/orders.js.

JavaScriptExample (Express 4 docs)
items.get('/:id', (req, res) => {
  res.json({ id: req.params.id, q: req.query.q });   // params are strings
});

items.post('/', (req, res) => {
  if (!req.body.name) return res.status(400).json({ error: 'name required' });
  res.status(201).json({ name: req.body.name });
});

Middleware that allows or rejects. Compare server/auth.js, which checks only that a header exists.

JavaScriptExample (Express 4 docs)
function requireKey(req, res, next) {
  if (req.get('X-Example-Key') !== '<expected-value>') {
    return res.sendStatus(403);          // stop here
  }
  next();                                // pass to the next handler
}
app.use('/api/private', requireKey, privateRouter);

Async errors in Express 4. What Shopfront's handlers are missing.

JavaScriptExample (Express 4 docs)
items.get('/:id/details', async (req, res, next) => {
  try {
    const item = await loadItem(req.params.id);  // your async lookup
    res.json(item);
  } catch (err) {
    next(err);                           // Express 4 won't do this for you
  }
});

app.use((err, req, res, next) => {     // four arguments, registered last
  res.status(err.status || 500).json({ error: 'something went wrong' });
});

Make an API call from the browser. How a Shopfront page would place an order.

JavaScriptExample (MDN: Using Fetch)
const res = await fetch('/api/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },   // needed for express.json()
  body: JSON.stringify({ items: [{ productId: 42, qty: 1 }] }),
});
if (!res.ok) throw new Error(`Order failed: ${res.status}`);  // fetch doesn't throw on 4xx/5xx
const { id } = await res.json();

Make an API call from the terminal. Against the local server only.

ShellExample (curl)
curl http://localhost:4000/api/products/42
curl -X POST http://localhost:4000/api/orders \
  -H 'Content-Type: application/json' \
  -d '{"items": [{"productId": 42, "qty": 1}]}'

Test an endpoint without starting a server. Shopfront's test/orders.test.js follows this pattern.

JavaScriptExample (supertest README)
const request = require('supertest');
const app = require('../server');

test('GET an item', async () => {
  const res = await request(app).get('/api/items/42');
  expect(res.status).toBe(200);
  expect(res.body.id).toBe('42');
});

Collapsed

Express is Shopfront's whole API server: one app, three routers, one auth middleware, and Postgres behind it, in about 6 tracked files under server/ and test/ (git ls-files server test | wc -l). The React client doesn't call the API yet. Sources: server/index.js, server/routes/, package.json.

Client / UIEdge & messagingCompute & app codeData storeSynchronous call
Clientfetch / curlPOST /api/orders1express.json()parses req.bodyindex.js:72Orders router/api/ordersindex.js:93createOrdervalidates, insertsorders.js:4-84Postgresdb.query()server/db.js5requireStaff/api/admin onlyserver/auth.jsAdmin routerPOST /productsproducts.js:5-6CALLEREXPRESS APP (server/)DATASQL/api/admin

A request passes through the middleware in the order server/index.js registers it: JSON parsing, then the router whose prefix matches, then the handler. /api/admin requests go through requireStaff first. The guided walkthrough follows steps 1–5.

EndpointWhat it doesWhere (repo path)
Public
GET /api/productsEvery product row, as a JSON array.server/routes/products.js:3
GET /api/products/:idOne product by id; responds 200 with no product if the id doesn't exist.server/routes/products.js:4
POST /api/ordersValidates items (400 empty cart), inserts an order, returns 201 { id }.server/routes/orders.js:4-9
GET /api/orders/:idOne order by id; same no-row behavior as products.server/routes/orders.js:10
Staff (behind requireStaff)
POST /api/admin/productsEchoes the request body back with 201. Nothing is saved yet.server/routes/products.js:5-6, server/index.js:10

PieceRoleWhere
Server
AppCreates the app, registers middleware and routers, listens on PORT or 4000 only when run directly, and exports app for tests.server/index.js
express.json()Parses JSON request bodies into req.body for every route.server/index.js:7
Routersexpress.Router() per resource, mounted at a prefix with app.use.server/routes/products.js, server/routes/orders.js
requireStaffMiddleware: calls next() when the x-staff-role header is present, otherwise 403.server/auth.js
Data & tests
dbA shared pg connection pool from DATABASE_URL; handlers call db.query with $1 placeholders.server/db.js
supertestSends requests to the exported app in Jest tests, without starting a server.test/orders.test.js
expressRange ^4.19.2, no lockfile (see Versions).package.json:10

Collapsed

Follow one request, POST /api/orders, from the caller to the database and back. It touches every Express idea Shopfront uses. Sources: server/index.js, server/routes/orders.js, server/db.js.

Nothing in Shopfront's client calls the API yet, so the caller today is a test or a tool like curl. A browser client would send the same request with fetch:

HTTPThe request
POST /api/orders HTTP/1.1
Content-Type: application/json

{"items": [{"productId": 42, "qty": 1}]}

Express runs middleware in the order it was registered. The first one, express.json(), reads the JSON body and puts it on req.body. Without it, req.body would be undefined. Concept: middleware.

JavaScriptserver/index.js:6-10
const app = express();
app.use(express.json());
app.use('/api/products', products);
app.use('/api/orders', orders);
app.use('/api/admin', requireStaff, products.admin);

/api/orders matches the orders router's prefix, so Express hands it the rest of the path (/). Inside the router, router.post('/', …) matches the method and path. Concept: routers.

JavaScriptserver/routes/orders.js:9-10
router.post('/', createOrder);
router.get('/:id', async (req, res) => res.json((await db.query('select * from orders where id=$1', [req.params.id])).rows[0]));

createOrder rejects an empty or missing cart with 400, before touching the database. Otherwise it inserts the order and responds 201 with the new id. Concept: request and response.

JavaScriptserver/routes/orders.js:4-8
async function createOrder(req, res) {
  if (!Array.isArray(req.body.items) || !req.body.items.length) return res.status(400).json({ error: 'empty cart' });
  const { rows } = await db.query('insert into orders(items) values ($1) returning id', [JSON.stringify(req.body.items)]);
  res.status(201).json({ id: rows[0].id });
}

db.query runs on a shared pg pool. Values go in as $1 parameters, never pasted into the SQL string. Where the flow stops today: the repo has no schema or migrations for the orders table, and the handler has no try/catch, so a database error crashes the server process instead of returning a 500 (see Patterns & gotchas). The PostgreSQL walkthrough picks up from here.

JavaScriptserver/db.js
const { Pool } = require('pg');
module.exports = new Pool({ connectionString: process.env.DATABASE_URL });

Steps 1–4 run without a database, because the empty-cart check returns before any query. The existing test does exactly that:

ShellLocal only
npm test   # runs test/orders.test.js: POST /api/orders with an empty cart → 400

npm run dev   # API on :4000
curl -i -X POST http://localhost:4000/api/orders \
  -H 'Content-Type: application/json' -d '{"items": []}'   # → 400 {"error":"empty cart"}

A non-empty cart reaches step 5, which needs a local Postgres in DATABASE_URL with an orders table. The repo doesn't define that table, so that part can't be run from the repo as it stands.

Collapsed

Shopfront's server is a small, conventional Express 4 app. The gotchas are mostly what it leaves out: error handling, 404s, and a real staff check. Sources: server/index.js, server/auth.js, server/routes/.

Conventions here

  • One router per resource in server/routes/, mounted under /api/<resource> in server/index.js.
  • Export the app, listen only when run directly (require.main === module), so tests can import it.
  • Parameterized SQL: $1 placeholders with a values array, never string concatenation.
  • Errors as JSON: res.status(4xx).json({ error: '…' }).
  • Gate a group of routes by passing middleware to app.use before the router (server/index.js:10).

Gotchas

  • A failed query crashes the server. Every handler is async with no try/catch. In Express 4 a rejected promise isn't passed to next; it's an unhandled rejection, which crashes the process on current Node versions (the Dockerfile uses node:20-slim). See error handling.
  • Stack traces reach clients. No (err, req, res, next) handler is registered and NODE_ENV isn't set anywhere in the repo, so Express's default handler sends the stack trace, e.g. for a request with invalid JSON (a 400 from express.json()).
  • Missing rows return 200. GET /:id sends rows[0] even when it's undefined, instead of a 404.
  • The staff check trusts a header. requireStaff accepts any request with an x-staff-role header, whatever its value. Its comment says staff come from a signed SSO cookie (server/auth.js:1-3). Safe only if a proxy always strips or sets that header; reported as a repo problem.
  • The admin endpoint is a stub: it echoes the body without saving (server/routes/products.js:6).

Collapsed

The API changes you're most likely to make in Shopfront, and how to test each one with Jest and supertest (npm test). Sources: server/, test/orders.test.js, package.json.

TaskStepsWhere
Add an endpoint1. Add router.get/post(…) to the resource's router. 2. Validate input and return res.status(…).json(…). 3. Add a supertest test.server/routes/, test/
Add a resource1. Create server/routes/<name>.js exporting a router. 2. Mount it in server/index.js with app.use('/api/<name>', …).server/index.js:8-10
Protect an endpointMount it behind requireStaff like /api/admin, or pass the middleware to one route: router.post('/', requireStaff, handler).server/auth.js
Handle async errorsWrap the handler body in try/catch and call next(err), then register one error-handling middleware last in server/index.js.server/index.js
Return 404 for a missing rowCheck rows[0] and return res.sendStatus(404) (or a JSON error) when it's missing.server/routes/orders.js:10

A cancel endpoint in the orders router, written the way the rest of the file is, plus the error handling it currently lacks:

JavaScriptserver/routes/orders.js (example change)
router.post('/:id/cancel', async (req, res, next) => {
  try {
    const { rows } = await db.query('update orders set status = $2 where id = $1 returning id', [req.params.id, 'cancelled']);
    if (!rows[0]) return res.status(404).json({ error: 'no such order' });
    res.json({ id: rows[0].id, status: 'cancelled' });
  } catch (err) {
    next(err); // Express 4 won't do this for you
  }
});

And its test, in the style of test/orders.test.js:

JavaScripttest/orders.test.js (example)
test('cancel needs a real order', async () => {
  await request(app).post('/api/orders/999/cancel').expect(404);
});

The status column is illustrative: the repo has no schema, so check the real table first. The test needs a database, unlike the existing empty-cart test.

Collapsed

Shopfront allows any Express 4.x from 4.19.2, and with no lockfile a fresh install gets the newest 4.x, so nobody can tell from the repo which version a given environment runs. Express 5 is the current major. Checked 2026-10-13. Sources: package.json:10 (no lockfile committed).

LineIn ShopfrontLatestWhat changed that matters here
express 4^4.19.2; a fresh install resolves to 4.22.34.22.3, 2026-09-14 (release)Security fixes after 4.19.2: 4.20.0 CVE-2024-43796, XSS via res.redirect() (advisory); 4.21.1 a cookie fix; 4.22.3 CVE-2026-4867 in path matching. Shopfront doesn't redirect, but it should still be on ≥ 4.22.3. 4.20.0 also changed some defaults (urlencoded depth, redirect body), none used here (4.x history).
express 5—5.3.0, 2026-10-09 (release)Node 18+. Rejected promises go to next automatically, which would fix Shopfront's crash-on-query-error. Stricter path syntax (named wildcards, no regex characters in strings), req.query read-only, several old methods removed (Migrating to 5).

1. Commit a lockfile so the version is reproducible. 2. Move to the latest 4.x. 3. Read the migration guide: Shopfront's routes use plain :id paths, so the path-syntax changes shouldn't bite. 4. Try the official codemod, then run the tests:

ShellFrom the migration guide
npx codemod@latest @expressjs/v5-migration-recipe
npm install express@5 && npm test

Everywhere else, this guide describes Express 4, the version Shopfront runs.

Collapsed

Where to go next, in reading order. Express links are the 4.x pages (the unversioned ones now show v5). Links checked 2026-10-13.

ResourceWhat it coversChecked
Express 4 guides
RoutingRoute paths and parameters, routers mounted at a prefix.2026-10-13
Using middlewareThe stack, order, next(), router-level middleware.2026-10-13
Error handlingError middleware, async handlers in v4, the default handler.2026-10-13
Reference
Express 4.x APIapp, req, res, express.json.2026-10-13
body-parser 1.xJSON parser options and error types (400/413/415).2026-10-13
supertestTesting an exported app.2026-10-13
MDN: Using FetchCalling the API from the browser.2026-10-13
Versions & upgrading
Migrating to Express 5Breaking changes and the codemod.2026-10-13
ReleasesRelease notes for every version.2026-10-13
Security advisoriesCVEs and patched versions.2026-10-13
Part 6ReferenceQuick lookups: commands and terms.

Collapsed

Common tasks and the command for each, from package.json.

I want to…Run
Run API and clientnpm run dev
Build the clientnpm run build
Run testsnpm test
Lintnpm run lint

Shellpackage.json scripts
npm run dev     # API + client, with hot reload
npm test        # Jest + Supertest
npm run lint    # ESLint
npm run build   # production client bundle

Collapsed

Across the guide, the first mention of a term in each section is underlined: hover it for the definition, or click to jump here.

TermMeaning (as used in Shopfront)Where it shows up (repo path)
Domain
ShopperA customer browsing the storefront and placing orders.client/src/pages/
StaffAcme employees who manage the catalog; identified by the x-staff-role header.server/auth.js
CartThe items array posted to create an order. Must be non-empty.server/routes/orders.js
Code & config
requireStaffExpress middleware that returns 403 unless the staff header is present. Guards /api/admin.server/auth.js
createOrderHandler for POST /api/orders: validates the cart, inserts the row, returns the id.server/routes/orders.js
newAdmin / VITE_FLAG_NEW_ADMINFeature flag that adds the /admin route. Set with VITE_FLAG_NEW_ADMIN at build time.client/src/flags.js
DATABASE_URLPostgreSQL connection string read by the pool.server/db.js
SSO proxy / SSO (single sign-on)Company proxy in front of the app that, per a code comment, identifies staff.server/auth.js
React Router / createBrowserRouterThe client-side routing library: createBrowserRouter maps URLs to page components. Not the same as an Express router.client/src/routes.jsx
ViteThe client's dev server (port 5173) and production bundler. It inlines VITE_* variables at build time, which is why /admin only exists in builds made with the flag on.package.json, client/src/flags.js
Express
ExpressThe Node.js web framework Shopfront's API is built on: one app, routers per resource, and middleware. See the Express deep dive.server/index.js
app.useRegisters middleware or mounts a router at a path prefix; matches any HTTP method, and runs in the order registered.server/index.js:7-10
MiddlewareA (req, res, next) function in the request pipeline that responds or passes the request on.server/index.js:7-10
Router (as in express.Router)A group of routes mounted at a path prefix.server/routes/
express.jsonMiddleware that parses JSON request bodies into req.body.server/index.js:7
Error-handling middleware / Error middlewareA four-argument (err, req, res, next) function that turns errors into responses. Not used in Shopfront.docs
Unhandled rejectionA rejected promise nobody catches; on current Node versions it crashes the process.docs
supertestTest library that sends HTTP requests to an Express app in-process.test/orders.test.js
fetchThe browser API for HTTP requests; check response.ok for errors.MDN
pg / node-postgres / PoolThe Postgres client; one shared pool from DATABASE_URL, used through db.query with $1 placeholders.server/db.js
PostgreSQL
PostgreSQL / PostgresThe relational database that stores Shopfront's products and orders. See the PostgreSQL guide.server/db.js
Parameterized query / Parameterized SQLSQL with $1, $2… placeholders and a separate values array, so values are never read as SQL. Every Shopfront query is one.server/routes/orders.js:6
RETURNINGSQL clause that makes an INSERT or UPDATE send back columns of the rows it changed, such as a generated id.server/routes/orders.js:6