This guide explains how to install, configure, run, test, and adapt the web application template.
- Get started
- Configuration
- Routes and API example
- Browser app and offline behavior
- Browser validation
- Container
- GHCR publication and deployment
- Adapt the template
- Validate changes
- Troubleshooting
Install Node.js 26 or later. Install Docker only when you plan to run the container.
Run these commands from the repository root:
npm ci
npm run build
node bin/web-template.mjsOpen http://localhost:1020/ to view the app. The server listens on port 1020 by default.
The server reads optional values from the process environment and the .env file.
Copy .env.example to .env, then uncomment or edit its values.
| Name | Required | Default | Accepted values |
|---|---|---|---|
PORT |
No | 1020 |
An integer from 1 through 65535. |
LOG_LEVEL |
No | info |
error, warn, info, http, verbose, debug, or silly. |
The server rejects an invalid PORT value before it opens a connection.
The LOG_LEVEL value sets the minimum level for application log messages.
The package version, repository URLs, and release tags are metadata or deployment settings. They do not change the runtime server configuration.
The server serves the app page, built browser files, public assets, API example, and status routes.
| Method | Path | Result |
|---|---|---|
GET |
/ |
Serves public/index.html. |
GET |
/client.js |
Serves dist/client.js. |
GET |
/styles.css |
Serves public/styles.css. |
GET |
/service-worker.js |
Serves dist/service-worker.js. |
GET |
/manifest.webmanifest |
Serves app install metadata. |
GET |
/icons/template.svg |
Serves the general app icon. |
GET |
/icons/template-maskable.svg |
Serves the scalable maskable icon. |
GET |
/icons/template-192.png |
Serves the 192 by 192 pixel icon. |
GET |
/icons/template-512.png |
Serves the 512 by 512 pixel icon. |
GET |
/icons/template-maskable-512.png |
Serves the 512 by 512 pixel maskable icon. |
GET |
/screenshots/web-template-desktop.png |
Serves the desktop preview. |
GET |
/screenshots/web-template-mobile.png |
Serves the mobile preview. |
GET |
/health |
Returns {"status":"ok"}. |
GET |
/ready |
Returns {"status":"ready"}. |
POST |
/api/examples/:exampleId |
Validates and returns an API example. |
Unsupported methods on page and asset routes return HTTP 405. Unknown paths return HTTP 404.
The API example accepts a lowercase letter, digit, or hyphen in exampleId.
The ID must contain between 1 and 40 characters.
The JSON body requires name with 1 to 80 characters.
The optional description field accepts a maximum of 280 characters.
The optional source query value accepts 1 to 40 characters.
The route rejects unlisted fields and returns HTTP 400 for invalid input.
The route does not save data.
Send a valid request with curl:
curl -X POST 'http://localhost:1020/api/examples/example-123?source=template' \
-H 'content-type: application/json' \
-d '{"name":"Example item","description":"A sample API contract."}'The server returns this JSON response:
{
"id": "example-123",
"name": "Example item",
"description": "A sample API contract.",
"source": "template"
}The manifest describes the app name, start path, window mode, colors, icons, and preview images.
The browser worker saves the app shell after a visit that loads the app.
It also saves valid preview image responses.
The worker does not cache API, health, or readiness routes.
The worker removes only obsolete caches that use its web-template-shell- prefix.
The app shell contains /, /client.js, /styles.css, /manifest.webmanifest, and /icons/template.svg.
The other manifest icons and preview images load from public/ when requested.
API calls require a network connection because the worker does not save API responses.
To check offline behavior, open the app in a supported browser and wait until the worker controls the page. Reload the page after the worker becomes active. Set the browser network mode to offline, then reload the page again. The saved app shell loads. API calls fail without a network connection.
Install Chrome for Puppeteer once:
npm run puppeteerStart the server in one terminal. Run the screenshot or audit commands in a different terminal.
npm run screenshots
npm run lighthouseThe screenshot command saves desktop and mobile images under the ignored artifacts/ directory.
Set WEB_URL to change the server address.
Set SCREENSHOT_PATH to select a screenshot output file.
The Lighthouse command writes its report to artifacts/lighthouse.json.
It checks performance, best practices, SEO, and accessibility.
Puppeteer then checks the manifest, install icons, preview images, worker control, and offline page load.
The audit passes when each check passes and the Accessibility score is at least 95 out of 100.
These browser checks run outside npm test and CI.
Stop the server after browser validation.
Build and start the container with Docker Compose:
docker compose up --build -dOpen http://localhost:1020/ to view the app.
Compose publishes port 1020 on 0.0.0.0, so the container listens on all host network interfaces.
Use your host firewall and network rules to limit access when required.
Compose checks both /health and /ready every 30 seconds.
Stop the container with docker compose down.
You can also build an image with docker build -t web-template ..
The image uses Node.js 26 and runs the server as the unprivileged node user.
The GitHub workflow publishes ghcr.io/eliware/web-template when a version tag reaches the repository.
The tag must match the version in package.json, and the workflow validates the image before publication.
Configure the ghcr-publish GitHub environment and the package access policy before you publish an image.
Set public or private visibility and grant pull access to the intended users.
The workflow builds the linux/amd64 architecture.
Pull a published version by its immutable version tag:
docker pull ghcr.io/eliware/web-template:v15.0.1Use the version that you approved and published. Record the image SHA-256 digest from the completed workflow. Publication does not deploy or start the app. Use an approved GitOps change to deploy or restore an image by digest.
Update package.json with the derived app name, description, keywords, and repository URLs.
Update public/manifest.webmanifest with the app name, description, colors, and start path.
Replace the icon and preview image files under public/ with artwork from the derived app.
Keep each public asset path aligned with src/backend/route-resolver.mjs.
Add routes in src/backend/ and browser code in src/frontend/.
Add route tests under the matching path in tests/.
Update the route table and examples in this guide after route behavior changes.
Read AGENTS.md and the specifications index before changing repository rules.
Read release notes for changes in each version.
Run the aggregate checks before you commit:
npm testThe aggregate command checks tests, lint, formatting, dependencies, and the browser build. Run focused tests by passing one or more test file paths:
npm test -- tests/backend/config.test.mjs
npm test -- tests/backend/config.test.mjs tests/frontend/service/worker.test.mjsRun npm run lint, npm run audit, and npm run format:check to run individual checks.
Run npm run format to apply the repository formatter.
If the server rejects PORT, set an integer from 1 through 65535.
If the server cannot open its port, stop the process that uses it or set a different port.
If /client.js returns HTTP 404, run npm run build and confirm that dist/client.js exists.
If an API request returns HTTP 400, check its path, query, and JSON body against the schema above.
If offline loading fails, reload after the worker controls the page and cache the app shell.
If browser checks cannot start Chrome, run npm run puppeteer before the checks.
If Compose reports an unhealthy container, check the server logs and both health routes.