typescript 40 lines · 6 steps

Turning TypeORM lock errors into 409s in NestJS

A NestJS exception filter catches TypeORM optimistic-lock conflicts and returns a clean HTTP 409 Conflict response.

Explained by highlit
1import {
2 ArgumentsHost,
3 Catch,
4 ConflictException,
5 ExceptionFilter,
6 HttpStatus,
7 Logger,
8} from '@nestjs/common';
9import { HttpAdapterHost } from '@nestjs/core';
10import { OptimisticLockVersionMismatchError } from 'typeorm';
11 
12@Catch(OptimisticLockVersionMismatchError)
13export class OptimisticLockExceptionFilter implements ExceptionFilter {
14 private readonly logger = new Logger(OptimisticLockExceptionFilter.name);
15 
16 constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
17 
18 catch(exception: OptimisticLockVersionMismatchError, host: ArgumentsHost) {
19 const { httpAdapter } = this.httpAdapterHost;
20 const ctx = host.switchToHttp();
21 const request = ctx.getRequest();
22 
23 this.logger.warn(
24 `Optimistic lock conflict on ${request.method} ${request.url}: ${exception.message}`,
25 );
26 
27 const conflict = new ConflictException({
28 statusCode: HttpStatus.CONFLICT,
29 error: 'StaleVersion',
30 message:
31 'The resource was modified by another request. Reload and retry with the latest version.',
32 });
33 
34 httpAdapter.reply(
35 ctx.getResponse(),
36 conflict.getResponse(),
37 HttpStatus.CONFLICT,
38 );
39 }
40}
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1Exception filters let you translate low-level persistence errors into meaningful HTTP responses in one place.
  2. 2Optimistic locking surfaces concurrent writes as errors you should map to 409 Conflict, not 500.
  3. 3Using the HttpAdapter keeps response writing platform-agnostic across Express and Fastify.

Related explainers

typescript
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import localeFrExtra from '@angular/common/locales/extra/fr';
import localeDe from '@angular/common/locales/de';

Locale-aware bootstrapping in Angular

i18n localization dependency-injection
Intermediate 8 steps
typescript
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';
 

Validating env config at boot in NestJS

configuration schema-validation environment-variables
Intermediate 8 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { InjectRepository } from '@nestjs/typeorm';

A cache-aside country lookup in NestJS

cache-aside dependency-injection batch-lookup
Intermediate 8 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core';
 
interface Preferences {
  theme: 'light' | 'dark';

A signal-based preferences store in Angular

signals state-management persistence
Intermediate 7 steps
typescript
import { useEffect, useState } from "react";
 
interface Section {
  id: string;

Building a scroll-spy hook in React

custom-hooks intersectionobserver dom-observation
Intermediate 8 steps
typescript
import { Injectable, Scope, Inject, NotFoundException } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { Request } from 'express';
import { DataSource } from 'typeorm';

Per-tenant database connections in NestJS

multi-tenancy connection-pooling dependency-injection
Advanced 8 steps

Share this explainer

Here's the card — post it anywhere.

Turning TypeORM lock errors into 409s in NestJS — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code