or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dependency-injection.mdindex.mdmodule-configuration.mdschema-definition.mdschema-factories.mdutility-functions.mdvalidation-pipes.md

validation-pipes.mddocs/

0

# Validation Pipes

1

2

Pre-built pipes for validating and parsing MongoDB ObjectIds in request parameters and body data. These pipes integrate with NestJS's validation system to ensure data integrity when working with MongoDB documents.

3

4

## Capabilities

5

6

### IsObjectIdPipe

7

8

Validates if a string is a valid MongoDB ObjectId format without converting it to an ObjectId instance.

9

10

```typescript { .api }

11

class IsObjectIdPipe implements PipeTransform {

12

/**

13

* Validates that a string is a valid MongoDB ObjectId format

14

* @param value - String value to validate

15

* @returns The original string if valid

16

* @throws BadRequestException if invalid ObjectId format

17

*/

18

transform(value: string): string;

19

}

20

```

21

22

**Usage Examples:**

23

24

```typescript

25

import { Controller, Get, Param } from '@nestjs/common';

26

import { IsObjectIdPipe } from '@nestjs/mongoose';

27

28

@Controller('users')

29

export class UsersController {

30

// Validate ObjectId in route parameter

31

@Get(':id')

32

async findOne(@Param('id', IsObjectIdPipe) id: string) {

33

// id is guaranteed to be a valid ObjectId string

34

return this.usersService.findById(id);

35

}

36

37

// Multiple ObjectId parameters

38

@Get(':userId/posts/:postId')

39

async findUserPost(

40

@Param('userId', IsObjectIdPipe) userId: string,

41

@Param('postId', IsObjectIdPipe) postId: string,

42

) {

43

return this.postsService.findByUserAndId(userId, postId);

44

}

45

46

// Optional ObjectId validation

47

@Get()

48

async findAll(@Query('authorId', new IsObjectIdPipe({ optional: true })) authorId?: string) {

49

if (authorId) {

50

return this.usersService.findByAuthor(authorId);

51

}

52

return this.usersService.findAll();

53

}

54

}

55

56

// In request body validation

57

import { Body, Post } from '@nestjs/common';

58

59

@Controller('comments')

60

export class CommentsController {

61

@Post()

62

async create(@Body() createCommentDto: CreateCommentDto) {

63

return this.commentsService.create(createCommentDto);

64

}

65

}

66

67

// DTO with ObjectId validation

68

export class CreateCommentDto {

69

@IsString()

70

content: string;

71

72

@Transform(({ value }) => value, { toClassOnly: true })

73

@Validate(IsObjectIdPipe)

74

postId: string;

75

76

@Transform(({ value }) => value, { toClassOnly: true })

77

@Validate(IsObjectIdPipe)

78

authorId: string;

79

}

80

```

81

82

### ParseObjectIdPipe

83

84

Validates and converts a string to a MongoDB ObjectId instance for direct use with Mongoose operations.

85

86

```typescript { .api }

87

class ParseObjectIdPipe implements PipeTransform {

88

/**

89

* Validates and converts a string to MongoDB ObjectId

90

* @param value - String value to parse

91

* @returns ObjectId instance

92

* @throws BadRequestException if invalid ObjectId format

93

*/

94

transform(value: string): Types.ObjectId;

95

}

96

```

97

98

**Usage Examples:**

99

100

```typescript

101

import { Controller, Get, Param, Delete } from '@nestjs/common';

102

import { ParseObjectIdPipe } from '@nestjs/mongoose';

103

import { Types } from 'mongoose';

104

105

@Controller('users')

106

export class UsersController {

107

// Parse ObjectId in route parameter

108

@Get(':id')

109

async findOne(@Param('id', ParseObjectIdPipe) id: Types.ObjectId) {

110

// id is now a proper ObjectId instance

111

return this.usersService.findById(id);

112

}

113

114

@Delete(':id')

115

async remove(@Param('id', ParseObjectIdPipe) id: Types.ObjectId) {

116

return this.usersService.remove(id);

117

}

118

119

// Complex route with multiple ObjectIds

120

@Get(':userId/orders/:orderId/items/:itemId')

121

async findOrderItem(

122

@Param('userId', ParseObjectIdPipe) userId: Types.ObjectId,

123

@Param('orderId', ParseObjectIdPipe) orderId: Types.ObjectId,

124

@Param('itemId', ParseObjectIdPipe) itemId: Types.ObjectId,

125

) {

126

return this.ordersService.findItemByIds(userId, orderId, itemId);

127

}

128

}

129

130

// Service usage with parsed ObjectIds

131

@Injectable()

132

export class UsersService {

133

constructor(@InjectModel(User.name) private userModel: Model<User>) {}

134

135

async findById(id: Types.ObjectId): Promise<User> {

136

// Can use ObjectId directly in Mongoose operations

137

return this.userModel.findById(id).exec();

138

}

139

140

async findByIds(ids: Types.ObjectId[]): Promise<User[]> {

141

return this.userModel.find({ _id: { $in: ids } }).exec();

142

}

143

144

async updateRelationship(userId: Types.ObjectId, friendId: Types.ObjectId): Promise<User> {

145

return this.userModel.findByIdAndUpdate(

146

userId,

147

{ $addToSet: { friends: friendId } },

148

{ new: true }

149

).exec();

150

}

151

}

152

```

153

154

## Advanced Pipe Usage

155

156

### Custom Validation Options

157

158

```typescript { .api }

159

import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';

160

import { isValidObjectId, Types } from 'mongoose';

161

162

@Injectable()

163

export class CustomObjectIdPipe implements PipeTransform {

164

constructor(private readonly options: { optional?: boolean; allowNull?: boolean } = {}) {}

165

166

transform(value: any, metadata: ArgumentMetadata): string | Types.ObjectId | null {

167

// Handle optional values

168

if (this.options.optional && (value === undefined || value === '')) {

169

return undefined;

170

}

171

172

// Handle null values

173

if (this.options.allowNull && value === null) {

174

return null;

175

}

176

177

// Validate ObjectId format

178

if (!isValidObjectId(value)) {

179

throw new BadRequestException(`Invalid ObjectId format: ${value}`);

180

}

181

182

// Return as string or ObjectId based on needs

183

return metadata.metatype === Types.ObjectId ? new Types.ObjectId(value) : value;

184

}

185

}

186

187

// Usage with custom options

188

@Controller('posts')

189

export class PostsController {

190

@Get()

191

async findAll(

192

@Query('categoryId', new CustomObjectIdPipe({ optional: true })) categoryId?: string,

193

@Query('authorId', new CustomObjectIdPipe({ optional: true })) authorId?: string,

194

) {

195

const filters: any = {};

196

if (categoryId) filters.category = categoryId;

197

if (authorId) filters.author = authorId;

198

199

return this.postsService.findWithFilters(filters);

200

}

201

}

202

```

203

204

### Array ObjectId Validation

205

206

```typescript { .api }

207

@Injectable()

208

export class ParseObjectIdArrayPipe implements PipeTransform {

209

transform(value: string | string[]): Types.ObjectId[] {

210

const ids = Array.isArray(value) ? value : [value];

211

212

return ids.map(id => {

213

if (!isValidObjectId(id)) {

214

throw new BadRequestException(`Invalid ObjectId format: ${id}`);

215

}

216

return new Types.ObjectId(id);

217

});

218

}

219

}

220

221

@Controller('users')

222

export class UsersController {

223

@Post('bulk-update')

224

async bulkUpdate(

225

@Body('userIds', ParseObjectIdArrayPipe) userIds: Types.ObjectId[],

226

@Body() updateData: any,

227

) {

228

return this.usersService.bulkUpdate(userIds, updateData);

229

}

230

231

@Delete('bulk-delete')

232

async bulkDelete(@Body('ids', ParseObjectIdArrayPipe) ids: Types.ObjectId[]) {

233

return this.usersService.bulkDelete(ids);

234

}

235

}

236

```

237

238

### Global Pipe Configuration

239

240

```typescript

241

import { ValidationPipe } from '@nestjs/common';

242

import { NestFactory } from '@nestjs/core';

243

import { AppModule } from './app.module';

244

245

async function bootstrap() {

246

const app = await NestFactory.create(AppModule);

247

248

// Global validation pipe with ObjectId support

249

app.useGlobalPipes(

250

new ValidationPipe({

251

transform: true,

252

whitelist: true,

253

forbidNonWhitelisted: true,

254

}),

255

);

256

257

await app.listen(3000);

258

}

259

bootstrap();

260

261

// Custom global ObjectId validation

262

@Injectable()

263

export class GlobalObjectIdValidationPipe implements PipeTransform {

264

transform(value: any, metadata: ArgumentMetadata) {

265

// Auto-detect ObjectId strings in parameters

266

if (metadata.type === 'param' && typeof value === 'string' && isValidObjectId(value)) {

267

return new Types.ObjectId(value);

268

}

269

270

return value;

271

}

272

}

273

274

// Register globally

275

app.useGlobalPipes(new GlobalObjectIdValidationPipe());

276

```

277

278

### Integration with Class Validators

279

280

```typescript { .api }

281

import { IsString, IsOptional, ValidateNested, Type, registerDecorator, ValidationOptions } from 'class-validator';

282

import { Transform } from 'class-transformer';

283

import { isValidObjectId, Types } from 'mongoose';

284

285

// Custom ObjectId validator

286

export function IsObjectId(validationOptions?: ValidationOptions) {

287

return function (object: Object, propertyName: string) {

288

registerDecorator({

289

name: 'isObjectId',

290

target: object.constructor,

291

propertyName: propertyName,

292

options: validationOptions,

293

validator: {

294

validate(value: any) {

295

return typeof value === 'string' && isValidObjectId(value);

296

},

297

defaultMessage() {

298

return '$property must be a valid ObjectId';

299

},

300

},

301

});

302

};

303

}

304

305

// DTO with ObjectId validation

306

export class CreatePostDto {

307

@IsString()

308

title: string;

309

310

@IsString()

311

content: string;

312

313

@IsObjectId()

314

@Transform(({ value }) => value.toString())

315

authorId: string;

316

317

@IsOptional()

318

@IsObjectId()

319

@Transform(({ value }) => value ? value.toString() : undefined)

320

categoryId?: string;

321

322

@IsOptional()

323

@IsObjectId({ each: true })

324

@Transform(({ value }) => Array.isArray(value) ? value.map(id => id.toString()) : [])

325

tagIds?: string[];

326

}

327

328

@Controller('posts')

329

export class PostsController {

330

@Post()

331

async create(@Body() createPostDto: CreatePostDto) {

332

// All ObjectIds are validated by class-validator

333

return this.postsService.create(createPostDto);

334

}

335

}

336

```

337

338

## Error Handling

339

340

The validation pipes throw standard NestJS `BadRequestException` when validation fails:

341

342

```typescript { .api }

343

// Example error responses

344

{

345

"statusCode": 400,

346

"message": "Validation failed (ObjectId is expected)",

347

"error": "Bad Request"

348

}

349

350

// Custom error handling

351

@Injectable()

352

export class CustomObjectIdPipe implements PipeTransform {

353

transform(value: string): Types.ObjectId {

354

if (!isValidObjectId(value)) {

355

throw new BadRequestException({

356

statusCode: 400,

357

message: `Invalid ObjectId format: "${value}". ObjectId must be a 24 character hex string.`,

358

error: 'Invalid ObjectId',

359

provided: value,

360

});

361

}

362

363

return new Types.ObjectId(value);

364

}

365

}

366

```