user9945420
user9945420

Reputation:

ParseObjectIdPipe for MongoDB's ObjectID

I want to create a NestJs API with TypeORM and MongoDB. My entity id fields are of type ObjectID. The controller routes should validate the incoming ids before passing them to the services. I know that Nest ships with the ParseIntPipe and ParseUUIDPipe but as far as I know there is nothing I can use for MongoDBs ObjectID.

So I created my own pipe for those fields as described here https://docs.nestjs.com/pipes#transformation-use-case

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

@Injectable()
export class ParseObjectIdPipe implements PipeTransform<any, ObjectID> {
  transform(value: any): ObjectID {
    const validObjectId: boolean = ObjectID.isValid(value);

    if (validObjectId) {
      throw new BadRequestException('Invalid ObjectId');
    }

    const objectId: ObjectID = ObjectID.createFromHexString(value);
    return objectId;
  }
}

and hope this will do the trick, even for edge cases. I can use it for my route params like

  @Get(':id')
  public getUserById(@Param('id', ParseObjectIdPipe) id: ObjectID): Promise<User> {
    return this.usersService.getUserById(id);
  }

The problem I have is that some routes need a Body validation. I use the class-validator package as described here

https://docs.nestjs.com/techniques/validation

It seems that I have to create my own class-validator decorator for those ObjectID fields but that should be fine. Maybe I'll find something here on how to do it https://github.com/typestack/class-validator#custom-validation-classes. But how can I transform those fields to the type ObjectID? Can I use the custom pipe for that later on too?


Update:

I also tried to transform the value via class-transformer package. So the code for this is

import { ObjectID } from 'typeorm';
import { Type, Transform } from 'class-transformer';
import { BadRequestException } from '@nestjs/common';

export class FooDTO {
  @Type(() => ObjectID)
  @Transform(bar => {
    if (ObjectID.isValid(bar)) {
      throw new BadRequestException('Invalid ObjectId');
    }

    return ObjectID.createFromHexString(bar);
  })
  public bar: ObjectID;
}

Unfortunately the value bar is always undefined. But maybe this code might help for validation and transformation purposes...

Upvotes: 10

Views: 13951

Answers (5)

yur0n
yur0n

Reputation: 31

class-validator provides IsMongoId decorator

import { IsMongoId } from 'class-validator';

export class FooDTO {
  @IsMongoId()
  objectId: string;
}

Upvotes: 0

sagar
sagar

Reputation: 21

Just create on pipe of object id in code by using below code :

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

@Injectable()
export class ParseObjectIdPipe implements PipeTransform<any, ObjectID> {

public transform(value: any): ObjectID {
try {
  const transformedObjectId: ObjectID = ObjectID.createFromHexString(value);
  return transformedObjectId;
} catch (error) {
  throw new BadRequestException('Validation failed (ObjectId is expected)');
}
}
}

and add piple in controller method :

@Post('cat/:id')
async cat(
@Param('id', ParseObjectIdPipe) id: ObjectId,
@Res() res,
@Body() cat: catDTO[],)

Upvotes: 2

Andr&#233;s Cruz
Andr&#233;s Cruz

Reputation: 81

For the class-transformer approach this worked for me

import { IsNotEmpty } from "class-validator";
import { Type, Transform } from 'class-transformer';
import { Types } from "mongoose"

export class CreateCommentDto {

    @IsNotEmpty()
    @Type(() => Types.ObjectId)
    @Transform(toMongoObjectId)
    articleId: Types.ObjectId

with this definition for 'toMongoObjectId':

export function toMongoObjectId({ value, key }): Types.ObjectId {
    if ( Types.ObjectId.isValid(value) && ( Types.ObjectId(value).toString() === value)) {
        return Types.ObjectId(value);
    } else {
        throw new BadRequestException(`${key} is not a valid MongoId`);
    }
}

Upvotes: 8

sh park
sh park

Reputation: 91

I took your code and changed some parts. I tested it, It works fine.

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

@Injectable()
export class ParseObjectIdPipe implements PipeTransform<any, Types.ObjectId> {
    transform(value: any): Types.ObjectId {
        const validObjectId = Types.ObjectId.isValid(value);

        if (!validObjectId) {
            throw new BadRequestException('Invalid ObjectId');
        }

        return Types.ObjectId.createFromHexString(value);
    }
}

Upvotes: 9

Hugo
Hugo

Reputation: 323

You are getting undefined because you are importing from wrong lib. you need to change this:

import { ObjectID } from 'typeorm';

for this:

import { ObjectID } from 'mongodb';

Upvotes: 1

Related Questions