Production-ready Node.js starter with cloud-native capabilities for building HMCTS digital services using Express.js, TypeScript and GOV.UK Design System.
This template provides everything you need to create accessible, secure, and scalable applications that meet GDS and HMCTS standards.
This monorepo will contain all your apps, libraries, and infrastructure for your HMCTS service.
- Team name: Your HMCTS service (e.g., CaTH, Divorce, Civil)
- Product name: The specific product/service (e.g., Possessions, Money-Claims)
- If the product encompasses the whole service, use "Service"
Examples:
- Team: CaTH, Product: Service →
cath-service - Team: Civil, Product: Money-Claims →
civil-money-claims
- Run the initialization script:
./.github/scripts/init.shThe script will:
- Prompt for your team name (e.g.,
CaTH) - Prompt for your product name (e.g.,
Service) - Replace all template values throughout the codebase
- Rebuild the yarn lockfile
- Run tests to verify everything works
- Remove itself after completion
- Review and commit:
git add .
git commit -m "Initialize from template"
git push- Health Checks: Configurable health endpoints with readiness and liveness probes for Kubernetes deployments
- Properties Volume: Secure configuration management through mounted volumes with automatic environment variable injection
- Azure Integration: Built-in support for Azure Key Vault secrets management and properties volume mounting
- Application Insights: Comprehensive monitoring with Azure Application Insights including custom metrics and distributed tracing
- GOV.UK Design System: Fully integrated GOV.UK Frontend with Nunjucks templates and automatic asset compilation
- Internationalization: Welsh language support with locale middleware and translation management system
- Security Headers: Pre-configured Helmet.js with CSP, HSTS, and nonce-based script protection
- Asset Pipeline: Vite-powered asset compilation with SCSS support and production optimization
- Cookie Management: Built-in support for cookie consent
- Session Handling: Session management using Redis or Postgres
A lightweight file-system router for Express applications, inspired by Next.js routing.
- File-based Routing: Maps files in directories to Express routes automatically
- Dynamic Parameters: Support for dynamic route segments using
[param]syntax (e.g.,/users/[id]) - HTTP Method Exports: Export handlers for any HTTP method (GET, POST, PUT, DELETE, etc.)
- Middleware Support: Single handlers or arrays of middleware for complex request pipelines
- Multiple Mount Points: Mount different directories with different URL prefixes
- Zero Dependencies: Lightweight implementation with no external dependencies
- Single repository for multiple applications (e.g. multiple frontends sharing common code, APIs or libraries)
- Workspace-based structure with Yarn workspaces
- Shared libraries for common functionality
- Testing with Vitest and Playwright
- Helm charts for Kubernetes deployment
- GitHub Actions CI/CD pipeline
- Biome for fast linting and formatting
expressjs-monorepo-template/
├── apps/ # Deployable applications
│ ├── api/ # REST API server (Express 5.x)
│ ├── crons/ # Cron jobs
│ ├── postgres/ # Migration runner + Prisma S
│ └── web/ # Web frontend (Express 5.x + Nunjucks)
├── libs/ # Modular packages (explicitly registered)
│ ├── cloud-native-platform/ # Cloud Native Platform features
│ ├── express-gov-uk-starter/ # GOV.UK Frontend integration
│ ├── postgres-prisma/ # Database client (Prisma)
│ ├── simple-router/ # Simple Router features
│ ├── footer-pages/ # Module with example footer pages
│ └── [your-module]/ # Your feature modules
│ └── src/
│ ├── pages/ # Page routes (imported in web app)
│ ├── routes/ # API routes (imported in API app)
│ ├── prisma/ # Prisma schema
│ ├── locales/ # Translations (loaded by govuk-starter)
│ └── assets/ # Module assets (compiled by vite)
├── e2e-tests/ # End-to-end tests (Playwright)
├── docs/ # Documentation and ADRs
├── helm/ # Helm charts for Kubernetes deployment
└── package.json # Root configuration
- Node.js 22+
- Yarn 4+
- Docker (optional, for PostgreSQL)
# Install dependencies
yarn install
# Run development server
yarn dev| Service | URL | Description |
|---|---|---|
| Web Application | http://localhost:3000 | Main web interface with GOV.UK styling |
| API Server | http://localhost:3001 | REST API backend |
| Prisma Studio | http://localhost:5555 | Database management UI |
# Development
yarn dev # Start all services concurrently
# Testing
yarn test # Run all tests across workspaces
yarn test:e2e # Playwright E2E tests
yarn test:coverage # Generate coverage report
# Code Quality
yarn lint:fix # Run Biome linter
yarn format # Format code with Biome
# Database Operations
yarn db:migrate # Apply migrations
yarn db:migrate:dev # Auto apply migrations, add new migrations if necessary
yarn db:generate # Generate the Prisma client
yarn db:studio # Open Prisma Studio
yarn db:drop # Drop all tables and reset the database- Create module structure:
mkdir -p libs/my-feature/src/pages # Page controllers and templates
mkdir -p libs/my-feature/src/locales # Translation files (optional)
mkdir -p libs/my-feature/src/assets/css # Module styles (optional)
mkdir -p libs/my-feature/src/assets/js # Module scripts (optional)
cd libs/my-feature- Initialize package.json:
{
"name": "@hmcts/my-feature",
"version": "1.0.0",
"type": "module",
"exports": {
".": {
"production": "./dist/index.js",
"default": "./src/index.ts"
},
"./config": {
"production": "./dist/config.js",
"default": "./src/config.ts"
}
},
"scripts": {
"build": "tsc && yarn build:nunjucks",
"build:nunjucks": "mkdir -p dist/pages && cd src/pages && find . -name '*.njk' -exec sh -c 'mkdir -p ../../dist/pages/$(dirname {}) && cp {} ../../dist/pages/{}' \\;",
"dev": "tsc --watch",
"test": "vitest run",
"test:watch": "vitest watch",
"format": "biome format --write .",
"lint": "biome check .",
"lint:fix": "biome check --write ."
},
"peerDependencies": {
"express": "^5.1.0"
}
}Note: The build:nunjucks script is required if your module contains Nunjucks templates.
- Create tsconfig.json:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true
},
"include": ["src/**/*"],
"exclude": ["**/*.test.ts", "**/*.spec.ts", "dist", "node_modules", "src/assets/"]
}- Register module in root tsconfig.json:
{
"compilerOptions": {
"paths": {
// ... existing paths ...
"@hmcts/my-feature": ["libs/my-feature/src"]
}
}
}- Create src/config.ts for module configuration:
import path from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Module configuration for app registration
export const pageRoutes = { path: path.join(__dirname, "pages") };
export const apiRoutes = { path: path.join(__dirname, "routes") };
export const prismaSchemas = path.join(__dirname, "../prisma");
export const assets = path.join(__dirname, "assets/");Create src/index.ts for business logic exports:
// Business logic exports only
export * from "./my-feature/service.js";
export * from "./my-feature/validation.js";IMPORTANT: Config exports (pageRoutes, apiRoutes, prismaSchemas, assets) must be in a separate config.ts file to avoid circular dependencies during Prisma client generation. Apps import config using the /config path (e.g., @hmcts/my-feature/config).
- Register module in applications:
// apps/web/src/app.ts
import { pageRoutes as myFeaturePages } from "@hmcts/my-feature/config";
app.use(await createGovukFrontend(app, [myFeaturePages.path], { /* options */ }));
app.use(await createSimpleRouter(myFeaturePages));
// apps/web/vite.config.ts
import { assets as myFeatureAssets } from "@hmcts/my-feature/config";
const baseConfig = createBaseViteConfig([
path.join(__dirname, "src"),
myFeatureAssets
]);
// apps/api/src/app.ts
import { apiRoutes as myFeatureRoutes } from "@hmcts/my-feature/config";
app.use(await createSimpleRouter(myFeatureRoutes));
// libs/postgres-prisma/src/schema-discovery.ts
import { prismaSchemas as myFeatureSchemas } from "@hmcts/my-feature/config";
const schemaPaths = [myFeatureSchemas, /* other schemas */];- Add dependency to relevant app package.json files:
"@hmcts/my-feature": "workspace:*"
| Type | Tool | Location | Purpose |
|---|---|---|---|
| Unit Tests | Vitest | Co-located *.test.ts |
Business logic validation |
| E2E Tests | Playwright | e2e-tests/ |
User journey validation |
| Accessibility Tests | Axe-core + Playwright | e2e-tests/ |
WCAG 2.1 AA compliance |
# Run specific test suites
yarn test # Unit tests
yarn test:e2e # E2E tests
yarn test:coverage # Coverage reportThe GitHub Action pipelines contain a number of security checks, including:
- Dependency Scanning: Automatically scans for vulnerabilities in dependencies
- SonarQube: SAST analysis for code quality and security
- Claude Security Scans: Claude AI-powered security scans for code vulnerabilities
MIT