Skip to main content

Features

Asset management

This project uses ESBuild for asset bundling and management, providing fast builds and efficient handling of JavaScript, TypeScript, and SCSS files.

Cache busting

Caching allows Express.js applications to store and serve frequently requested data efficiently, reducing the strain on servers and minimizing latency. This application improves caching through: - Intelligent browser caching for assets and static resources - Package management tools to optimize the caching process of installing, upgrading, configuring, and removing software dependencies

Form validation

This application implements form validation for user inputs. You can find further information on the validation library used by searching in the Express documentation

CSRF protection

The application uses the csrf-sync middleware to protect against cross-site request forgery attacks.

Content Security Policy (CSP)

This app uses helmet.js to help secure the application by setting HTTP response headers, including Content Security Policy.

Response compression

The app uses a Node.js compression middleware called compression. The middleware will attempt to compress response bodies for all request that traverse through the middleware, based on the given options.

Rate limiting

This application uses a basic rate-limiting middleware for Express.js called express-rate-limit. It is used to limit repeated requests to public APIs and endpoints such as password reset.

For further information please visit the documentation here.

Linter

ESLint is a static code analysis tool for identifying and fixing problems in JavaScript and TypeScript code. It helps maintain code quality and consistency across a project by enforcing a set of coding standards and best practices. ESLint can catch syntax errors, stylistic issues, and potential bugs before they become actual problems.

The project has TypeScript support through the @typescript-eslint/eslint-plugin and @typescript-eslint/parser packages installed as dev dependencies.

To run ESLint:

yarn lint

This will run ESLint on all TypeScript files in your project, ignoring specific files and directories.

Ignore Configuration

The project configures ESLint to ignore certain files directly in the eslint.config.js file:

{
  ignores: [
    'node_modules/*',
    'public/*',
    'tests/**/*.spec.ts'
  ],
}

This configuration: - Ignores the node_modules directory - Ignores the public directory (generated output) - Ignores all test specification files (*.spec.ts) in any subdirectory of the tests folder

Linter for staged commits

We use husky & lint-staged to run ESLint on all our staged git commits. This ensures that TypeScript files are linted before they’re committed to the repository.

  • husky - helps us with our Git hooks
  • lint-staged - helps us run a linter on our staged commits (configured in package.json to target both .js and .ts files)

To set-up locally:

  • Install all the dependencies:
  yarn install
  • Initialise husky:
  yarn husky install
  • To help debug, run the command when you have a staged commit:
  yarn lint-staged --debug

TypeScript

This project uses TypeScript to provide static type checking, improving code quality and developer experience. TypeScript helps catch errors during development rather than at runtime and provides better IDE support through enhanced autocompletion and navigation.

Main TypeScript Configuration

The TypeScript configuration is defined in tsconfig.json with the following key settings: - Target: ES2022 - Module System: NodeNext - Strict Type Checking: Enabled - Source Maps: Generated for debugging

Test TypeScript Configuration

The project uses a separate TypeScript configuration for tests in tsconfig.test.json, which extends the main configuration:

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "allowImportingTsExtensions": true,
    "noEmit": true,
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  },
  "include": ["tests/**/*.spec.ts", "routes/**/*.ts", "src/**/*.ts", "middleware/**/*.ts", "utils/**/*.ts"]
}

This configuration: - Extends the main tsconfig.json - Allows importing TypeScript files with extensions (.ts) - Doesn’t emit compiled output files when running tests - Includes all test files (*.spec.ts) in all test subdirectories - Includes source files from routes, src, middleware, and utils directories that tests may need to reference

To compile TypeScript files: shell yarn build:ts

To run type checking without emitting files: shell yarn tsc

Axios

This application uses axios with middleware-axios (configured as a utility in utils/axiosSetup.ts) for making HTTP requests to external APIs.

Below is an example of how to use the Axios middleware in your TypeScript routes to make server/API calls:

// routes/index.ts
import express, { Request, Response, NextFunction } from 'express';
const router = express.Router();

/* GET home page. */
router.get('/', (req: Request, res: Response, next: NextFunction) => {
  res.render('main/index', { title: 'Express' });
});

// Make an API call with `Axios` and `middleware-axios`
// GET users from external API
router.get('/users', async (req: Request, res: Response, next: NextFunction) => {
  try {
    // Use the wrapped Axios instance attached to the request object (via middleware-axios)
    const response = await req.axiosMiddleware.get('https://jsonplaceholder.typicode.com/users');
    res.json(response.data);
  } catch (error) {
    next(error);
  }
});

export default router;

Nunjucks templating

This project uses Nunjucks for server-side HTML templating. You can render Nunjucks templates from your TypeScript route handlers just as you would from JavaScript. Templates are located in the views/ directory and are compatible with both JS and TS backends.

Project structure and source directory

  • Project-specific TypeScript code should go in src/scripts/.
  • Other source TypeScript files are located in src/, middleware/, routes/, and utils/.
  • Compiled JavaScript output is placed in the public/ directory.
  • Do not edit files in public/ directly; always edit the .ts source files.

Import paths and path aliases

  • The project uses path aliases (see tsconfig.json), such as import foo from '#utils/foo'.
  • Ensure your editor/IDE is configured to recognize these aliases for best developer experience.

Running and debugging

  • The app is started using the compiled JS in public/ (see yarn dev and yarn start).
  • If you want to run TypeScript directly (e.g., for debugging), consider using ts-node or a similar tool, but this is not the default workflow.

Development workflow

The project uses ESBuild for fast compilation and bundling with watch mode for automatic rebuilds during development. The development workflow is managed through the yarn dev script which concurrently runs:

  1. TypeScript compilation in watch mode - Monitors TypeScript source files for changes
  2. ESBuild bundling in watch mode - Handles SCSS, JavaScript bundling, and asset copying
  3. Nodemon for server restarts - Automatically restarts the Express server when compiled files change

The watch system monitors:

  • TypeScript source files (src/**/*.ts) for compilation
  • SCSS files (src/scss/**/*.scss) for CSS bundling
  • Asset files from GOV.UK Frontend and MOJ Frontend packages
  • The compiled output in the public/ directory for server restarts

Nodemon configuration (nodemon.json):

{
  "watch": ["public"],
  "ext": "js,json",
  "ignore": ["public/assets/"],
  "delay": "500ms"
}

This configuration:

  • Watches the public directory for changes in compiled output
  • Only monitors changes in .js and .json files
  • Ignores the public/assets/ directory (managed by ESBuild)
  • Adds a 500ms delay before restarting to avoid excessive restarts during rapid file changes

The development workflow is started with:

yarn dev

This command builds the project initially and then sets up all watch processes for continuous development.

Type definitions

  • Type definitions for Node, Express, and other dependencies are included as dev dependencies (see @types/* packages in package.json).
  • These are required for type safety and improved autocompletion in TypeScript.

DevSecOps pre-commit hooks

This project uses the Ministry of Justice pre-commit hooks for scanning hardcoded secrets and credentials. More information can be found here.

The DevSecOps pre-commit hooks are a lightweight, Docker-based security scanner that integrate with Git workflows to prevent hardcoded secrets from being committed to your repository.

To set-up locally:

Sign into your Docker Desktop locally

Ensure prek is installed globally

curl --proto '=https' --tlsv1.2 \
-LsSf https://raw.githubusercontent.com/ministryofjustice/devsecops-hooks/e85ca6127808ef407bc1e8ff21efed0bbd32bb1a/prek/prek-installer.sh | sh

Close terminal

Activate, by executing the following command in the repository directory

prek install

Test

prek run
This page was last reviewed on 12 November 2025. It needs to be reviewed again on 24 December 2025 .