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 hookslint-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/, andutils/. - Compiled JavaScript output is placed in the
public/directory. - Do not edit files in
public/directly; always edit the.tssource files.
Import paths and path aliases
- The project uses path aliases (see
tsconfig.json), such asimport 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/(seeyarn devandyarn start). - If you want to run TypeScript directly (e.g., for debugging), consider using
ts-nodeor 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:
- TypeScript compilation in watch mode - Monitors TypeScript source files for changes
- ESBuild bundling in watch mode - Handles SCSS, JavaScript bundling, and asset copying
- 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
publicdirectory for changes in compiled output - Only monitors changes in
.jsand.jsonfiles - 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 inpackage.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