Eliminating the parent property name from the validation message of a nested object

When using @ValidateNested() with the class-validator library, I encountered a formatting issue when validating a nested object:

// Object Schema:
export class CreateServerSettingsDTO {
  @IsNotEmpty({ message: 'Username is required' })
  username: string;

  @IsNotEmpty({ message: 'Password is required' })
  password: string;
}

export class CreateServerDTO {
  @IsNotEmpty({ message: 'Server name is required' })
  serverName: string;

  description?: string;

  @ValidateNested()
  @Type(() => CreateServerSettingsDTO)
  @IsObject({ message: 'Settings must be an object' })
  @IsNotEmpty({ message: 'Settings are required' })
  settings: CreateServerSettingsDTO;
}
// Validation response:
{
  "statusCode": 400,
  "message": [
    "settings.Username is required",
    "settings.Password is required"
  ],
  "error": "Bad Request"
}

I am looking to customize the validation messages for the nested object under settings. Specifically, I want the messages to display as:

{
  "statusCode": 400,
  "message": [
    "Username is required",
    "Password is required"
  ],
  "error": "Bad Request"
}

I have tried different approaches but could not find a way to remove the settings. prefix from the nested property messages. Any suggestions on how this can be accomplished?

Answer №1

The most effective technique I discovered

formatErrors.ts

import { ValidationPipe, BadRequestException } from '@nestjs/common';
import { ValidationError } from 'class-validator';

type IErrorMessage = Record<string, any>;

function formatErrorsHelper(errors: ValidationError[]): IErrorMessage[] {
  return errors.map((item): IErrorMessage => {
    const { property, constraints, children } = item;
    const result: IErrorMessage = {};

    if (constraints) {
      result[property] = Object.values(constraints);
    }

    if (Array.isArray(children) && children.length > 0) {
      result[property] = formatErrorsHelper(children);
    }

    return result;
  });
}

export const formatErrorPipe = new ValidationPipe({
  exceptionFactory: (errors: ValidationError[]) => {
    return new BadRequestException(formatErrorsHelper(errors));
  },
});

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
import { formatErrorPipe } from './helpers/formatErrors';

async function initialize() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(formatErrorPipe);

  await app.listen(3000);
}
initialize();

Answer №2

**handle-errors.ts**

import { ValidationPipe, BadRequestException } from '@nestjs/common';
import { ValidationError } from 'class-validator';

function handleErrors(errors: ValidationError[]): string[] {
  const errorMessages: string[] = [];

  function extractErrorMessage(error: ValidationError) {
    if (error.constraints) {
      errorMessages.push(...Object.values(error.constraints));
    }

    if (error.children && error.children.length > 0) {
      error.children.forEach(extractErrorMessage);
    }
  }

  errors.forEach(extractErrorMessage);

  return errorMessages;
}

export const formatErrorHandle = new ValidationPipe({
  whitelist: true,
  forbidNonWhitelisted: true,
  exceptionFactory: (errors: ValidationError[]) => {
    throw new BadRequestException(handleErrors(errors));
  },
});

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app/app.module';
import { formatErrorHandle } from './helpers/errorHandler';

async function startApp() {
  const app = await NestFactory.create(AppModule);

  app.useGlobalPipes(formatErrorHandle);

  await app.listen(3000);
}
startApp();

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

I'm diving into the world of Typescript and trying to figure out how to use tooltips for my d3 stacked bar chart. Any guidance on implementing mouseover effects in Typescript would be greatly

I am currently facing some issues with the code below and need guidance on how to proceed. I am new to this and unsure of how to call createtooltip. Any assistance would be greatly appreciated. The error message states that createtooltip is declared but n ...

Initiate the input change event manually

Struggling with creating a custom counter input component where the input value is controlled by custom increment/decrement buttons. Desired output: https://i.sstatic.net/oYl1g.png Content projection will be used to expose the input for form usage and a ...

I'm experiencing an issue with my website where it appears broken when I build it, but functions properly when I use npm run dev in Next

For my project, I have utilized NextJs, Tailwind, React, and Typescript. It is all set and ready to be hosted. After using "output: export" in next.config.js and running npm run build, the process was successful. However, when viewing my website locally, I ...

I am looking to dynamically print countries from an array in my code based on the selected option

I'm in need of some simple code assistance. I want to display a list of countries that correspond to a selected letter, but I'm struggling to do so dynamically at the moment. For example, if I select the letter A from a dropdown menu, I want the ...

Prisma : what is the best way to retrieve all elements that correspond to a list of IDs?

I'm currently implementing Prisma with NextJs. Within my API, I am sending a list of numbers that represent the ID's of objects in the database. For example, if I receive the list [1, 2, 12], I want to retrieve the objects where the ID is eithe ...

The storage does not reflect updates when using redux-persist with next-redux-wrapper in a Typescript project

I'm currently working on integrating redux-persist with next.js using next-redux-wrapper. However, I'm facing an issue where the storage is not updating and the state is lost during page refresh. Below is my store.ts file: import { createStore, ...

Converting Mat-Raised-Button into an HTML link for PDF in Angular 6 using Material Design Library

Greetings, I have a couple of interrelated inquiries: Upon clicking the code snippet below, why does <a mat-raised-button href="../pdfs/test.pdf"></a> change the URL (refer to image 4) instead of opening test.pdf in a new window? If I have a ...

Creation of Card Component with React Material-UI

I am facing difficulties in setting up the design for the card below. The media content is not loading and I cannot see any image on the card. Unfortunately, I am unable to share the original image due to company policies, so I have used a dummy image for ...

The typing library for Angular does not properly identify the JQueryStatic object

Encountered an issue with the Angular declaration file: Error TS2304: Cannot locate identifier 'JQueryStatic'. The typings for jQuery are installed and properly declare JQueryStatic as an interface. Looking for solutions to resolve this error. ...

Implementing ETag in Angular 2

Implementing the odata standard which utilizes ETag has presented a challenge for me, particularly with PATCH requests. Each PATCH request requires sending the ETag in the header as If-None-Match. A HTTP status of 200 indicates that the change was successf ...

Error! Element not found in cloned iframe #2460, promise unhandled

Can you help me troubleshoot this issue? This is the code I'm working with: const section = document.createElement("section"); const myHTMLCode = "<p>Greetings</p>"; section.insertAdjacentHTML("afterbegin", my ...

Next.js: Importing from a new endpoint triggers the code execution once more

Here is a simplified example I created for this specific question. Imagine I want to maintain a server-side state. components/dummy.ts console.log('initialize array') let number: number = 0 const incrementValue: () => number = () => numbe ...

After deploying to Heroku, cal-heatmap encounters errors despite functioning correctly in a local environment

I successfully implemented a cal-heatmap instance in my Angular 2 (angular-cli) project locally, but when I deployed the project to Heroku, I encountered some errors that prevent the cal-heatmap from displaying. https://i.stack.imgur.com/8gY90.png The er ...

Typescript error: Import statement not allowed here

Recently delving into the world of TypeScript, I encountered an issue when attempting to build for production. My first step was running tsc Although this step passed without any errors, I faced import errors when trying to execute the build file with ...

Can you please provide the Typescript type of a route map object in hookrouter?

Is there a way to replace the 'any' type in hookrouter? type RouteMap = Record<string, (props?: any) => JSX.Element>; Full Code import { useRoutes, usePath, } from 'hookrouter' //// HOW DO I REPLACE any??? type RouteMap = ...

Tips for removing a row from a DataGrid column with the click of a button

I am facing a challenge with my data table that contains users. I am trying to implement a delete button for each row, but it seems like the traditional React approach may not work in this situation. Currently, I am utilizing the DataGrid component in the ...

Tips on deactivating a div when a checkbox is selected

I am currently working with a checkbox element in my code: <md-checkbox checked.bind="addEventCommand.allDay" change.delegate="allday()">All Day</md-checkbox> When the above checkbox is true, I want to disable the following ...

MaterialUI Divider is designed to dynamically adjust based on the screen size. It appears horizontally on small screens and vertically on

I am currently using a MaterialUI divider that is set to be vertical on md and large screens. However, I want it to switch to being horizontal on xs and sm screens: <Divider orientation="vertical" variant="middle" flexItem /> I h ...

Ways to expand the DOM Node type to include additional attributes

I've been diving into typescript and transitioning my current code to use it. In the code snippet below, my goal is: Retrieve selection Get anchorNode from selection -> which is of type 'Node' If anchorNode has attributes, retrieve attr ...

Is it possible to generate a property for an interface by casting a key within a for-in loop?

When I attempt to set a property on an object with a value from a dynamically generated form, I utilize a for-in loop to identify a property in the object and assign it. FormFeatureArray.forEach((el) => { // form handling stuff omitted For(c ...