# Building a REST API with NestJS and Prisma: Input Validation & Transformation URL: https://www.prisma.io/blog/nestjs-prisma-validation-7D056s1kOla1 Published: 2022-07-19 Updated: 2026-07-08 Authors: Tasin Ishmam Tags: education Series: Building a REST API with NestJS, PostgreSQL, Swagger and Prisma Description: Learn how to add input validation and transformation to a NestJS REST API with ValidationPipe, class-validator and ParseIntPipe. Fully updated for Prisma ORM 7 and NestJS 11. > Blog posts describe Prisma as it was on the date shown and are not kept current. Before implementing from a post, check the documentation index at https://www.prisma.io/docs/llms.txt and the changelog at https://www.prisma.io/changelog.md. > For an index of every published post, fetch https://www.prisma.io/blog/llms.txt.
8 min read
Input validation in NestJS is handled by the built-in `ValidationPipe`: you declare rules with `class-validator` decorators on your DTO classes, register the pipe globally, and every incoming request body is checked before it reaches your route handlers. This tutorial is the second part of a five-part series on building a REST API with NestJS and Prisma ORM. In it, you will add validation and transformation to the Median blog API: reject malformed input, strip unknown fields with the `whitelist` option, and convert URL path parameters from strings to numbers with `ParseIntPipe`. > **Updated (July 2026):** This tutorial has been fully revised for **Prisma ORM 7** and **NestJS 11**, and continues directly from the updated [first part](https://www.prisma.io/blog/nestjs-prisma-rest-api-7D056s1BmOL0) of the series. Every command and code block below was run end-to-end against `prisma@7.8`, `@prisma/client@7.8` and `@nestjs/core@11`, with the database on [Prisma Postgres](https://www.prisma.io/docs/postgres). ## Introduction [#introduction] In the [first part](https://www.prisma.io/blog/nestjs-prisma-rest-api-7D056s1BmOL0) of this series, you created a new NestJS project and integrated it with Prisma ORM, PostgreSQL and Swagger. Then, you built a rudimentary REST API for the backend of a blog application called "Median". In this part, you will learn how to validate the input, so it conforms to your API specifications. Input validation is performed to ensure only properly formed data from the client passes through your API. It is best practice to validate the correctness of any data sent into a web application. This can help prevent malformed data and abuse of your API. You will also learn how to perform input transformation. Input transformation is a technique that allows you to intercept and transform data sent from the client before being processed by the route handler for that request. This is useful for converting data to appropriate types, applying default values to missing fields, sanitizing input, etc. ### Development environment [#development-environment] To follow along with this tutorial, you will be expected to: * ... have [Node.js](https://nodejs.org) (v18 or higher) installed. * ... have a PostgreSQL database. The easiest option is Prisma Postgres; you can also run Postgres locally with [Docker](https://www.docker.com/) or a native install. * ... have the [Prisma VSCode Extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) installed. *(optional)* * ... have access to a Unix shell (like the terminal/shell in Linux and macOS) to run the commands provided in this series. *(optional)* > **Note 1**: The optional Prisma VSCode extension adds IntelliSense and syntax highlighting for Prisma. > **Note 2**: If you don't have a Unix shell (for example, you are on a Windows machine), you can still follow along, but the shell commands may need to be modified for your machine. ### Set up the project [#set-up-the-project] The starting point for this tutorial is the ending of [part one](https://www.prisma.io/blog/nestjs-prisma-rest-api-7D056s1BmOL0) of this series: a rudimentary REST API built with NestJS 11 and Prisma ORM 7, with a working database connection and seed data. If you haven't completed the first tutorial yet, work through it first; it takes about 20 minutes and every step in this article builds directly on it. Before continuing, make sure the project runs: 1. Start the development server: ```shell npm run start:dev ``` 2. Confirm the API documentation is available at [`http://localhost:3000/api/`](http://localhost:3000/api/). > **Note**: The original edition of this series linked to a companion GitHub repository. That repository still targets the older Prisma 4 and NestJS 8 stack, so for the updated series you should continue from your own project from part one. ### Project structure and files [#project-structure-and-files] Your project should have the following structure: ``` median ├── node_modules ├── generated │ └── prisma ├── prisma │ ├── migrations │ ├── schema.prisma │ └── seed.ts ├── src │ ├── app.module.ts │ ├── main.ts │ ├── articles │ └── prisma ├── test │ ├── app.e2e-spec.ts │ └── jest-e2e.json ├── README.md ├── .env ├── eslint.config.mjs ├── nest-cli.json ├── package-lock.json ├── package.json ├── prisma.config.ts ├── tsconfig.build.json └── tsconfig.json ``` The notable files and directories in this repository are: * The `src` directory contains the source code for the application. There are three modules: * The `app` module is situated in the root of the `src` directory and is the entry point of the application. It is responsible for starting the web server. * The `prisma` module contains `PrismaService`, which wraps the Prisma Client, your database query builder. * The `articles` module defines the endpoints for the `/articles` route and accompanying business logic. * The `prisma` directory has the following: * The `schema.prisma` file defines the database schema. * The `migrations` directory contains the database migration history. * The `seed.ts` file contains a script to seed your development database with dummy data. * The `generated/prisma` directory contains the generated Prisma Client. In Prisma 7, the Client is generated into your project as source files instead of into `node_modules`. * The `prisma.config.ts` file is Prisma's central configuration file, where the database connection URL and the seed command live. * The `.env` file contains the `DATABASE_URL` connection string for your database. > **Note**: For more information about these components, go through [part one](https://www.prisma.io/blog/nestjs-prisma-rest-api-7D056s1BmOL0) of this tutorial series. ## Perform input validation [#perform-input-validation] To perform input validation, you will be using [NestJS Pipes](https://docs.nestjs.com/pipes). A pipe in NestJS is a class that validates or transforms the arguments of a route handler before the handler runs. Nest invokes a pipe before the route handler, and the pipe receives the arguments destined for the route handler. Pipes are similar to [middleware](https://docs.nestjs.com/middleware), but the scope of pipes is limited to processing input arguments. NestJS provides a few pipes out-of-the-box, but you can also create your own [custom pipes](https://docs.nestjs.com/pipes#custom-pipes). Pipes have two typical use cases: * **Validation**: Evaluate input data and, if valid, pass it through unchanged; otherwise, throw an exception when the data is incorrect. * **Transformation**: Transform input data to the desired form (e.g., from string to integer). A NestJS validation pipe will check the arguments passed to a route. If the arguments are valid, the pipe will pass the arguments to the route handler without any modification. However, if the arguments violate any of the specified validation rules, the pipe will throw an exception with an HTTP `400 Bad Request` status. The following two diagrams show how a validation pipe works, for an arbitrary `/example` route. ![](https://www.prisma.io/blog/nestjs-prisma-validation-7D056s1kOla1/imgs/valid-args.png) ![](https://www.prisma.io/blog/nestjs-prisma-validation-7D056s1kOla1/imgs/invalid-args.png) In this section, you will focus on the validation use case. ### Set up `ValidationPipe` globally [#set-up-validationpipe-globally] To perform input validation, you will be using the built-in NestJS `ValidationPipe`. The `ValidationPipe` provides a convenient approach to enforce validation rules for all incoming client payloads, where the validation rules are declared with decorators from the `class-validator` package. To use this feature, you will need to add two packages to your project: ```shell npm install class-validator class-transformer ``` The `class-validator` package provides decorators for validating input data, and the `class-transformer` package converts incoming JSON into typed instances of your DTO classes so those rules can run. Both packages are required by the `ValidationPipe`. Now import the `ValidationPipe` in your `main.ts` file and use the `app.useGlobalPipes` method to make it available globally in your application: ```ts // src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe()); const config = new DocumentBuilder() .setTitle('Median') .setDescription('The Median API description') .setVersion('0.1') .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); await app.listen(process.env.PORT ?? 3000); } bootstrap(); ``` ### Add validation rules to `CreateArticleDto` [#add-validation-rules-to-createarticledto] You will now use the [`class-validator`](https://github.com/typestack/class-validator) package to add validation decorators to `CreateArticleDto`. You will apply the following rules to `CreateArticleDto`: 1. `title` can't be empty or shorter than 5 characters. 2. `description` has to have a maximum length of 300. 3. `body` and `description` can't be empty. 4. `title`, `description` and `body` must be of type `string` and `published` must be of type `boolean`. Open the `src/articles/dto/create-article.dto.ts` file and replace its contents with the following: ```ts // src/articles/dto/create-article.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsBoolean, IsNotEmpty, IsOptional, IsString, MaxLength, MinLength, } from 'class-validator'; export class CreateArticleDto { @IsString() @IsNotEmpty() @MinLength(5) @ApiProperty() title: string; @IsString() @IsOptional() @IsNotEmpty() @MaxLength(300) @ApiProperty({ required: false }) description?: string; @IsString() @IsNotEmpty() @ApiProperty() body: string; @IsBoolean() @IsOptional() @ApiProperty({ required: false, default: false }) published?: boolean = false; } ``` These rules will be picked up by the `ValidationPipe` and applied automatically to your route handlers. One of the advantages of using decorators for validation is that the `CreateArticleDto` remains the single source of truth for all arguments to the `POST /articles` endpoint. So you don't need to define a separate validation class. Test out the validation rules you have in place. Try creating an article using the `POST /articles` endpoint with a very short placeholder `title` like this: ```json { "title": "Temp", "description": "Learn about input validation", "body": "Input validation is...", "published": false } ``` You should get an HTTP 400 error response along with details in the response body about what validation rule was broken: ```json { "message": ["title must be longer than or equal to 5 characters"], "error": "Bad Request", "statusCode": 400 } ``` This diagram explains what the `ValidationPipe` is doing under the hood for invalid inputs to the `/articles` route: ![Input validation flow with ValidationPipe](https://www.prisma.io/blog/nestjs-prisma-validation-7D056s1kOla1/imgs/invalid-args-specific.png) ### Strip unnecessary properties from client requests [#strip-unnecessary-properties-from-client-requests] The `CreateArticleDto` defines the properties that need to be sent to the `POST /articles` endpoint to create a new article. `UpdateArticleDto` does the same, but for the `PATCH /articles/{id}` endpoint. Currently, for both of these endpoints it is possible to send additional properties that are not defined in the DTO. This can lead to unforeseen bugs or security issues. For example, you could manually pass invalid `createdAt` and `updatedAt` values to the `POST /articles` endpoint. Since TypeScript type information is not available at run-time, your application will not be able to identify that these fields are not available in the DTO. To give an example, try sending the following request to the `POST /articles` endpoint: ```json { "title": "example-title", "description": "example-description", "body": "example-body", "published": true, "createdAt": "2010-06-08T18:20:29.309Z", "updatedAt": "2021-06-02T18:20:29.310Z" } ``` ![](https://www.prisma.io/blog/nestjs-prisma-validation-7D056s1kOla1/imgs/inject-dates.png) In this way, you can inject invalid values. Here you have created an article that has an `updatedAt` value that precedes `createdAt`, which does not make sense. To prevent this, you will need to filter any unnecessary fields/properties from client requests. Fortunately, NestJS provides an out-of-the-box option for this as well. All you need to do is pass the `whitelist: true` option when initializing the `ValidationPipe` inside your application. ```ts // src/main.ts async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe({ whitelist: true })); // ... } bootstrap(); ``` With this option set to true, `ValidationPipe` will automatically remove all *non-whitelisted* properties, where *"non-whitelisted"* means properties without any validation decorators. It's important to note that this option will filter *all properties* without validation decorators, *even if they are defined in the DTO.* Now, any additional fields/properties that are passed to the request will be stripped automatically by NestJS, preventing the previously shown exploit. > **Note**: The NestJS `ValidationPipe` is highly configurable. All configuration options available are documented in the [NestJS docs](https://docs.nestjs.com/techniques/validation#using-the-built-in-validationpipe). If necessary, you can also build [custom validation pipes](https://docs.nestjs.com/pipes#custom-pipes) for your application. ## Transform dynamic URL paths with `ParseIntPipe` [#transform-dynamic-url-paths-with-parseintpipe] Inside your API, you are currently accepting the `id` parameter for the `GET /articles/{id}`, `PATCH /articles/{id}` and `DELETE /articles/{id}` endpoints as a part of the path. NestJS parses the `id` parameter as a string from the URL path. Then, the string is cast to a number inside your application code before being passed to the `ArticlesService`. For example, take a look at the `DELETE /articles/{id}` route handler: ```ts // src/articles/articles.controller.ts @Delete(':id') @ApiOkResponse({ type: ArticleEntity }) remove(@Param('id') id: string) { // id is parsed as a string return this.articlesService.remove(+id); // id is converted to number using the expression '+id' } ``` Since `id` is defined as a string type, the Swagger API also documents this argument as a string in the generated API documentation. This is unintuitive and incorrect. ![](https://www.prisma.io/blog/nestjs-prisma-validation-7D056s1kOla1/imgs/id-string.png) Instead of doing this transformation manually inside the route handler, you can use a NestJS pipe to convert `id` to a number automatically. Add the built-in `ParseIntPipe` to the controller route handlers for these three endpoints: ```ts // src/articles/articles.controller.ts import { Controller, Get, Post, Body, Patch, Param, Delete, ParseIntPipe, } from '@nestjs/common'; export class ArticlesController { // ... @Get(':id') @ApiOkResponse({ type: ArticleEntity }) findOne(@Param('id', ParseIntPipe) id: number) { return this.articlesService.findOne(id); } @Patch(':id') @ApiOkResponse({ type: ArticleEntity }) update( @Param('id', ParseIntPipe) id: number, @Body() updateArticleDto: UpdateArticleDto, ) { return this.articlesService.update(id, updateArticleDto); } @Delete(':id') @ApiOkResponse({ type: ArticleEntity }) remove(@Param('id', ParseIntPipe) id: number) { return this.articlesService.remove(id); } } ``` The `ParseIntPipe` will intercept the `id` parameter of string type and automatically parse it to a number before passing it to the appropriate route handler. This also has the advantage of documenting the `id` parameter correctly as a number inside Swagger. If a client sends a value that can't be parsed to a number, like `GET /articles/abc`, the pipe rejects the request with an HTTP 400 response: ```json { "message": "Validation failed (numeric string is expected)", "error": "Bad Request", "statusCode": 400 } ``` ## Frequently asked questions [#frequently-asked-questions] ### Does whitelist: true reject requests that contain unknown fields? No. With `whitelist: true`, the `ValidationPipe` silently strips unknown properties from the request body and processes the rest. If you want the API to reject such requests instead, add `forbidNonWhitelisted: true` alongside it; the request then fails with an HTTP 400 response like `"property extraField should not exist"`. ### Do I need both class-validator and class-transformer? Yes. The `ValidationPipe` depends on both packages: the validation rules come from `class-validator` decorators on your DTO classes, and `class-transformer` converts the plain JSON request body into instances of those classes so the rules can be evaluated. ### How do I validate URL path parameters like /articles/:id? Use a transformation pipe on the parameter, for example `@Param('id', ParseIntPipe) id: number`. The pipe converts the string from the URL into a number, documents the parameter correctly in Swagger, and rejects non-numeric values with an HTTP 400 response. ## Summary and final remarks [#summary-and-final-remarks] Congratulations! In this tutorial, you took an existing REST API built with NestJS 11 and Prisma ORM 7 and: * Integrated validation using the `ValidationPipe`. * Stripped client requests of unnecessary properties with the `whitelist` option. * Integrated `ParseIntPipe` to parse a `string` path variable and convert it to a `number`. You might have noticed that NestJS heavily relies on decorators. This is a very intentional design choice. NestJS aims to improve code readability and modularity by heavily leveraging decorators for various kinds of cross-cutting concerns. As a result, controllers and service methods do not need to be bloated with boilerplate code for doing things like validation, caching, logging, etc. In the [next part](https://www.prisma.io/blog/nestjs-prisma-error-handling-7D056s1kOop2) of this series, you will learn how to handle errors in a NestJS and Prisma application, including the database errors that validation alone can't catch. If you want to go deeper on the Prisma side first, the [Prisma getting started guide](https://www.prisma.io/docs/getting-started) and [Prisma Migrate docs](https://www.prisma.io/docs/orm/v7/prisma-migrate/getting-started) are good next steps, and Prisma Postgres is an easy way to stand up the database layer for follow-on work. Looking ahead: [Prisma 8](https://www.prisma.io/docs/orm) is a TypeScript-native rewrite of Prisma ORM, built for AI coding agents, and it is now a [release candidate](https://www.prisma.io/docs/orm/release-status). Prisma 7 remains fully supported; its docs live at [prisma.io/docs/orm/v7](https://www.prisma.io/docs/orm/v7). To start with Prisma 8, run `npm create prisma@latest` or read the [Prisma 8 docs](https://www.prisma.io/docs/getting-started).