-
-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kafka Microservice Does not validate DTO #5974
Comments
Please provide a minimum reproduction |
@kodeine I had the same issue also for RabbitMQ, what happens is that you need ValidationPipe, since Kafka has a custom object, you need to pass on the payload your Pipe to validate, i ended up creating my own but class-validator will do just fine |
@underfisk already said that we need to have a ValidationPipe for Micrsoservices (be it kafka or RabbitMQ). Check this out: import { Controller, Logger, UseFilters, UsePipes, ValidationPipe } from '@nestjs/common';
@Controller('event-broker')
export class EventBrokerController {
@UseFilters(new ExceptionFilter())
@UsePipes(new ValidationPipe())
@EventPattern('jsontest', Transport.KAFKA)
async processStream(
@Payload() message: CommunicationEventDto,
@Ctx() context: KafkaContext,
): Promise<void> {
const { value, headers, topic, key } = message;
// here your logic
});
} my validation object import { IsNumber, IsObject, IsString } from 'class-validator';
interface IncomingMessage {
topic: string;
partition: number;
timestamp: string;
magicByte: number;
attributes: number;
offset: string;
key: any;
value: any;
headers: Record<string, any>;
}
export interface CommunicationEventHeader {
mvno_id?: number;
endpoint?: string;
msisdn?: string;
email?: string;
priority?: 'HIGH' | 'MID' | 'LOW';
allowedChannels?: any;
}
export class CommunicationEventDto implements IncomingMessage {
@IsString()
readonly topic: string;
@IsNumber()
readonly partition: number;
@IsString()
readonly timestamp: string;
@IsNumber()
readonly magicByte: number;
@IsNumber()
readonly attributes: number;
@IsString()
readonly offset: string;
@IsString()
readonly key: string;
@IsObject()
readonly value: { payload: any };
@IsObject()
readonly headers: CommunicationEventHeader | Record<string, any>;
} |
@enesyalcin Its weird because its probably happening on Kafka, on rabbit mq it does work fine |
I just extended regular validation pipe and passed payload.value to it for the validation to work. So a decorator for kafka validation |
@kodeine it seems to be a good alternative having a custom decorator but i think this might be an internal issue and Nestjs should do this out of the box, if it does for most of the transports, Kafka is no different |
My project is not a hybrid but a Kafka Project (so no REST API is being provided on this application). Maybe that changes anything?
I remember that there was some kind of reason for that but I forgot. Does your solution work with the provided example? |
@enesyalcin i forgot which method i was using... you can try doing this first,
in controller use like if that doesnt work, following is the pipe that you can use. Problem is kafka payload.value has the dto needed so validator doesnt work as we need to define @type for validator. so if you modify the pipe to use payload.value you will achieve the same.
and in your
|
any solution from nest.js for this issue? |
@tanyudii Nestjs uses Payload for convention because its also possible to pass context, its not going to do anything extra. By default if you need a validation you have to perform it by yourself, the solution presented by @kodeine will work just fine. |
Not sure if I get this wrong or the documentation is wrong but it says:
I now assumed that it works the |
@MickL I'm not sure if its a bug but probably the way that microservices are, they rely upon the normal controller flow but if you do enable global validation pipe it should validate but you have to pass the dto
This will not invoke the validation pipe, its going to be ignored since you omitted but if you pass the type it should validate (but its not related with kafka or nats, its related with the core itself because they are relying on the natural procedures. If you check the source code, you'll see that the reason microservices use controllers like HTTP its because they can inherit pipes,guards, interceptors etc out of the box without having to create a special adjustment for them in the microservices context As a workaround @kodeine solution by creating a custom pipe just to invoke manually class validator is a solution but i'm looking forward to hearing from @jmcdo29 in order to consider whether its a bug or intended to be this way |
@jmcdo29 if this is intended than the documentation is misleading and it might be noted that validation is not working on microservice controllers the same way. |
I mean, does anyone want to provide a minimum reproduction, as I asked for Dec 17, 2020? I've only done basic work with microservices, and haven't looked too deeply into how the |
this is my minimum reproduction, i want to validate create request organization entity from api gateway |
@tanyudii thank you. For reproduction steps, just need a bit more info. docker-compose up -d
# start the two servers
# send request to what?
# ???
# Profit |
open http://localhost:3000/docs/ (the service api-gateway) and submit the organization post to create organization. I'm expect validation run in account-service, because i don't wanna manage validation rule in api gateway. Sorry for my english😂 |
Okay, @kamilmysliwiec I finally had time to confirm this, and can say that unless a custom pipe is created for Kafka, the I'm not sure where this is happening, if it comes from |
In v8 (#6349), it will be possible to pass a property name to the @Payload('value') dto: DtoClass |
I wonder why it needs a name to work. But anyway, will it work like this then? const app = await NestFactory.createMicroservice<MicroserviceOptions>(...);
app.useGlobalPipes(new ValidationPipe()); And also with hybrid apps and |
So long as (on V8) you use the @kamilmysliwiec should we maybe provide a specific |
@MickL This needs a name to work because this is how pipes work in general (they run for a specific argument which is the whole Kafka incoming message, so in the case described in the main post, it just can't work properly).
In Kafka, we pass the entire incoming message object because it may contain very important, additional details & information about the message/event (and this isn't the case for most of the other existing, built-in transport strategies, that's why it's different for this one), so imo |
i'm update to V8 package:
and i getting problem when i register @nestjs/config module in app.module.ts
|
@tanyudii can you please try again with 8.0.0-alpha.2? |
for all package? |
All packages from this repository (core, common, platform-X, microservices, testing, and websockets). |
It's working, thank you. |
Maybe something like |
|
I think the documentation is misleading because it says:
So I assumed setting I tried with websocket gateway but they are not auto-validated, I need to add |
Can you explain how you got your example in the documentation working? https://docs.nestjs.com/microservices/kafka#outgoing @Controller()
export class HeroesController {
@MessagePattern('hero.kill.dragon')
killDragon(@Payload() message: KillDragonMessage): any {
const dragonId = message.dragonId;
const items = [
{ id: 1, name: 'Mythical Sword' },
{ id: 2, name: 'Key to Dungeon' },
];
return items;
}
} Because using this exact code doesn't work - dragonId is not a member of message. From what you are explaining in the previous comments, it seems like it was never intended to work like explained in the documentation:
I'm confused. |
@ericmorand payload for kafka is different, docs don’t portray this correctly. You need to use message.payload.dragonid something like that I’m on my phone so cant confirm the syntax exactly but do a console log and im sure ull figure it out. |
I got this interface from someone on another thread on here and am using that for the
So for the example would be:
And to access:
Where value will be correctly typed as KillDragonMessage |
Any news on this? |
I am facing the same thing. I thought global validation would be applied, but i had to manually apply the @UsePipes on each @EventPattern & @MessagePattern i got inside my controller. I am not really sure weither if it is a feature or a bug. Anyone got an answer ? --- Update: setting @UsePipes on the controller level did do the job too. |
Bug Report
Kafka Microservice does not validate dto's. kafka payload response is incorrect according to docs.
Current behavior
it does not output
KillDragonMessage
interface but interfaceIncomingMessage
AccountController
code above gives an error,[ERROR] 15:45:00 TypeError: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object
value of payload comes out to
IncomingMessage
which isThe text was updated successfully, but these errors were encountered: